I’ve been waiting for a release of Flex (AIR) where system tray (systray) options where available and finally with the latest release of Adobe AIR it’s there.
After searching some while on the internet I’ve found my way of minimizing my AIR application to the system tray, including a little menu.
Important to understand is that I wanted a minimize solution for a WindowedApplication. The WindowApplication offers it’s own minimize, maximize and close buttons, so these actions have to be caught if we want to attach our own actions to them.
Flex offers you docking, so that your application can be minimized, this is available for Windows and Mac platforms. The example presented here is for Windows, but close to what it should be for Macs.
I’ve created an Systray test application which contains the basic code, necessary for docking and undocking your application from the systray. The only part you will not need is the dummy label…
To test this you can:
1. create a new Flex project (application type AIR) called Systray.
2. edit Systray-app.mxml and set SystemChrome to none (so that you get the cool app skin
).
2. copy the contents of the presented code into the Systray.mxml.
After testing it (and also without
), you should be able to place the code into your own project and use it.
Here’s the code:
<?xml version="1.0" encoding="utf-8"?><mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="initApplication()"><mx:Script><![CDATA[/*** This example describes how to dock an AIR application to the system tray* and then undock it again.* The minimize and close actions of the WindowedApplication are caught, so* that we can introduce our own actions.** A simple systray menu is presented, to show the usage of that.** @Author: S.Radovanovic* @Url: http://www.saskovic.com/blog/?p=5* @Date: 24-11-2007* @Author: Jeffry Houser* @Date: 09-01-2007* Updated for AIR Beta 3*/import mx.controls.Alert;import mx.events.CloseEvent;private var dockImage:BitmapData;/*** Initialize the application to the default values.* This method is called upon creationComplete from the Windowed Application** @Author: S.Radovanovic*/public function initApplication():void {//Use the loader object to load an image, which will be used for the systray //After the image has been loaded into the object, we can prepare the application //for docking to the system trayvar loader:Loader = new Loader();loader.contentLoaderInfo.addEventListener(Event.COMPLETE, prepareForSystray);loader.load(new URLRequest("http://www.saskovic.com/images/systray_icon_16.png"));//Catch the closing event so that the user can decide if it wants to dock or really //close the applicationthis.addEventListener(Event.CLOSING, closingApplication);}/*** Check if the user wants to close the application or dock it** @Author: S.Radovanovic*/private function closingApplication(evt:Event):void {//Don't close, so prevent the event from happeningevt.preventDefault();//Check what the user really want's to do //Alert.buttonWidth = 110;Alert.yesLabel = "Close";Alert.noLabel = "Minimize";Alert.show("Close or minimize?", "Close?", 3, this, alertCloseHandler);}// Event handler function for displaying the selected Alert button.private function alertCloseHandler(event:CloseEvent):void {if (event.detail==Alert.YES) {closeApp(event);} else {dock();}}/*** Check to see if the application may be docked and set basic properties** @Author: S.Radovanovic*/public function prepareForSystray(event:Event):void {//Retrieve the image being used as the systray icondockImage = event.target.content.bitmapData;//For windows systems we can set the systray props //(there's also an implementation for mac's, it's similar and you can find it on the net...
)if (NativeApplication.supportsSystemTrayIcon){setSystemTrayProperties();//Set some systray menu options, so that the user can right-click and access functionality //without needing to open the applicationSystemTrayIcon(NativeApplication.nativeApplication .icon).menu = createSystrayRootMenu();}}/*** Create a menu that can be accessed from the systray** @Author: S.Radovanovic*/private function createSystrayRootMenu():NativeMenu{//Add the menuitems with the corresponding actionsvar menu:NativeMenu = new NativeMenu();var openNativeMenuItem:NativeMenuItem = new NativeMenuItem("Open");var exitNativeMenuItem:NativeMenuItem = new NativeMenuItem("Exit");//What should happen when the user clicks on something...openNativeMenuItem.addEventListener(Event.SELECT, undock);exitNativeMenuItem.addEventListener(Event.SELECT, closeApp);//Add the menuitems to the menumenu.addItem(openNativeMenuItem);menu.addItem(new NativeMenuItem("",true));//separatormenu.addItem(exitNativeMenuItem);return menu;}/*** To be able to dock and undock we need to set some eventlisteners** @Author: S.Radovanovic*/private function setSystemTrayProperties():void{//Text to show when hovering of the docked application iconSystemTrayIcon(NativeApplication.nativeApplication .icon).tooltip = "Systray test application";//We want to be able to open the application after it has been dockedSystemTrayIcon(NativeApplication.nativeApplication .icon).addEventListener(MouseEvent.CLICK, undock);//Listen to the display state changing of the window, so that we can catch the minimizestage.nativeWindow.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING, nwMinimized); //Catch the minimize event}/*** Do the appropriate actions after the windows display state has changed.* E.g. dock when the user clicks on minize** @Author: S.Radovanovic*/private function nwMinimized(displayStateEvent:NativeWindowDisplayStateEvent):void {//Do we have an minimize action? //The afterDisplayState hasn't happened yet, but only describes the state the window will go to, //so we can prevent it!if(displayStateEvent.afterDisplayState == NativeWindowDisplayState.MINIMIZED) {//Prevent the windowedapplication minimize action from happening and implement our own minimize //The reason the windowedapplication minimize action is caught, is that if active we're not able to //undock the application back neatly. The application doesn't become visible directly, but only after clicking //on the taskbars application link. (Not sure yet what happens exactly with standard minimize)displayStateEvent.preventDefault();//Dock (our own minimize)dock();}}/*** Do our own 'minimize' by docking the application to the systray (showing the application icon in the systray)** @Author: S.Radovanovic*/public function dock():void {//Hide the applcationstage.nativeWindow.visible = false;//Setting the bitmaps array will show the application icon in the systrayNativeApplication.nativeApplication .icon.bitmaps = [dockImage];}/*** Show the application again and remove the application icon from the systray** @Author: S.Radovanovic*/public function undock(evt:Event):void {//After setting the window to visible, make sure that the application is ordered to the front, //else we'll still need to click on the application on the taskbar to make it visiblestage.nativeWindow.visible = true;stage.nativeWindow.orderToFront();//Clearing the bitmaps array also clears the applcation icon from the systrayNativeApplication.nativeApplication .icon.bitmaps = [];}/*** Close the application** @Author: S.Radovanovic*/private function closeApp(evt:Event):void {stage.nativeWindow.close();}]]></mx:Script></mx:WindowedApplication>- Download this code: Systray.txt
Tips on shortening some code or something else are always welcome.
Thanks for Jeffry Houser for updating it for Flex 3 (beta 3).
cool~~
very COOL…
BRAVO! Great work here and well documented example code. Keep up the great work
10x, very cool
If there was an easier / cleaner way to post this I would.
Here is code updated for Beta 3. For the most part, Shell was renamed to NativeApplication. It only took me a few minutes to change. I also removed unused imports. In an unexpected maneuver, Beta 3 is allowing us to access “NativeApplication” without importing it.
Looks like my code was stripped out of the post..
But if you contact me off-list I can provide it.
For those wondering, I have blogged about my Beta 3 rendition of Saskovic’s code here:
http://www.jeffryhouser.com/index.cfm/2008/1/8/How-do-you-put-an-AIR-App-in-the-System-Tray
Awesome! Thank you very much!
Although some tweaks were necessary to make this work in Flex 3, I’m beginning to understand a few more things about AIR desktop application development in Flex 3. I greatly appreciate your willingness to share your code with others. Thanks again.
Took me some while, but I’ve finally updated the code according to Jeffry Housers changes.
Thanks for your effort Jeffry!
This is great. There is one case that doesn’t work on Windows though.
The NativeWindowDisplayStateEvent is not fired if I minimise my desktop by pressing “Windows button + M” or “Windows button + D”.
It seems it’s just hidden.
How would I get around this?
No event is fired (that I can find).
Ran into the same problem as Sean. The event is not fired in case of using “show desktop” functionality in Windows (at least XP). However the actual “open” item in menu works and restores the windows but if you attach the same action to click the tray icon it will not restore windows (i traced and it actually runs the function but to no avail). Weird…
[...] http://www.saskovic.com/blog/?p=5 [...]
If you minimise this on a Mac nothing happens. you need to add a check there… “if supports dock icon… DONT prevent minimise action”.
Sick! Now to use Flash to animate the minimize/maximize …
To make easier?
I’m trying to do the same thing (Minimize to System Tray in Windows) but using Flash CS3 not Flex. Would you happen to know how to write the code for that? By the way, I’m using Air 1.0.
Also, it might be helpful to know that I created my own close (close_btn) and minimize (mini_btn) buttons.
Any help you can provide would be greatly appreciated!
How can i add mouse over event to system tray icon? I need a functionality to show a new window when the user point to the system tray icon. Any idea?
How can i add mouse over event in a system tray icon? I need to open new window when a user point to or mouse over system tray icon..Is this possible in AIR app? I appreciate any idea, thanx..
Hello, that is a very good tutorial but i got some trouble you know, it gives me debug error when i compile. These are the lines where i got error:
1- SystemTrayIcon(NativeApplication.nativeApplication .icon).menu = createSystrayRootMenu();
2- SystemTrayIcon(NativeApplication.nativeApplication .icon).tooltip = “Systray test application”;
3- SystemTrayIcon(NativeApplication.nativeApplication .icon).addEventListener(MouseEvent.CLICK, undock);
Well, if i quit those lines, the app seems to be working, but when i minimise, i don’t have any menu so i cannot close the app unless i do it from the taskbar. So i don’t know what to do. Help me please, i’m a desperate rookie, thanks.
Very nice code, very simple…but how can i put it into an .as file an invoke it from another mxml file, I don’t want to paste into my large app.mxml..
Thanks!! works great…
Thanks!
You are my Angel
thanks!!
it’s very helpfull to me
Hi!
I want to extend my SQL experience.
I red really many SQL books and would like to
get more about SQL for my work as db2 database manager.
What can you recommend?
Thanks,
Werutz
Thanks!
This code is great!
It works!
It is beautifully turned out .. I liked ..) would be continuously zahazhivat to you.
great thanks for that … just one little thing … because I’m using nativeApplication.exit(); I needed to change the listener in the init function to this.nativeApplication.addEventListener(Event.EXITING, closingApplication);
Cheers,
Ondrej
I Really appreciate you… Nice Post and working perfectly also easy to understand..
Thanks,
Logeshkumar P.
A very informative starter. Thanks muchly. A few comments (for those like me who found this article some 14 months after it was written):
You can use an embedded asset as the minimizing graphic. Woo!
[Embed(source='assets/systray_icon_16.png')]
[Bindable] private var dockImg:Class;
and in dock() replace the appropriate line with
NativeApplication.nativeApplication.icon.bitmaps = [BitmapAsset(new dockImg()).bitmapData];
Kill off all other references to “dockImage”
Also, creationComplete() is now evidently the incorrect event to use as setSystemTrayProperties() is trying to add events to the stage, which hasn’t yet been created. It only worked because the URL loading took long enough that the stage _was_ initialised. Replace it with applicationComplete() in the WindowedApplication declaration and it’s all good.
Thank for your stuff
For me it work well as this..
in my mxml…
private var dockImage:BitmapAsset;
[Embed(source='icons/systray.png')]
[Bindable] public var systrayIcoCls:Class;
private function init():void {
dockImage = new systrayIcoCls() as BitmapAsset;
if (NativeApplication.supportsSystemTrayIcon) setSystemTrayProperties();
addEventListener(Event.CLOSING, closingApplication);
addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING, minimizedApplication);
}
private function setSystemTrayProperties():void{
SystemTrayIcon(NativeApplication.nativeApplication.icon).menu = createSystrayRootMenu();
SystemTrayIcon(NativeApplication.nativeApplication.icon).tooltip = ‘Favoris Bank’;
SystemTrayIcon(NativeApplication.nativeApplication.icon).addEventListener(MouseEvent.CLICK, undock);
}
private function createSystrayRootMenu():NativeMenu{
var menu:NativeMenu = new NativeMenu();
var openNativeMenuItem:NativeMenuItem = new NativeMenuItem(’Ouvrir’);
var exitNativeMenuItem:NativeMenuItem = new NativeMenuItem(’Quitter’);
openNativeMenuItem.addEventListener(Event.SELECT, undock);
exitNativeMenuItem.addEventListener(Event.SELECT, closeApp);
menu.addItem(openNativeMenuItem);
menu.addItem(new NativeMenuItem(”,true));
menu.addItem(exitNativeMenuItem);
return menu;
}
private function alertCloseHandler(event:CloseEvent):void {
if (event.detail == Alert.YES) closeApp(event);
else dock();
}
private function closingApplication(evt:Event):void {
evt.preventDefault();
Alert.buttonWidth = 110;
Alert.yesLabel = ‘Quitter’;
Alert.noLabel = ‘Minimiser’;
alertWindow = Alert.show(’Quitter ou minimiser ?’, ‘Quitter ?’, 1|2, this, alertCloseHandler);
}
private function minimizedApplication(displayStateEvent:NativeWindowDisplayStateEvent):void {
if (displayStateEvent.afterDisplayState == NativeWindowDisplayState.MINIMIZED) {
displayStateEvent.preventDefault();
dock();
}
}
private function dock():void {
stage.nativeWindow.visible = false;
NativeApplication.nativeApplication.icon.bitmaps = [dockImage];
}
private function undock(evt:Event):void {
stage.nativeWindow.visible = true;
stage.nativeWindow.orderToFront();
NativeApplication.nativeApplication.icon.bitmaps = [];
}
private function closeApp(evt:Event):void {
stage.nativeWindow.close(); // My friend this is ze end
}
[...] – 6 Apps in AIR. The best application I liked is Agile Agenda. And for desktop system tray I found this tutorial I was looking for. Posted in Uncategorized [...]
I built a transparent desktop app and use my own custom buttons…simply called the functions and it worked like a charm without having to have a windowed app. Good luck and email me for the code if you need it.
Why would you use a Loader? Great for an example, but why would you wait for a URLRequest to come back. Just compile the thing in.
[Embed(source="/assets/icons/systrayImage.png")]
private var imagesource : Class;
Then in the init:
dockImage = new imagesource().bitmapData ;
Quicker, cleaner and smaller.
gr8 work dude..thnx a ton!! i was lookin for something this..
Cheers !!
thanks man
Hi,
Thanks for the great info.
The minimise to systray works fine when I compile and run the Air app from within Flex Builder but when I run the app from an install/.air the minimise no longer works and instead of minimising to systray when clicking the windows close/minimise buttons the app just dissapears.
Anyone know why this is happening? Is it somthing to do with settings in app.xml?
Cheers,
Luke
Hi! Thanks for the post, it got me started really quickly!
I have one question.
When I try the example with a png that has alpha transparency, this transparency is not shown in the tray icon.
I did a screenshot, so you can see what I mean:
http://bnd24.de/stuff/systray_wo_alpha.png
The black corners around the red clock should be totally transparent, I thougth…
How can I get transparency in systray icons working or does it work for you and its just a problem of my dosbox?
Thanks in advance!
Many thanks. A great tutorial!!!
This is very helpful. Thanks.
Very Nice work.
But i add more option in system array context menu how i do that.
Thanks