back

Build a personal productivity application
with Adobe AIR

by Brian Rinaldi

In our daily lives, we all must perform many trivial yet time-consuming tasks. Many people have developed applications to assist with these tasks, such as the many task list applications built with Adobe AIR. Oftentimes, however, these tasks are peculiar to an individual or small group of people. In these cases, the more generic applications typically offered for widespread consumption can be insufficient or inapplicable.

Much has been written about how Adobe AIR enables you to use the same tools you use to create web applications (such as Adobe Flex Builder and Adobe Creative Suite software) to rapidly build desktop applications. Most of these articles focus on distributed applications that are made available to the general public, for sale or for free, or within a corporation. However, the very fact that creating desktop applications in AIR is both quick and easy makes it the perfect tool for building a customized personal productivity application.

The goal of this article is to show you not only what a personal productivity application is but how it differs from a standard application. We examine why Adobe AIR is the ideal tool for developing this type of application and take a look at an example of a personal productivity application that involves working with the Google Notebook API.

What is a personal productivity application?

To clarify the differences between a standard application and a personal productivity application, let's look at a more concrete example.

Each week, I write a post on my blog that aggregates nearly all the blog posts related to open-source applications built in Adobe ColdFusion. I collect these during the week and place them into Google Notebook using the Firefox plug-in. Then every Monday, I sort them into categories, summarize them, and insert them into a blog post. The process of sorting, summarizing, and assembling the links into a post takes several hours of tedious copying and pasting when done manually. The repetitive nature of the task made it a perfect candidate for automation using some sort of software tool.

To solve this problem, I built an application in Adobe AIR using Adobe Flex that pulls the data from Google Notebook and makes it easier to categorize the posts and assemble the HTML for the blog post. I estimate that the application took no more than three hours to build initially, with minimal ongoing updates (much of that initial development time went into figuring out how the Google Notebook API worked because the documentation was limited). However, after I completed it, this application helped decrease the amount of time I dedicate to this task each week from approximately three hours to 45 minutes. While it's still a significant task, a savings of more than two hours' time each week is not trivial. Yet, while certain aspects of this application may be useful to someone else, as a whole it is designed to serve my own personal purpose.

How do application requirements differ?

The key to building a personal productivity application is to spend as little time building and maintaining it as possible. The goal, after all, is to decrease the time a task takes rather than to divert the time to a different task. With that in mind, developing this kind of application can be simple because it doesn't necessarily have the same rigorous requirements that public applications demand. The main differences include:

The anatomy of the application

The code for the sample application is available via a Subversion repository on Google Code at http://code.google.com/p/remotesynthesis. This is a simple example of working with the Google Notebook API, and it could easily be modified to generate your own blog posting, if you have a similar need.

The majority of the application code is contained within the main MXML file (osupdater.mxml) with a couple of additional views and several value object files. Normally I wouldn't advocate this code structure. I generally use a framework such as Mate or Cairngorm, but for a small, lightly maintained application, following a framework would likely cause more development overhead than is necessary.

The core actions of this application are to load the notebooks from Google Notebook, load the posts from the selected notebook, and spit out the blog post in HTML.

A typical distributed application like this would usually require some logic to handle security around the user ID, so that users can't pull data from other users' notebooks. However, a nondistributed application can use a much simpler solution. To access a list of your public Google Notebooks, you can access a URL such as www.google.com/notebook/feeds/15420494164259897982, where 15420494164259897982 is your user ID. You can determine your user ID by going to any of your public notebook sharing options and parsing it out of the shared URL. I actually parse it within the application by asking users to input a public notebook URL the first time they use the application. The ID then is saved to a text file and never requested again.

Within a typical AIR application that requires a URL request, you would test the connection to the Internet, but again, that kind of usability concern can easily be overlooked on an application you are building for yourself. You access Google Notebook data in Flex or Adobe AIR using a standard HTML GET URLRequest, and the data returned is XML. The only thing I have added is a simple try...catch should the request fail. Let's look at the code to load and parse the notebook list, which is contained in three functions:

public function loadNotebookUrl():void {
    notebookListUrl = "http://www.google.com/notebook/feeds/" + userId;
    urlRequest = new URLRequest(notebookListUrl);
    urlRequest.method = "GET";
    loader = new URLLoader();
    loader.addEventListener(Event.COMPLETE, notebookListLoadedHandler);
    try {
        loader.load(urlRequest);
    } catch (error:Error) {
        trace("Unable to load Google Notebook list.");
    }
}

private function notebookListLoadedHandler(event:Event):void {
    var docsListXml:XMLDocument = new XMLDocument();
    docsListXml.parseXML(loader.data);
    loadNotebooks(docsListXml.firstChild);
}

public function loadNotebooks(parent:XMLNode):void {
    notebooks.removeAll();
    var node:XMLNode = parent.firstChild;
    do {
        if (node.localName == "entry") {
            var notebook:Notebook = new Notebook();
            notebook.title = node.childNodes[3].firstChild.nodeValue;
            notebook.url = node.childNodes[5].attributes.src;
            notebooks.addItem(notebook);
        }
        node = node.nextSibling;
    } while (node.nextSibling != null);
    CursorManager.removeBusyCursor();
    loadNotebook();
}

Once you have loaded the notebooks, you can load the contents of the selected notebook, which by default is the most recent notebook. The application opens a simple edit form to allow you to add a short summary and project name and choose from one of the predetermined categories.

Here, again, standard usability concerns go out the window because you don't save changes to notebook items (they persist only while the application is open) and the categories are not editable. These would be significant drawbacks if this were a publicly distributed application, and they would be nice features even for personal use, but the time it would take to build and maintain these features would detract from the very productivity the application is designed to enhance.

One interesting note for anyone wanting to work with the Google Notebook API is that by default it appears that the API sends only the top 10 items within the given notebook. It was not well documented that you could append ?start-index= and supply a starting point from which it will get the next 10. Since the API does provide a count of the total items within a notebook, I use this URL parameter to increment through the items in a notebook and retrieve all of them, parsing the XML as I go along. Below are the four functions related to parsing notebook items:

public function loadNotebook():void {
    CursorManager.setBusyCursor();
    notebook = allNotebooks.selectedItem as Notebook;
    // we can only load 10 at a time.
    startIndex = 1;
    entries.removeAll();
    callNotebookURL()
}

private function callNotebookURL():void {
    var url:String = notebook.url + "?start-index=" + startIndex;
    urlRequest = new URLRequest(url);
    urlRequest.method = "GET";
    loader = new URLLoader();
    loader.addEventListener(Event.COMPLETE, notebookLoadedHandler);
    try {
        loader.load(urlRequest);
    } catch (error:Error) {
        trace("Unable to load selected Google Notebook.");
    }
}

public function notebookLoadedHandler(event:Event):void {
    var docsListXml:XMLDocument = new XMLDocument();
    docsListXml.parseXML(loader.data);
    loadEntries(docsListXml.firstChild);
}

public function loadEntries(parent:XMLNode):void {
    // we don't remove all here since they are appended 10 at a time
    var node:XMLNode = parent.firstChild;
    var totalResults:Number = 10;
    do {
        if (node.localName == "entry") {
            var entry:Entry = new Entry();
            entry.title = node.childNodes[4].firstChild.nodeValue;
            entry.content = node.childNodes[5].firstChild.nodeValue;
            entry.url = node.childNodes[6].attributes.href;
            entries.addItem(entry);
        }
        else if (node.localName == "totalResults") {
            totalResults = Number(node.firstChild.nodeValue);
        }
        node = node.nextSibling;
    } while (node != null);
    CursorManager.removeBusyCursor();
    startIndex += 10;
    // if there are still more items, start the process again
    if (startIndex < totalResults) {
        callNotebookURL();
    }
}

Conclusion

While some of this code might be useful to pick through, the finished application serves a specific purpose by improving my personal productivity for a particular task. You could argue that the primary difference between an application such as this and a typical application is the freedom to take shortcuts that you normally wouldn't take. This holds some truth, but the key requirement for a personal productivity application is that the time it takes to build the application must be significantly shorter than the time it would save the individual it is intended for. This is usually easily quantifiable because the audience is small and the task is very specific.

That's why Adobe AIR presents such opportunity for developing these applications. It offers tools that enable you to throw together an application like this very quickly. Now if I could just write an AIR application that can write articles for me....

‹ Back


Brian Rinaldi is as a Content and Community Manager for the Adobe Developer Center team, where he helps drive content strategy for HTML5 and JavaScript developer content. Brian blogs regularly at http://remotesynthesis.com and is a unreformed twitter addict @remotesynth.