Friday, September 19, 2008

Minimizing an AIR Application to system tray

Adobe AIR applications are windows applications. So they have all the possibilities like any other windows application like using the system tray.

First of all you have to prevent the application to close when clicked on the X close button on the window. To do that you have to trap the "closing" event of the WindowedApplication class.


<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="ApplicationComplete()" closing="MainWindow_OnClosing(event)">

or with actionscript:


this.addEventListener(Event.CLOSING, MainWindow_OnClosing);

When the user clicks on the close button on the corner of the main window MainWindow_OnClosing() function gets called. In this function you have to prevent the default action for the event which is closing the window. The reason why we listen to the "closing" event instead of "close" event is that the window is not closed yet. If you listen to the "close" event you can't prevent the window from getting closed.

So our closing event handler looks like this:


protected function MainWindow_OnClosing(event:Event):void
{
event.preventDefault();
this.visible = false;
}

We didn't close the window but we made it invisible. But how is the user going to interact with our program now? Yes. With the tray icon. So here is the sample code to setup a tray icon:


// A Loader to load the image for the icon png
var icon:Loader = new Loader();

// Not all the operating systems have to support tray icons.
// We just check for it. Windows supports it as you probably know. :)
if (NativeApplication.supportsSystemTrayIcon)
{
// When the user clicks the icon on the tray "TrayIcon_Click" event handler will be called.
NativeApplication.nativeApplication.icon.addEventListener(MouseEvent.CLICK, TrayIcon_Click);

// When the icon gets loaded it will call the iconLoadComplete event handler.
icon.contentLoaderInfo.addEventListener(Event.COMPLETE, iconLoadComplete);

// We actually start loading an icon from the hard drive.
// This could be an icon on the internet as well
icon.load(new URLRequest("images/icons/icon16x16.png"));

// Lets get a handle of the trayIcon to change its tooltip.
var systray:SystemTrayIcon = NativeApplication.nativeApplication.icon as SystemTrayIcon;
systray.tooltip = "Your Application Tooltip";
}

Below you can see the handler for the icon load event.


private function iconLoadComplete(event:Event):void
{
NativeApplication.nativeApplication.icon.bitmaps = [event.target.content.bitmapData];
}

For best performance the icon image should be a 16x16 PNG. (At least in windows this is so).
So what happens when the user clicks the icon in the tray?


protected function TrayIcon_Click(event:Event):void
{
// Do anything here.
}

It is up to your imagination. :) You could do a bunch of things here. You can make the main application visible.

I can hear you guys asking how to add a menu to your tray icon. Ok! Adding a menu to your tray icon is very simple. Below there is the modifed version of the example code at the top:



// A Loader to load the image for the icon png
var icon:Loader = new Loader();

// Not all the operating systems have to support tray icons.
// We just check for it. Windows supports it as you probably know. :)
if (NativeApplication.supportsSystemTrayIcon)
{
// When the user clicks the icon on the tray "TrayIcon_Click" event handler will be called.
NativeApplication.nativeApplication.icon.addEventListener(MouseEvent.CLICK, TrayIcon_Click);

// When the icon gets loaded it will call the iconLoadComplete event handler.
icon.contentLoaderInfo.addEventListener(Event.COMPLETE, iconLoadComplete);

// We actually start loading an icon from the hard drive.
// This could be and icon on the internet as well
icon.load(new URLRequest("images/icons/icon16x16.png"));

// Lets get a handle of the trayIcon to change its tooltip.
var systray:SystemTrayIcon = NativeApplication.nativeApplication.icon as SystemTrayIcon;
systray.tooltip = "Your Application Tooltip";

// Lets add a menu to our tray icon
var iconMenu:NativeMenu = new NativeMenu();
var menuCommand:NativeMenuItem = iconMenu.addItem(new NativeMenuItem("Exit"));
menuCommand.addEventListener(Event.SELECT, Exit_Handler);

// Assign our menu to the icon. Thats it.
systray.menu = iconMenu;
}

Here we have added a menu item which will close the application when clicked on. To close the application we call the exit() function of the native application. Here is the Exit_Handler function:


protected function Exit_Handler(event:Event):void
{
// Actually exiting the application it self removes the items from the tray.
// But just to give an example I clear the icons.
NativeApplication.nativeApplication.icon.bitmaps = [];

NativeApplication.nativeApplication.exit();
}

Here are the example source codes.

1 comment:

Ganesh Bhat said...

Some cases, just calling this.activate(); and this.restore();
wont be sufficient.

I had a similar requirement, where I had to write an event handler like this

--- event handler --
this.activate();
this.restore();

if(this.alwaysInFront != true) {
this.alwaysInFront = true;
else {
this.alwaysInFront = false;
this.minimize();
}