This is something I thought would take me a couple of hours to implement for the application I am working on but ended up taking way too long validating behavior and the gotchas between the PC and Mac OS’s so its definitely good utility code to keep in your toolbox. The behavior I was trying to mimic was that of apps that run in the background when the main window is closed and then can be re-invoked by clicking on the icon in the dock on the Mac or the system tray in Windows.
First the API, which I am pretty proud of. To get the above behavior, once you have my class included in the classpath, all you need to do is:
Doesn’t get much simpler than that ;). Optionally you can pass arguments to handle the invoke interaction and conditionally cancel it, an array of Bitmaps to choose from for the icon in the dock or system tray and a NativeMenu object that displays the menu that displays when the user right clicks on the icon. If the Array of Bitmaps is not passed in as a parameter, the code parses the applicationDescriptor XML file and uses the icons defined there (why doesn’t AIR do this by default?)
You can check the code at my AIRUtils Github repo.
Some important points to note:
1) InvokeEvent Gotcha
There were a few gotchas in the implementation. My first pass was something that looked like this:
For some bizarre reason this didnt work at all and debugging it showed that the InvokeEvent was being called multiple times. Turns out the InvokeEvent is unlike any other Event as (as per the docs): “When an event listener registers for an invoke event, it will also receive all invoke events that occurred before the registration”. The fix for that was to check NativeWindow visibility when activating the NativeWindow.
2) Overriding NativeWindow’s Close Event:
Another gotcha here: I wanted to call preventDefault() on the NativeWindow’s Close event, but that meant there was no way to exit the application on a Mac. Why? Because calling NativeApplication.exit called the close action on the NativeWindow which was being preventDefault-ed. So the app would just never exit. To fix this, I had to check if the NativeApplication was exiting when the close event was called and if so, not prevent its default behavior.
3) Loading the icon image from the appDescriptor.xml:
All the icon used in the dock/system tray is pulled from the appDescriptor.xml. The parsing/loading and capturing Bitmap of the icon is probably more work than just using a bitmap embedded in the swf but it made for a cleaner API. Another gotcha when implementing this was that using Loader.load() with a File’s nativePath worked on Windows but not on a Mac. Apparently the right way to load a file into a loader from the filesystem is by using the file’s url property and not the nativePath.