In the past I've build some Web-based applications--iClipper and iClock, among others--that were meant to hack around the problem of storing applications on your (non-jailbroken) iPhone or iPod Touch. I did this through a completely unusable combination of bookmark-editing and base64-encoding that no sane user would ever actually endure. Recent releases of Mobile Safari have made creating, deploying, and updating offline-capable Web apps for the iPhone much easier.
Backchannel is a re-implementation of @jerrymichalski's Red:Green Cards. It's intended for use during seminars and other meetings where the audience needs to communicate a limited set of one-way messages to the speaker. While running Backchannel, your phone's screen will turn red while vertical and green while horizontal. This could be used for feedback (faster/slower, go/stop) or voting (yes/no). There's no technical reason why the colors couldn't be different, or programmable, or a third color added to the plus-ninety-degrees orientation.
While I've seen several red:green versions, they suffer from a) having to be downloaded from the App Store or b) having to be downloaded live from a Web site, which may or may not be responding from your chair at SxSW.
Enter the offline version. To try it out, fire up your iPhone and go here:
http://kentbrewster.com/backchannel/bc.html
Once you're done flipping your device up and down to see the color change, try saving a local copy. To do this, touch the + button at the bottom of your browser and choose Add to Home Screen. The Add to Home panel should come up, with Backchannel's red-and-green icon and title. Touch Add and you should see it on your home screen.
To see if local storage worked, go to Settings, put your device into Airplane Mode, turn off your Wi-Fi connection, exit Settings, and try Backchannel again. If all is good, Backchannel should behave exactly the same as it did before, with the important difference that all browser chrome should be completely gone, leaving only the very top ten-pixel-thick connection/battery status bar.
Important Note from the Voice of Experience: if you forget to turn your network back on when you're done testing, you will be unable to tell that you have not broken your iPhone. If you take it back to the Genius Bar and ask for help, they will point at you and laugh.
Besides the one that tells how everything works (which you are reading now) there are five files in this directory:
bc.html
-- the HTML structurepresentation.css
-- the CSSbehavior.js
-- the JavaScripticon.png
-- the icon used to represent the application on the iPhone desktopmain.manifest
-- the list of files to be stored locallyThis is basic HTML with some custom links, attributes, and metas, as discussed below.
<!DOCTYPE html> <html manifest="main.manifest"> <head> <title>Backchannel</title> <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; minimum-scale=1.0; user-scalable=0;" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta names="apple-mobile-web-app-status-bar-style" content="black" /> <link rel="apple-touch-icon" href="icon.png" /> <link rel="stylesheet" type="text/css" href="presentation.css" media="screen" /> </head> <body> <script src="behavior.js"></script> </body> </html>
Those custom items:
manifest
-- points at the list of files that we want to save locally.viewport
-- sets the initial width of the document to whatever the width of the device is, and tells it not to scale the size, ever. This plus preventing default on touchmove
(in the behavior, below) causes the app to be rock-steady.apple-mobile-web-app-capable
-- lets the device know that this page can be saved as a mobile web app.apple-mobile-web-app-status-bar-style
-- hides the status barapple-touch-icon
-- points to the icon we want to use on the home screenYep, that's the HTML5 doctype up there. Please promise me right here and now that you will never build anything for an iPhone that's not HTML5.
Here we tell Safari what color to make each orientation of the screen:
.landscape {background-color:green;} .portrait {background-color:red;}
All we're going to do to make this happen is to change the main body's CSS class when the device rotates.
These colors are, of course, trivially easy to change for color-blind speakers.
Here we listen for the onorientationchange
event and change the screen's CSS class name accordingly.
(function (w, d, k) { var $; $ = w[k] = {}; $.w = w; $.d = d; $.f = (function () { return { init : function () { $.s = $.d.getElementsByTagName('BODY')[0]; $.s.addEventListener("touchmove", function (e) { e.preventDefault(); }, false); $.w.setTimeout($.f.orient, 0); $.w.onorientationchange = function () { $.f.orient(); }; }, orient : function () { if ($.w.orientation === 0) { $.s.className = 'portrait'; } else { $.s.className = 'landscape'; } $.w.setTimeout($.f.home, 0); }, home : function () { $.w.scrollTo(0, 1); } }; }()); $.f.init(); }(window, document, 'backchannel'));
We're listening for touchmove
in order to prevent the screen from bobbling about when the user touches and drags.
Here's our happy little icon, icon.png
:
We could have also used a .jpg
here, but it's significantly bigger and uglier.
Here we tell the app what files we want it to save locally:
CACHE MANIFEST # v20091210 presentation.css behavior.js icon.png
Our manifest starts with CACHE MANIFEST
. This is required.
Next up is a comment. If your device is online when the user starts your app, it will ping the remote version of the manifest before loading, to make sure nothing needs to be updated. If I make a change to behavior.js
, I'll also change something in my manifest--that version number, most likely--to let the client know it needs to pull fresh copies of the cached files.
Finally, list all files. We could be using relative path names here--caution: these will be relative to the manifest's path, not the path of the document referencing the manifest--but we're keeping things simple.
Our manifest does not include bc.html
, since that's the file that points to main.manifest
.
Unless you've been reading ahead, chances are excellent that your Web server won't send main.manifest
with the proper content type. If your server sends your manifest as text/plain
, it is guaranteed to silently fail. What you want is text/cache-manifest
. To make it work, I added the following directive to my Apache .htaccess
file:
AddType text/cache-manifest .manifest
To make sure it was working, I used Web-Sniffer to make sure my Content-Type was being set correctly:
http://web-sniffer.net/?url=http%3A%2F%2fkentbrewster.com%2Fbackchannel%2Fmain.manifest
... and it was. Huge thanks to @thecssninja for this tip, and much useful material about creating offline apps; please see How To Create Offline Web Apps On The iPhone for more. (The Ninja also has tips for those of you stuck with IIS and other inferior Web servers.)
As always, have fun with this, please let me know how it goes, and feel free to repurpose the code as long as you're within the limits of my Right and Permissions.
You can do the orientation changes with CSS3 media queries e.g.
@media screen and (orientation:landscape) {
body { background: green; }
}
And to disable the user selecting the body so the copy action doesn't happen you could do this:
* {
-webkit-user-select: none;
-webkit-touch-callout: none;
-webkit-user-modify: none;
}
And since Android also support orientation media queries and it's webkit based it should work no problem on it either. Most likely blackberries new webkit browser will also work.
I have even made a signature capture area so we can collect signatures and send them back as a jpg to the document.
This is cool stuff!
yup...gimme that cookie.