Uma das primeiras coisas que eu fiz no meu primeiro aplicativo desenvolvido em Adobe Air, foi estudar uma maneira de minimizá-lo no System Tray, ou se preferir, como um pequeno ícone ao lado do relógio do Windows. É um ótimo recurso, principalmente quando você quer criar uma aplicativo de uso prolongado, como um Instant Messenger.
E então…? Vamos colocar a mão na massa no código…?
Você já deve ter o Adobe Flex instalado em seu computador – Caso não, baixe a versão Trial do site da Adobe, através deste link. Agora vá no menu File, opção New e escolha Flex Project.

Não esqueça de dizer que é em Adobe Air!
Dê um nome para o seu projeto (Project Name), escolha a pasta onde você quer salvá-lo (Project Location) e escolha a opção Desktop Application, já que estamos em uma dica de Adobe Air. Em seguida eu reduzi a área para 200×300 – apenas porque eu quis assim – e coloquei um label para o aplicativo não ficar vazio. Cliquei no botão de Debug e o resultado você confere na imagem à seguir…

Legal, mas pode ficar melhor...
Em seguida, eu decidi abrir o arquivo SystrayExample-app.xml ( Atenção: É o nome deste arquivo de configuração é o o nome do projeto + “-app.xml“ ) procurei pela linha 46, onde se fala do system chrome. Ela vem previamente comentada e devido à isso, seu valor padrão é standard. Vamos descomentá-la e aplicar o valor none, da seguinte maneira:
<systemChrome>none</systemChrome>
Depois dessa mudança, salve o seu projeto e rode novamente. Teremos então:

A opção Chrome
O que foi…? Você gostaria de personalizar totalmente o visual da sua janela…? É possível, mas deixaremos isso para uma outra oportunidade. Agora, experimente mover a sua janela, minimizá-la, restaurá-la, redimensioná-la. Veja que ela reage como a janela de um aplicativo comum. Feche-a, e veja o código do seu aplicativo, que deve ser mais ou menos assim:
<?xml version="1.0" encoding="utf-8"?> <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="200" height="300"> <mx:Label y="10" text="Exemplo de aplicação AIR" fontWeight="bold" horizontalCenter="0"/> </mx:WindowedApplication>
Agora vamos começar a programar de verdade. Primeiro, adicione um applicationComplete no mx:WindowedApplication. Ela serve para chamar a primeira função que o seu aplicativo deverá chamar, assim que ele estiver completo em memória. Eu decidi chamar essa função de AutoExec() em homenagem ao autoexec.bat da época do DOS. Em seguida, crie um bloco <mx:Script></mx:Script> como o exemplo abaixo:
<?xml version="1.0" encoding="utf-8"?> <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="200" height="300" applicationComplete="autoExec()"> <mx:Script> <![CDATA[ // Vamos inserir código AS3 aqui! ]]> </mx:Script> <mx:Label y="10" text="Exemplo de aplicação AIR" fontWeight="bold" horizontalCenter="0"/> </mx:WindowedApplication>
Agora, entre o <![CDATA[ e o ]]>, vamos inserir diversas funções. Para não ficar confuso, vou explicar aos poucos e depois, juntar tudo! Primeiro, vamos importar classes necessárias e declarar variáveis!
import mx.core.BitmapAsset; private var dockImage:BitmapAsset; private var win:NativeWindow; [Embed(source="systray_icon.png")][Bindable] public var mySysTray:Class;
Primeiro nós importamos a classe BitmapAsset, para em seguida declarar uma variável – dockImage – usando o tipo importado. Depois criei uma variável win para servir apenas de atalho para se referir à janela principal do aplicativo. Para finalizar, incorporei o ícone systray_icon.jpg ao SWF, já jogando ele na declaração do mySystray. Lembre-se, é um aplicativo AIR, então não dá pra confiar se uma determinada imagem vai estar no mesmo diretório da aplicação. É melhor deixá-la dentro do SWF ou acessá-la, remotamente.
private function autoExec():void {
win = stage.nativeWindow;
dockImage = new mySysTray() as BitmapAsset;
if (NativeApplication.supportsSystemTrayIcon){
SystemTrayIcon(NativeApplication.nativeApplication.icon).tooltip = "Nome do meu aplicativo!";
SystemTrayIcon(NativeApplication.nativeApplication.icon).addEventListener(MouseEvent.CLICK, undock);
SystemTrayIcon(NativeApplication.nativeApplication.icon).menu = createSystrayRootMenu();
win.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING,interceptMinimize);
}
}
Como mencionei antes, a função autoExec() está sendo chamada no applicationComplete, então ela é quem dará início à tudo. Primeiro, eu setei o meu “atalho-variável” win para a janela nativa da aplicação. Depois preparamos o dockImage e verificamos se o ambiente onde nossa aplicação está rodando suporta Systray. Se sim, ótimo: Vamos preparar o SystemTrayIcon. Em tooltip, podemos escrever o nome do nosso aplicativo. O addEventListener detecta o click do mouse para restaurar a janela, através da função undock que será discutida adiante. Depois associamos um menu ao botão direito ( createSystrayRootMenu ) e finalmente, iremos fazer um addEventListener para interceptar o ato tradicional de minimizar a janela.
private function interceptMinimize(e:NativeWindowDisplayStateEvent):void {
if (e.afterDisplayState == "minimized") {
e.preventDefault();
win.visible = false;
NativeApplication.nativeApplication.icon.bitmaps = [dockImage];
}
}
A função interceptMinimize roda toda vez que há mudança no status da janela. Então, no if nós iremos verificar se ele está tentando ser minimizado. Se sim, nós iremos CANCELAR através da função preventDefault(). Em seguida, iremos tornar a janela invisível e ativar o ícone na bandeja do relógio. Simples até aqui, não…?
private function undock(e:Event):void {
win.visible = true;
win.orderToFront();
NativeApplication.nativeApplication.icon.bitmaps = [];
}
Para realizar o undock e restaurar a janela, também é simples. Primeiro deixamos ela visível e por segurança, trazemos ela pra frente com a função orderToFront(). Em seguida, tire o ícone do Systray. Pronto! Com apenas isto você já consegue minimizar seu aplicativo Air no Systray. Agora vamos à um bônus: Colocando um menu no botão direito do mouse, quando o aplicativo estiver minimizado do lado do relógio:
private function createSystrayRootMenu():NativeMenu{
var menu:NativeMenu = new NativeMenu();
var openNativeMenuItem:NativeMenuItem = new NativeMenuItem("Mostrar o Aplicativo!");
var siteNativeMenuItem:NativeMenuItem = new NativeMenuItem("Acessar o meu Blog!");
var exitNativeMenuItem:NativeMenuItem = new NativeMenuItem("Fechar o Aplicativo...");
openNativeMenuItem.addEventListener(Event.SELECT, undock);
siteNativeMenuItem.addEventListener(Event.SELECT, openSite);
exitNativeMenuItem.addEventListener(Event.SELECT, closeWindowByEvent);
menu.addItem(openNativeMenuItem);
menu.addItem(siteNativeMenuItem);
menu.addItem(new NativeMenuItem("",true));
menu.addItem(exitNativeMenuItem);
return menu;
}
A função createSystrayRootMenu cria o menu inteiro. Neste exemplo, vamos criar um menu com três itens! O primeiro vai ser uma opção extra para restaurar o aplicativo minimizado. Veja que basta associar a função undock já desenvolvida à esta opção. O segundo item, chama uma função openSite, que irá abrir o browser padrão que irá carregar um link específico. A última opção, é para fechar o aplicativo sem ter a necessidade de maximizá-lo antes. Bom, como a undock já foi explicada, veja como ficou a função openSite:
private function openSite(e:Event):void {
var url:String = new String("http://www.armandoschiavondias.com.br/");
var urlReq:URLRequest = new URLRequest(url);
navigateToURL(urlReq);
}
Como você viu acima, é bem simples. Basta criar uma string com a URL do site, depois criar um URLRequest para finalmente chamar o navigateToURL. Agora vamos ver a última função do nosso exemplo:
private function closeWindowByEvent(e:Event):void {
var closing:Event = new Event(Event.CLOSING,true,true);
dispatchEvent(closing);
if(!closing.isDefaultPrevented()){
win.close();
}
}
Nesta função, nós simulamos a ação “tradicional” de fechar a janela, nativa do Flex. Ou seja, nós criamos um evento que na verdade não aconteceu – e guarda este evento fictício na variável closing – envia ele para o sistema ( através do dispatchEvent ) e se ela não for cancelada, finalmente fechamos a aplicação!
O código inteiro fica assim:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="200" height="300" applicationComplete="autoExec()">
<mx:Script>
<![CDATA[
import mx.core.BitmapAsset;
private var dockImage:BitmapAsset;
private var win:NativeWindow;
[Embed(source="systray_icon.png")][Bindable] public var mySysTray:Class;
private function autoExec():void {
win = stage.nativeWindow;
dockImage = new mySysTray() as BitmapAsset;
if (NativeApplication.supportsSystemTrayIcon){
SystemTrayIcon(NativeApplication.nativeApplication.icon).tooltip = "Nome do meu aplicativo!";
SystemTrayIcon(NativeApplication.nativeApplication.icon).addEventListener(MouseEvent.CLICK, undock);
SystemTrayIcon(NativeApplication.nativeApplication.icon).menu = createSystrayRootMenu();
win.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING,interceptMinimize);
}
}
private function interceptMinimize(e:NativeWindowDisplayStateEvent):void {
if (e.afterDisplayState == "minimized") {
e.preventDefault();
win.visible = false;
NativeApplication.nativeApplication.icon.bitmaps = [dockImage];
}
}
private function undock(e:Event):void {
win.visible = true;
win.orderToFront();
NativeApplication.nativeApplication.icon.bitmaps = [];
}
private function createSystrayRootMenu():NativeMenu{
var menu:NativeMenu = new NativeMenu();
var openNativeMenuItem:NativeMenuItem = new NativeMenuItem("Mostrar o Aplicativo!");
var siteNativeMenuItem:NativeMenuItem = new NativeMenuItem("Acessar o meu Blog!");
var exitNativeMenuItem:NativeMenuItem = new NativeMenuItem("Fechar o Aplicativo...");
openNativeMenuItem.addEventListener(Event.SELECT, undock);
siteNativeMenuItem.addEventListener(Event.SELECT, openSite);
exitNativeMenuItem.addEventListener(Event.SELECT, closeWindowByEvent);
menu.addItem(openNativeMenuItem);
menu.addItem(siteNativeMenuItem);
menu.addItem(new NativeMenuItem("",true));
menu.addItem(exitNativeMenuItem);
return menu;
}
private function openSite(e:Event):void {
var url:String = new String("http://www.armandoschiavondias.com.br/");
var urlReq:URLRequest = new URLRequest(url);
navigateToURL(urlReq);
}
private function closeWindowByEvent(e:Event):void {
var closing:Event = new Event(Event.CLOSING,true,true);
dispatchEvent(closing);
if(!closing.isDefaultPrevented()){
win.close();
}
}
]]>
</mx:Script>
<mx:Label y="10" text="Exemplo de aplicação AIR" fontWeight="bold" horizontalCenter="0"/>
</mx:WindowedApplication>
Atualização (26/06/2009): Graças à observação do Lucas Monteverde, a função undock que era declarada como private function undock(m:MouseEvent):void { passou a ser declada como private function undock(e:Event):void {, pois deixando o parâmetro “mais genérico” conseguimos acioná-la tanto pelo click do mouse quanto pela opção do menu do botão direito do mouse, sem problemas! Atualizei o código e o exemplo em Zip!
E se você preferir, faça o download de um zip, contendo este exemplo inteiro, prontinho para você estudar e aprender!

[...] [...]
Olá, o programa funcionou legal, só deu um problema quando vc chama a função undock selecionando com botão direto na system tray, pois a função undock esta para receber um MouseEvent, e assim ela recebe um Event, e da erro.
..de resto, muito bom!
Olá Lucas! Que bom que você detectou isso. Já consertei e atualizei o Post! =)