10 June 2010
General experience of building applications with Flash Professional is suggested. For more details on getting started with this Quick Start, refer to Building the Quick Start sample applications with Flash.
Intermediate
When an application needs to display informational messages without disturbing the user's workflow, toast-style windows can be the right solution. The WordUp example application, shown in Figure 1, creates toast windows with the following attributes:
The WordUp example illustrates how to use the following AIR APIs:
The WordUp example application runs in the background with no main window. When a timer with a randomized interval fires, the application displays a toast window containing a random message. After about 5 seconds, the message expires and the toast window closes automatically.
To detect when the user is present, the application listens for userIdle and userPresent events from the NativeApplication object. When the userIdle event is dispatched, message expiration is suspended until the next userPresent event. This keeps the messages on the screen until the user has a chance to read them. A userIdle event is dispatched when no keyboard or mouse input has been detected within an interval determined by the NativeApplication.idleThreshold property. The default interval is a reasonable 5 minutes, but WordUp resets the value to be short so the change in application behavior can be more easily observed.
To test the application, launch the WordUp installer (WordUp.air) file. On Windows, a system tray icon is displayed in the notification area of the taskbar. On Mac OS X, the normal triangle under the dock icon shows that the application is running. After about 5 seconds, the first message pop ups from the bottom of the screen. It then disappears after a few moments. If you leave your computer idle for several seconds, the messages stay on screen until you make a mouse or keyboard input, and then remain for a few seconds longer to give you time to read the messages.
To exit the application, on Windows, right-click the system tray icon and select Exit WordUp from the menu. On Mac OS X, click and hold the dock icon to open the menu and select Quit WordUp.
For more information about non-AIR specific functions and APIs used in this example, see the ActionScript 3.0 Language Reference.
The first step to creating a windowless application is to make sure that the initial window that is created automatically by AIR never becomes visible. The visibility of this window is controlled in two places. The first place is within the <initialWindow> element of the application descriptor file. If <visible>true</visible> is placed within this element, then the window is visible on application startup. The second place window visibility is controlled is the visible property of the window itself. The visible property can be set directly. It is also set to true when the window activate() method is called.
Note: If you are using the mx:WindowedApplication component from the Flex Framework to define your initial window, you must also set the component's visible property to false.
The main class added to the initial window by AIR is never deleted and garbage collected. You can close the initial window if you do not need to display it for your application. The objects referenced by the variables of the main class will also not be deleted unless you clear the references. In WordUp, the MessageCenter and DisplayManager classes are referenced by variables in the main WordUp class, so these objects are not subject to garbage collection, even though the initial window is closed.
It is often a good idea to give the user some indication that an application is running, even when it has no visible windows. On Mac OS X, the application dock icon serves this purpose. On Windows, the system tray icon can be used. The dock icon automatically appears in the dock when the application is running and provides a default menu for exiting the application so WordUp doesn't need to change the dock icon properties. The system tray icon is only shown if you assign an image.
WordUp detects whether it is running on an operating system that supports system tray icons using the static NativeApplication.supportsSystemTrayIcon property. If so, it adds a tooltip and a menu containing an exit command:
NativeApplication.nativeApplication.icon.bitmaps = icon.bitmaps;
if(NativeApplication.supportsSystemTrayIcon){
var sysTray:SystemTrayIcon =
NativeApplication.nativeApplication.icon as SystemTrayIcon;
sysTray.tooltip = "WordUp";
sysTray.menu = new NativeMenu();
var exitCommand:NativeMenuItem =
sysTray.menu.addItem(new NativeMenuItem("Exit WordUp"));
exitCommand.addEventListener(Event.SELECT, function(event:Event):void{
NativeApplication.nativeApplication.exit();
});
}
An exit command is not necessary on Mac OS X, because the dock icon automatically includes a command to quit the application.
To initialize the application logic, WordUp creates a DisplayManager object, which manages the pop-up toast windows, and a new MessageCenter object, which periodically generates an event containing a random message:
displayManager = new DisplayManager();
messageCenter = new MessageCenter();
messageCenter.addEventListener(MessageCenter.NEW_MESSAGE, onMessage);
The onMessage() event handler function passes the message text to the display manager, which creates a message window.
Since the initial window created by AIR is not needed, it is closed. Setting the autoExit property of the NativeApplication object to false prevents the application from terminating when the last window is closed.
NativeApplication.nativeApplication.autoExit = false;
stage.nativeWindow.close();
The MessageWindow class extends NativeWindow. The constructor sets the appropriate NativeWindowInitOptions for the window. The important options are:
options.type = NativeWindowType.LIGHTWEIGHT;
options.systemChrome = NativeWindowSystemChrome.NONE;
options.transparent = true;
These options define a window with no system chrome and which don't appear on the Windows taskbar or Mac OS X windows menu. In addition, the window alwaysInFront property is set to true so that the message appears above other windows.
To regulate the life span of a message window, the window listens to custom lifeTick events from the DisplayManager object:
manager.addEventListener(DisplayManager.LIFE_TICK,lifeTick,false,0,true);
When this event is dispatched, the window decrements its time-to-live counter. When the counter reaches zero, the window closes itself:
public function lifeTick(event:Event):void{
timeToLive--;
if(timeToLive < 1){
close();
}
}
public override function close():void{
manager.removeEventListener(DisplayManager.LIFE_TICK,lifeTick);
super.close();
}
The MessageWindow class overrides the close() method so that the event listener is removed when the window closes.
WordUp uses the Screen API to find a spot to display the new message. The findSpotForMessage() function searches for an open area starting with the lower right-hand corner of the first screen in the Screen.screens array. When it finds an open spot, it animates a window Move from the bottom of the screen to its appointed destination.
To check whether a message window is already displayed in a given spot, WordUp loops through the NativeApplication.nativeApplication.openedWindows array. It tests whether the rectangle defined by the area being considered intersects the rectangle defined by the bounds of an existing message window:
private function isOccupied(testRect:Rectangle):Boolean{
var occupied:Boolean = false;
for each (var window:NativeWindow in
NativeApplication.nativeApplication.openedWindows){
occupied = occupied || window.bounds.intersects(testRect);
}
return occupied;
}
WordUp uses the NativeApplication userIdle and userPresent events to determine whether the user is actively using the computer.
NativeApplication.nativeApplication.idleThreshold = idleTime;
NativeApplication.nativeApplication.addEventListener(Event.USER_IDLE,onIdle);
NativeApplication.nativeApplication.addEventListener(Event.USER_PRESENT,onPresence);
In response to these events, WordUp suspends or restarts the DisplayManager timer that dispatches lifeTick events to the message windows:
//When the computer is idle, don't remove the messages
private function onIdle(event:Event):void{
displayManager.pauseExpiration();
trace("Idling.");
}
//On return, let windows expire again
private function onPresence(event:Event):void{
displayManager.resumeExpiration();
trace("Resuming.");
}