Skip to content

A podcast for those who design, develop and run websites.

Boagworld is the blog of web strategist Paul Boag who lives in the heart of rural Dorset (hence the cows). He produces a weekly podcast with UX consultant Marcus Lillington on building and running websites. They also run the web design agency Headscape.

Latest Shows

203. Why your blog fails
This week on boagworld: the secret of successful blogging, will Google personalisation affect your sites ranking and how to help users too busy to read.
202. Rocket Surgery Made Easy
This week on Boagworld: Steve Krug on monthly usability, Steve Marshall talks about form design and Paul rejoices over the new era for browsers in Europe.
201. Are clients stupid?
This week on Boagworld: We review the freelancing book Noded, discover a new web tool called 'Support Details' and Paul tells us all a story.
200. A taste of the show
This week's show gives you a taste of the live 12 hour marathon that took place to celebrate the 200th Boagworld.
199. Time to generalise
This week on Boagworld: The changing role of web designers, Colin Firth on content and Becky Jones talks about the changes at Google.

or view all shows

Have your say

Become a part of the Boagworld community...

Our First AIR App

Posted in Tech/Development on: Friday, February 13, 2009 by Paul Boag

With Adobe AIR now up to 100 million downloads, being utilised by big name players such as the BBC and increasingly invading our desktop Headscape decided to give it a go.

This is a guest post by two of the Headscape Craig Rowe and Dave McDermid

Setting up

Adobe is Flash + WebKit + SQL Lite on the desktop. As a Flash developer you can dive right in and use the Air extension for Flash to publish your beautifully crafted swfs and AS into an installable cross platform desktop app. However, the flash projector has been around for a while doing similar things and we wanted to put our hard earned web skills to productive use. So we went the / route.

The SDK is free for download but Aptana provides a rather neat eclipse based IDE in which to work. Handily the new project wizard allows you to import a multitude of Javascript making it incredibly easy to get a new project up and running with your preferred initial setup (we went for ).

The Problem

To make the exercise worth while we needed a real world problem to solve. Trivial examples usually do very little other than increase your ability to /paste example code and do your best night ‘ooo’ ‘ahh’ impression at it. Instead, we chose to add to our server admin experience…

As loving, caring web developers we actively monitor all our servers, and most of our live websites. For a while this was a DOS script, this was then migrated to a Bash script on a Cron job. It worked great, but required a computer science degree to maintain. So here was our problem: we needed a pretty, maintainable and reliable app to monitor websites. All it had to do was let us know when one disappears or throws a nasty error.

Download and try our site watcher AIR application

The Journey

Step 1: The wireframe

Paper Wireframe

A new Adobe AIR project in Aptana comes with an html file named after your application which acts as your main program window. Creating a basic with a few buttons, titles etc can be done in a snap. The html, and javascript are dealt with in exactly the same way as if intended to be deployed as a website (with no compatibility worries as we are only targeting the Adobe AIR WebKit rendering engine).

Step 2: The magic

The wireframe identified the main viewer as comprising of an unordered list of sites, each with their current status and edit/check/remove links. This list needed to be persisted, but editable by the user.

If this was a website we may be looking to server side scripting and a database of some nature. However, we had only jQuery and the air libraries. Although SQL Lite was an option we decided it was an over complication for what is a relatively simple, first AIR app. So, knowing that we could use jQuery to manipulate a and the air libraries to save and open local files we opted for XML as a data source.

<?xml version="1.0" encoding="utf-8"?>
<sites>
<site address="http://www..co.uk/" status="200" frequency="30">
Headscape&#146;s website
</site>
...
</sites>

Looking back into our application html file we can see that we are given a readLocalfile() function that returns the string contents. This can then be passed into a jQuery object and manipulated in the usual way.

[The canny among you will notice that this readLocalfile() function is merely a few calls to the flash filesystem classes (using the AIR aliases). In fact at some points I directly call the flash library rather than using the AIR alias. There’s no functional difference, I’m just used to the flash namespaces]

With this ability added to the jquery ajax capabilities the application flow could be easily envisaged as follows:

  • On DOM Ready read the local xml file
  • For each site element in the xml create an LI element with the appropriate display and action elements
  • Fire off a jquery ajax call for each site
  • Use the response code to formulate a class for the LI to change its display
  • Fire off any notifications if the response code has changed i.e. e-mail, notification window, post
  • Set a timeout before checking again
  • On window close parse the unordered list back into xml and write it to the persistent file

The Stumbling Blocks

Viewing the source of an installed AIR app can be done by nipping into program files (or Applications for the MACs amongst us) and looking in the application name directory. Here you will see the html, css and javascript files that make up the app (so we can continue to learn from others deployments just as we would with a website).

A brief look at the sitewatcher source and the flow described above becomes immediately clear. ‘Sitewatcher.html’ is our main form and it includes script.js as the main driver of the application with the #sites ul as the main containing element. The rest is GUI. ‘PopulateUIfromXML()’ directly completes steps 1-2 and fires off the 3-6 process via ‘CheckSite()’. However dispersed within this are the unusual non-front end website development bits, so we’ll look at those now:

Acting as a System Tray App

Many AIR apps, particularly those to do with notification (twitter, yammer etc) seem to want to run as system tray applications, we were no different.

The process of doing so is relatively easy, and encapsulated in the appropriately named SetUpSysTray() function of script.js. Essentially what we need to achieve is an override of the minimise behaviour, the setting of an appropriate icon and the associated window toggling behaviour.

Window.nativeWindow gives us access to the OS window holding our html window, and we can listen to events on it in much the same way. ‘nwMinimized’ is set to fire on display state changing and, if being minimized, instead docks (hides) the window and prevents the default behaviour.

if(air.NativeApplication.supportsSystemTrayIcon)
window.nativeWindow.addEventListener(runtime.flash.events.NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING, nwMinimized);
function nwMinimized(nativeWindowDisplayStateEvent) {
if(nativeWindowDisplayStateEvent.afterDisplayState == runtime.flash.display.NativeWindowDisplayState.MINIMIZED) {
nativeWindowDisplayStateEvent.preventDefault();
Dock();
}
}
function Dock() {
window.nativeWindow.visible = !window.nativeWindow.visible;
}

This works fine but on its own will actually just hide the window from view/the taskbar leading the user to have to use task manager to close it. To ensure that an icon for your application sits in the system tray we need to set an icon for the nativeApplication (the mere presence of which will cause windows to display the app in the systray).

Back to SetUpSysTray() and we’re using a loader to load the icon graphic into memory, with an iconLoadComplete handler waiting to do the work:

var iconLoader = new runtime.flash.display.Loader();
iconLoader.contentLoaderInfo.addEventListener(air.Event.COMPLETE, iconLoadComplete);
iconLoader.load(new air.URLRequest("../icons/AIRApp_16.png"));
function iconLoadComplete(event){
if(air.NativeApplication.supportsSystemTrayIcon){
air.NativeApplication.nativeApplication.icon.bitmaps = new Array(event.target.content.bitmapData);
air.NativeApplication.nativeApplication.icon.tooltip = "SiteWatcher";
air.NativeApplication.nativeApplication.icon.menu = new air.NativeMenu();
// Create Menu Items
var openCommand = new air.NativeMenuItem("Toggle");
openCommand.addEventListener(air.Event.SELECT,function(event){
Dock();
});
var sep = new air.NativeMenuItem("", true);
var exitCommand = new air.NativeMenuItem("Exit");
exitCommand.addEventListener(air.Event.SELECT,function(event){
air.NativeApplication.nativeApplication.exit();
});
// Add Items to menu
air.NativeApplication.nativeApplication.icon.menu.addItem(openCommand);
air.NativeApplication.nativeApplication.icon.menu.addItem(sep);
air.NativeApplication.nativeApplication.icon.menu.addItem(exitCommand);
}
}

We simply set the native application icon to be the bitmap data before finalising the sys tray setup by creating a menu to appear on click.

Note for MACs: The minimised event listener is not applied if the system does not system tray icons. This is to avoid confusion on MACs where a minimise goes to the MAC dock anyway.

Notification Windows

So the app can now run in the system tray and use jQuery to check the sites listed in an XML file at regular intervals. However we need a process of notification. The App could be running on an actively used machine in which case we want messenger style pop-ups. Or it could be running on a separate machine/server from where we want it to send e-mail/other notifications.

In the case of window notifications we took a short cut and used some example code from Adobe Developer Center. This is encapsulated within the DisplayManager.js and Message.js files. Display Manager acts as a queue, dequeuing and displaying on a timed basis if the user is present. This is an important requirement as you do not want a user missing a notification prompt merely because they were away from their desk for a while. It can be easily achieved via the USER_IDLE and USER_PRESENT air events – in this case stopping and starting the poller.

‘CheckSite()’ simply queues message HTML when the response code received is different from the previously stored code. When the queue poller is running (the user is present) the message is dequeued and displayed via Message.js.

At this point it is worth remembering that notification windows are no different to any other native window. It’s just a name we’re giving to the way we are using native windows. They therefore have their own events, contents, position etc and this can be seen by the Message.js code where a new chromeless, transparent native window is created and its contents loaded from the message.html template.

The display process is then as follows:

  • Message.js stores the message content in a variable on the new window
  • MessageScripts.js, then running once the message.html dom is ready, sets a listener on the HTML_BOUNDS_CHANGE event before setting the message variable content as the body – ultimately firing the bounds change event
  • This event is handled by setting the native window height to match before firing the layoutComplete event
  • On hearing this Message.js makes the window visible, finds an appropriate resting position (in relation to any other messages) and animates it in.
E-mailing

A key feature of any site watcher is to let us know when something bad happens. The combination of emails and an -to-text service allows us to be notified the minute we spot trouble. This was easy enough in the bash script, using sendmail on the Mac. Not so straight forward for an AIR app. We can’t run sys commands and there is no built-in SMTP server. The solution is to use sockets in AIR. A little hardcore, but it keeps the solution nice and tight.

Anyone who’s sent an email with telnet will know that the principle of SMTP is, as the acronym suggests, simple. Adobe gives us plenty of clues for opening sockets and listening for messages. All we had to do was make sure we sent the right info. There are some restrictions in opening sockets from scripts outside the application sandbox, but for our purposes it worked a treat. With a little trial and error we were firing off emails left right and centre.

The icing on the cake was adding twitter support. With a one-line AJAX call in JQuery and a little config it was a no-brainer. This allows us to keep an online audit in the form of a private tweet stream. For people who check twitter more frequently than their email, it’s handy for notifications. If Twitter let us UK folk receive updates via SMS again then we can ditch email-to-text in favour of Twitter.

Step 3: The makeover

One of the nice benefits to working in the web-kit world is being able to use some CSS3 styling such as rounded corners. So we went to town. The more that is CSS based and the less that is image based the better.

JQuery UI allowed us to make the entire list sortable in a sweep of the mouse, and the prefs popup tabbed in a blink (suddenly there was heaps to customise!).

The End

Hopefully this post has given you an understanding of how quick and easy it really is to make a useful AIR application. We’ve shown how you can implement a system tray application that utilises notification popup windows and sends e-mails as well as uses local files as a data store. This is not intended as a best practice discussion. It was our first AIR app and developed in a very small amount of time as a proof of concept and so that we could share our experiences with you. We welcome any feedback.

Download and try our site watcher AIR application

What did you think about this post?

10 Comments

Comments are for the discussion of this post. If you have other questions / comments then post them to the forum or send me an email

  • Paul says:

    Very interesting, thanks. I’m going to have a play with adobe air. I have been avoiding it until now. :)
    On a side note, I quite like the app. Add screen shots and some decent websites ;) and i’ll use it.

  • Ben Bishop says:

    Great, looks very handy. Noob question: would this checking of the site appear in the analytic’s log for the site? Presumably anything js based wouldn’t?
    Also is it likely to be polished a little more? When I remove a site, the remove button then disappears from the other sites. Thanks.

  • Craig says:

    Its effectively the same as you doing an AJAX call to display the target site on your site but without it actually rendering in a browser. So yes the page is requested from the server, so server side logging would be triggered. However as you say the javascript would not be run so js based google analytics should not be effected. As for polishing I’m not sure.. we will most probably keep it up to date in house as and when and that may well filter its way onto here somehow…

  • Paul says:

    Another thought. This app would be great if you could add live feeds from websites like cssmania and design meltdown etc with screenshots. I realize it’s not the purpose of the post, but just a thought on improving the sample app. :)

  • Justin Long says:

    It looks nice the only thing I saw is that once you go to fullscreen you have to select fullscreen again to get it back to a window. Which I know is not all in all the end of the world but it can be slightly confusing. But now that I am done with the negative I think it is great little app and I can’t wait to see where you go with it.

  • Richard says:

    Great first app. Would you consider putting together some reference to get started with Air, books, sites, video, that kinda thing :)

  • Helder says:

    While testing headscape site watcher I ran into what seams to be a bug. I was editing the frequency option for a site» pushed Enter and all the sites I had previously added disappeared remaining only the default ones’.

  • Sulcalibur says:

    I’ve been quite interested in Adobe Air for a while now and in a year or so would love to build an app I have planned out. I was chatting to (the Mighty) Jonathan Snook about it and was very intrigued when he told me that “anything that you can make on a website, you can make into an app in ‘air’”. This just us humble web developers is amazing. Across the operating systems is an even bigger plus :D
    Thanks for the article, going to try out your app now.

  • Switchmac says:

    Looking good, so when are you releasing the iPhone app then? ;0)

  • rb3m says:

    An excellent introduction to Air! I have been putting it off, but I had not realised how easy it is to create an Air app. Thanks, Paul!

Leave a comment

Additional Information

Produced by Headscape

Boagworld is produced by the web design agency Headscape founded by Marcus, Paul and Chris Scott. Headscape also has a number of other talented guys who blog. Check them out.

  • Craig Rowe is one of our amazing developers and writes some superb posts on everything from .net to AIR apps.

  • Ed Merritt is a Headscape designer who's blog contains examples of his work and a number of free Wordpress themes.

  • Dave McDermid is a Headscape developer who has an excellent blog. He blogs on everything from AJAX to security.

  • Rob Borley is one of our project managers and blogs regularly on client and project management issues.

  • Leigh Howells is our multimedia design guru (whatever one of those is). He blogs on a mixture of design and music.

Paul elsewhere

Paul just can't shut up. He publishes regular audioboos, has a personal blog and is addicted to twitter. He also writes and speaks regularly. Check out the most recent below: