In my spare time over the past few months I’ve been writing an app for my Palm Pre, and have finally got to point where I’m happy enough to release it. Palm Pre and Pixi apps run on Palm’s webOS operating system in HTML and Javascript. This is a summary of how I got on…
- Background
- Javascript++
- Development environment
- Execution sequence
- Callbacks and testing
- Events
- Releasing it
Background: A basic usability problem
I’ve always regarded Palm as the masters of productivity. David Allen based his Getting Things Done system on his use of a Palm Pilot. I stuck to my old Palm Vx as long as I could, as I lived almost entirely out of its task list application. I bought an iPod Touch when I had to ditch my old Palm, but while Apple makes beautiful products it doesn’t seem to care about productivity apps. (And that’s understandable — if you can look cool in Starbucks with a black polo neck sweater and a slim silver laptop, why should you care about productivity? Me, I don’t look cool in polo neck sweaters.) The iPod Touch and iPhone’s calendar app, for instance, only allowed a limited choice of event recurrence options; if you had a meeting which occurred on the 3rd Thursday of every month then that was just tough. There was no task list app as standard.
While I had migrated my task list to Remember The Milk (RTM) on my desktop, the Touch’s RTM app was for me a poor user experience. For one thing you had to navigate through several screens to get to your task list. Then you couldn’t see today’s and tomorrow’s tasks in one list — they were on separate screens you had to navigate back and forth between. And when you tapped to create a new task you couldn’t start typing “Get bread”, you had to first tap on the description field to select it — that’s just an annoying unnecessary extra user action. One lesson I learnt — from GUI Bloopers, I think — is understand what the user will primarily want to do and then absolutely minimise the work needed to do it. In a task list application I’d have thought having your tasks on the first screen might be obvious. Apparently not.
So I held out for the long-awaited Palm Pre to be my first smartphone. (And in the UK that was a very long awaiting.) Take a look at the Pre’s calendar app in this inevitably unnatural video run-through. Notice for one how the free time concertinas up so that you don’t miss knowing about appointments just because there’s a big gap before them. That’s an application made by people who really understand what their users need — or at least understand what I need.
Unfortunately, while the Pre does come with a very beautiful task list app it really is very basic. It doesn’t capture the ease of use of the Palm Pilot’s original (once again you have to drill down to actually see your tasks). And of course it was foolish to have hoped it would sync with Remember The Milk.
So, anyway, I wrote a task list app.
Palm’s webOS is based on Linux, and the basic programming environment is Javascript and HTML running in Webkit. Now I know what you’re thinking: isn’t that just what Steve Jobs announced when the iPhone first appeared, before the iPhone SDK existed, and wasn’t it unconvincing then? This is what he said:
We have been trying to come up with a solution to expand the capabilities of the iPhone so developers can write great apps for it, but keep the iPhone secure. And we’ve come up with a very. Sweet. Solution. Let me tell you about it. An innovative new way to create applications for mobile devices… it’s all based on the fact that we have the full Safari engine in the iPhone. […] don’t worry about distribution, just put ’em on an internet server. They’re easy to update, just update it on your server. They’re secure, and they run securely sandboxed on the iPhone. And guess what, there’s no SDK you need! You’ve got everything you need if you can write modern web apps…
That section of transcript came from Engadget’s live coverage, and they immediately added the following comment of their own:
Weeeeeaaaak
Well, don’t confuse that with what Palm has done, because there are three key differences. First, they’ve provided their own APIs which allow access to useful functions, including some system functions such as background alarms, network status, location, and so on. Second, you can package up your app into a single bundle and deploy it onto a device. Third, when an app runs it does so within a well-defined framework. So you can create scenes with their distinct logic and pass data between them, and the framework (called Mojo) handles the application lifecycle.
So Javascript is still the language, and HTML and CSS is still the rendering mechanism, but that’s just a language detail. You really are writing real apps on the actual device platform. It’s Javascript, Jim, but not as you probably know it.
As an example, here’s how my task list app exits from the scene in which someone has added or edited a task:
EditTaskAssistant.prototype.popScene = function(wasCancelled) { Mojo.Log.info("EditTaskAssistant.popScene: Entering"); if (this.config.task.name == '') { // Don't allow a blank name to be entered this.config.task.name = this.savedTaskProperties.name; } this.config.task.update(); Mojo.Controller.stageController.popScene({ lastScene: 'EditTask', task: this.config.task, isNew: this.config.isNew, wasCancelled: wasCancelled }); }
The Mojo.* Javascript classes are built in, and I’m using them to do a bit of logging and also to exit from this scene. The framework itself knows what the last scene was to return to, and the only thing I’ve added is some parameters to pass back.
The webOS development environment and workflow is pretty good. It consists of
- Eclipse;
- The Aptana Javascript plugin;
- Sun’s VirtualBox; and
- The webOS SDK itself.
The SDK consists of
- Palm Pre and Palm Pixi images for VirtualBox (thus creating an emulator);
- Another Eclipse plugin which adds some webOS specific actions (deploy to emulator, create a new Mojo scene, etc);
- Some command line tools to perform actions such as tailing logs, creating an app package, deploying an app, etc;
- Graphical tools for resourcing monitoring and DOM inspection; and
- Sample apps to study.
What appeals to me about all this is Palm’s relaxed attitude to technology ownership. Write an IDE? Why go to the trouble when Eclipse already exists. Write an emulator? Run it in VirtualBox. And so on. Plus, the choice of Javascript as a development language ensures a very low bar to entry.
This relaxed (Palm would say “open”) attitude extends to deployment, too. Once I’ve packaged my Javascript app I can send it to the emulator or I can hook up my Palm Pre to my laptop with the USB cable and send the app down the line. No licencing, no cryptography, no UUIDs, just drop it on the device. That also means I can e-mail you the app package and you can drop it on your webOS device, too. As a result there’s been a lot of healthy activity around so-called “homebrew” apps — apps which aren’t available in the Palm App Catalog but can still be easily downloaded and installed.
I said the development environment was “pretty good”, but I have reservations. For one thing, the debugging and troubleshooting tools are a bit basic. But my more significant reservations are around use of Javascript as a development language. After coming from the nice, safe, statically-typed world of Java — having a dynamically-typed language with no safety nets of compile-time type-checking or intelligent auto-completion means I have to work harder. More of my thinking goes into typing rather than programming.
Also, Javascript is a language and platform that has its own quirks that stalled this non-expert developer, particularly when dealing with a large application…
One thing I took a while to grasp was how Javascript handles its execution sequence. On the one hand we are told Javascript is single-threaded. On the other hand, it’s easy to have events fire out of order using setTimeout or in response to some user action. Eventually I worked out that Javascript will execute a sequence of code without interruption until it reaches completion, and then it will select another sequence which has queued up in the interim (from setTimeout, or a user action, or whatever). Overall this is really simplifies things, and it meant I could stop worrying about concurrency problems. (It also explained why I couldn’t write any code to successfully demonstrate a concurrency problem.)
However Palm realised that this is potentially bad, too, because a long-running routine can lock up the application — it will become totally unresponsive while the routine runs. So in order to preserve the user experience the webOS platform kills any routine that continues for more than 10 seconds without interruption. Unfortunately when this happens webOS doesn’t tell you (e.g. by logging a message) so you’re left to guess why your software has suddenly stopped. And the documentation of this “feature” is not terribly obvious, either. I spent quite a bit of time wondering what was going on when it happened to me. But eventually I discovered the cause, found myself in a small community of similarly-frustrated developers, and posted some sample code which showed the problem in action and also demonstrated that at least webOS doesn’t kill your whole application, just the long-running routine.
So I had to adapt my code to run in smaller, disjoint chunks. Fortunately by that stage I had become rather adept at dealing with…
Most of the interesting Javascript extensions use callbacks, in particular XMLHttpRequest and the HTML5 SQL database API, which both return their results on callbacks. Initially I structured my code around XMLHttpRequest (more precisely, Prototype’s Ajax.Request wrapper) and I avoided asynchronous storage by putting all my data in cookies, which are accessed synchronously.
But eventually I felt I had to switch from cookies to SQL and convert my synchronous storage code to asynchronous code. Not only was that conversion quite painful, but trying to find a way of writing tests around so many, er, sequential asynchronous actions sent me down a lot of dead ends. I was using Yahoo!’s YUI test framework, which is quite elegant, but I had found the limit of its usefulness here.
In the end I devised a mechanism to break up my test cases’ code into asynchronous segments with pauses to aid sequencing. So a test would create a database table if it wasn’t already there, pause, clear down the table, pause, write some data into it, pause, and so on. Essentially I was pumping many of my tests full of race conditions (although it was fine in practice… generally) . What didn’t help was that to run the tests I now needed a browser which supported the HTML5 SQL database API, and on my Windows box that meant only Safari. Which, it turns out, is spectacularly disappointing: slow, resource-hungry, and oddly buggy. Test execution took three times as long, and it was only in a small part due to the little pauses I put in between database calls.
But at least I settled on an approach. One area I never resolved satisfactorily was how best to handle events. Because of the way Javascript executes, or because of what my application does, there were a lot of “things that had to respond to other things”. I didn’t settle on a single way to manage those, and actually found myself using four techniques:
- Writing event listeners. Various functions register with an object to show their interest in an event. The object adds each function to a list. When the event occurs the object calls the functions in turn. Here, the event happens and the functions are called synchronously.
- A dummy function designed to be overwritten. This is a simplification of the first, where the object knows that it will have only one listener. When the event happens the object calls the dummy function. The interested code simply needs to replace the dummy function with its own implementation.
- DOM events. The interested code listens for a custom event in the DOM. When the interesting thing happens the custom event is fired in the DOM for the interested code to pick up. Useful if the firing code and the listening code aren’t otherwise well connected.
- Weird specialist class. Sometimes I found some actions depended on other things having happened first. For example, pushing out task changes depended on having first fetched a “timeline” ID from the Remember The Milk server, which in turn depended having an authentication token, which in turn depended on having an internet connection. To handle this I encapsulated the logic in a single class. It had a method called fire() which just went off and did the next thing it could.
Now having four ways to handle events isn’t necessarily wrong: arguably there are different kinds of events, and each kind may well benefit from its own design pattern. But I don’t have a handy classification of events, much less a mapping from them to appropriate design patterns. So perhaps here more than anywhere else in my code I felt I was guessing.
Undoubtedly my experience of writing a webOS application was a positive one: the very fact that I finished the project proves that. It is the most complete and release-quality piece of software I’ve developed in my own time. And now my smartphone has its missing app: Cloud Tasks. I can continue to live by my task list even when I’ve left my desk.
With some trepidation I’ve made the application available for others to download and use, too, in case they find it useful. The trepidation exists because it’s taken a substantial chunk of my time to develop and I really want to leave it now, and get on with the rest of my life. But I know there are plenty of people who use Remember The Milk in different ways, making use of features I’ve not implemented in the webOS client, and don’t want to implement, because I don’t use them myself. It’s not very community-minded to release something with minimal support. Instead I’ve chosen to release it under the GPL, so that others can develop it and any developments remain similarly freely available.
We’ll see what happens.
first, thanks for the app, yours is one of 3 RTM (one has died, though) apps for the palmpre. i really like it, as well as the fact that it is gpl, i like it’s simple interface and the fact that you can see a bunch of tasks on the screen without having to look in multiple places or scroll the screen even.
there are several enhancements that i would like to make because i use priorities, categories, and notes, and like to sort in different ways at different times. so, i will gladly offer my time to make the coding changes as soon as i have some time freed up from my other development efforts.
but thanks for the great beginning!