Mar
3
2010

Creating a auto-update application with AIR

Hello, everyone!

In this article I will address a subject that I consider very interesting and that Adobe had a special care when designing this update on the AIR SDK 1.5, which was the API for auto-update of your application.

It is possible that you configure your application to search it on the server if an update is available and if so, if you want to download and update your application. You can still tell the User’s system which would be the reasons for the upgrade of the application. All this through a few lines of coding and only one XML file on your server.

To illustrate this, let’s create an application that will read a feed of Imasters here. The first version of the application will be responsible for reading only one of the feeds, and your upgrade may have more options for reading.

I’ll start with the assumption that you already know to create a project in FlexBuilder for AIR. This application has some interesting details. The first is that it will have its background transparent, that is, you have the impression that the application is running as a gadget, another major innovation that brought him to Adobe AIR.

I created a project called AIRAutoUpdate, FlexBuilder and created the following structure:

Flex Project

Flex Project

Well, for those who do not know the structure of the AIR, the file AIRAutoUpdate-app.xml is responsible for defining various information to the compiler. The FlexBuilder adds a very extensive file when you create a project, we will replace the file that he created a version simple, containing only what is relevant to this tutorial, as reported below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.5">
	<id>AIRAutoUpdate</id>
	<version>1.0</version>
	<filename>AIRAutoUpdate</filename>
	<name>AIRAutoUpdate - Tutorial</name>
	<installFolder>Horochovec/AIR/AIRAutoUpdate</installFolder>
	<programMenuFolder>Horochovec/AIR/AIRAutoUpdate</programMenuFolder>
	<description>Creating a auto-update application with AIR</description>
    	<copyright>Stefan Horochovec (stefan@horochovec.com)</copyright>
	<initialWindow>
		<title>AIRAutoUpdate</title>
		<content>AIRAutoUpdate.swf</content>
		<systemChrome>none</systemChrome>
		<transparent>true</transparent>
		<visible>true</visible>
		<minimizable>true</minimizable>
		<maximizable>false</maximizable>
		<resizable>false</resizable>
	</initialWindow>
</application>

This article is not meant to describe all the information that was assigned to XML, however, some exceptions will be made to the attributes that are the basis for this article.

  1. <version></version> – Used to define which version of the application currently is used to make version checks for updates.
  2. <systemChrome>none</systemChrome> – Definition of the type of System Chrome, this option has no default value “standard”, however, to create an application with transparent background, you should assign the value “none”.
  3. <transparent>true</transparent> – Setting the window should be transparent, is applicable only when the type is systemChrome none.
  4. <visible>true</visible> – Sets whether the window should start as visible as the default value is false, we move to true.

The compiler will get the necessary information at the application setting for your background is transparent.

That done, we can start to develop the application itself. I created a component called Posts.mxml, it is in the package: br.com.horochovec.view.Posts. The idea of this component is that it is responsible for consuming the feed on Flex here the Imasters site, pass some information to a grid and, thus, the User can list the posts on the site and learn more about it.

This component extends from a TitleWindow, within it there is a VBox, AdvancedDataGrid, and a Canvas, which contains within it a Label, a TextArea and a LinkButton.

The component is a VBox container that will group all the other visual components of that component.

Although the component Posts.mxml, I own an HTTPService. This HTTPService is responsible for consuming the feed Imasters in its moment of creation. That is, when the component is created Posts.mxml, will consume the HTTPService to fetch all the articles on the site in the Flex. The format of the consumption of information will be through e4x.

The AdvancedDataGrid has only a single column, she is responsible for printing the title of the article. If the User chooses to click on the item of the Grid, I ask for the application to show me the table below (composed of a Canvas, Label, TextInput and LinkButton) some information from the selected post.

Following is the source in the developed part of the component:

?Download Posts.mxml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow 
	xmlns:mx="http://www.adobe.com/2006/mxml" 
	layout="absolute" 
	width="500" 
	height="250" 
	showCloseButton="true" 
	creationComplete="creationCompleteHandler(event);"
	close="onCloseHandler(event);" 
	title="Feeds Imasters - Flex Session">
 
	<mx:Script>
		<![CDATA[
 
			import flash.desktop.NativeApplication;
			import mx.events.CloseEvent;
			import mx.events.FlexEvent;			
			import mx.rpc.events.ResultEvent;
 
			private var xmlList : XMLList;
 
			private function creationCompleteHandler(event:FlexEvent) : void
			{
				this.service.send();
			}
 
			private function serviceHandler(event:ResultEvent) : void
			{
				var result : XML = new XML(event.result);
				xmlList	= result..item;
				this.grid.dataProvider	= xmlList;
			}
 
			private function onCloseHandler(event:CloseEvent) : void 
			{
				NativeApplication.nativeApplication.exit();
			}
 
			private function onClickGridHandler(event:MouseEvent) : void
			{
				this.btnLink.visible	= true;
				this.autor.text		= ((this.grid.selectedItem) as Object)['author'];
				this.descricao.text	= ((this.grid.selectedItem) as Object)['description'];
			}
 
			private function onBtnLinkClickHandler(event:MouseEvent) : void
			{
				navigateToURL(new URLRequest(((this.grid.selectedItem) as Object)['guid'].toString()), "_blank");
			}
 
		]]>
	</mx:Script>
 
	<mx:HTTPService id="service" url="http://imasters.uol.com.br/feed/secao/flex" 
					showBusyCursor="true" resultFormat="e4x" 
					result="serviceHandler(event)"/>
 
	<mx:VBox bottom="5" left="5" right="5" top="5">
		<mx:AdvancedDataGrid id="grid" designViewDataType="flat" 
							 bottom="5" left="5" right="5" top="5" 
							 height="50%" width="100%"
							 click="onClickGridHandler(event);">
			<mx:columns>
				<mx:AdvancedDataGridColumn headerText="Titulo do artigo" dataField="title"/>
			</mx:columns>
		</mx:AdvancedDataGrid>
 
		<mx:Canvas height="50%" width="100%" borderColor="#DADADA" borderStyle="solid">
			<mx:Label x="6" y="6" id="autor" fontWeight="bold"/>
			<mx:TextArea 	height="53" id="descricao" bottom="6" left="6" right="6" 
							borderStyle="none" selectable="false" mouseFocusEnabled="false" 
							mouseEnabled="false"/>
			<mx:LinkButton 	x="383" y="4" label="Ler artigo" visible="false" 
							id="btnLink" click="onBtnLinkClickHandler(event);"/>
 
		</mx:Canvas>
 
	</mx:VBox>
 
</mx:TitleWindow>

Some interesting considerations about the component above:

  • creationComplete=”creationCompleteHandler(event);” – When the component has been created, it will trigger the funçãocreationCompleteHandler (), which is responsible for initiating the use of the HTTPService.
  • onCloseHandler(event:CloseEvent); – In this event the class is thrown NativeApplication being responsible for closing the application by the close button of TitleWindow, base component for the component Posts;
  • serviceHandler(event:ResultEvent); – Event responsible for filtering the information that the HTTPService consumed and returned as a string. This information was passed to an XMLList, it will be the dataProvider of the grid.

With this we have a component based on a TitleWindow, which is able to seek information from a site feed of Imasters through HTTPService and provide information on the screen.

Inside my main application, I know the definition of some styles, so that the transparent background to work without any inconvenience.

For a better interpretation, I will provide all the main source, after commenting on each of the observations necessary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
	xmlns:mx="http://www.adobe.com/2006/mxml"
	xmlns:app="br.com.horochovec.view.*" 
	width="500" height="250"
    	layout="absolute"
    	paddingRight="0"
    	paddingLeft="0" 
    	creationComplete="onCreationCompleteHandler(event);">
 
    <mx:Style>
        Application 
        {
            background-color:""; 
            background-image:""; 
            padding: 0px;
        }
    </mx:Style>
 
    <mx:Script>
    	<![CDATA[
    		import air.update.events.DownloadErrorEvent;
 
    		import air.update.events.UpdateEvent;
    		import air.update.ApplicationUpdaterUI;
    		import mx.controls.Alert;
    		import mx.events.CloseEvent;
    		import mx.events.FlexEvent;
 
    		private var autoUpdater	: ApplicationUpdaterUI = new ApplicationUpdaterUI();
 
    		private function onCreationCompleteHandler(event:FlexEvent) : void 
    		{
    			this.main.addEventListener( MouseEvent.MOUSE_DOWN,	onMouseDownHandler );
    			this.autoUpdateInit();
    		}
 
    		private function autoUpdateInit() : void 
    		{
    			this.autoUpdater.updateURL	= "http://www.horochovec.com.br/air/update-config.xml";
    			this.autoUpdater.isCheckForUpdateVisible			= false;
    			this.autoUpdater.addEventListener(UpdateEvent.INITIALIZED,	onUpdateApp);
    			this.autoUpdater.addEventListener(ErrorEvent.ERROR,		onErrorUpdate);
    			this.autoUpdater.initialize();
    		}
 
    		private function onUpdateApp(event:UpdateEvent) : void 
    		{
    			this.autoUpdater.checkNow();
    		}
 
    		private function onErrorUpdate(event:ErrorEvent) : void
    		{
    			Alert.show(event.toString());
    		}
 
    		private function onMouseDownHandler(event:MouseEvent) : void
    		{
    			stage.nativeWindow.startMove();
    		}
 
 
    	]]>
    </mx:Script>
 
    <app:Posts x="0" y="0" id="main"/>
 
</mx:Application>
  • creationComplete=”onCreationCompleteHandler(event);” – At the end of the creation of application, the function onCreationCompleteHandler () is triggered, it is responsible for adding one of eventListner MouseDown component in my posts and also performs the function autoUpdateInit (), which will responsible for object I start my automatic update.
  • function autoUpdateInit(); – This role is the configuration of the object to perform the automatic update. The first informed attribute is the URL of the XML file that will be consulted to see if an update is available for the application (the file format will be informed as a result). The second attribute is reported to be visible to the search for new updates, this attribute is reported as true, so we can monitor the search for new updates and see if it really is working to implement. Then, two eventListner are added to the object, when it boots, and other case there is any error. Finally, the function initializes the update through the function initialize ();
  • function initialize(); – This function is responsible for requesting the update component to check the URL listed if the application has a new version.

The final configuration must be made within the project is that, within the src folder, you must create a file called update-config.xml. This file will contain the information about the update process should behave.

1
2
3
4
5
6
7
8
9
10
11
12
<configuration xmlns="http://ns.adobe.com/air/framework/update/configuration/1.0">
	<url>http://www.your-website.com/air/update-config.xml</url>
	<delay>1</delay>
	<defaultUI>
		<dialog name="checkForUpdate" visible="true" />
		<dialog name="downloadUpdate" visible="true" />
		<dialog name="downloadProgress" visible="true" />
		<dialog name="installUpdate" visible="true" />
		<dialog name="fileUpdate" visible="true" />
		<dialog name="unexpectedError" visible="true" />
	</defaultUI>
</configuration>

All options were marked as true, so you can follow all the stages of the process.

From this moment, the work is all due to the Flex. There is no need for you to worry about anything. He will be responsible for the verification, suggest, or not download, and if the User chooses to download the application, I will still save it to your hard drive, or automatically update the application. Easy, right?

But as he knows that there is a new version to be downloaded?

Through an XML file that will be published on the server at the same URL was reported in the URL property of the object AutoUpdate.

Let’s take a given on how this should be the XML AutoUpdater can make the necessary checks.

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>  
<update xmlns="http://ns.adobe.com/air/framework/update/description/1.0">  
	<version>1.1</version>  
	<url>http://www.your-website.com/air/AIRAutoUpdate1-1.air</url>  
	<description><![CDATA[   
	v1.1
	* News feeds to read
	]]></description>  
</update>

Simple, right? Little information is necessary for the update to occur. You should report only the version of the application to be updated. That is, my first compilation of the first version of the project was 1.0, the next version, it was built as 1.1, then it is this version that I should go to the update-config.xml.

Second, I must inform the URL where you download the file which should be done to upgrade. And finally, I can describe what’s changed in the version that generated the new update.

Thus, when we start the application, the application will read this XML, check that there is a new version available, and will start the update process.

Put simply, you no longer need to worry about updaters or to inform his client that there is a new version of the system. Leave the AIR work a little for you!

The codes given above, we have the first version of the track, except the XML update, which should only be published when there is a new version. Below, follow the sources for version 1.1 of the project. These, yes, without further comment, since the whole point of the article was only to explain how the update is done automatically for applications in AIR.

AIRAutoUpdate-app.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.5">
	<id>AIRAutoUpdate</id>
	<version>1.1</version>
	<filename>AIRAutoUpdate</filename>
	<name>AIRAutoUpdate - Tutorial IMasters</name>
	<installFolder>Horochovec/AIR/AIRAutoUpdate</installFolder>
	<programMenuFolder>Horochovec/AIR/AIRAutoUpdate</programMenuFolder>
	<description>Exemplo de app que faz sua atualização automática</description>
    	<copyright>Stefan Horochovec (stefan@horochovec.com)</copyright>
	<initialWindow>
		<title>AIRAutoUpdate</title>
		<content>AIRAutoUpdate.swf</content>
		<systemChrome>none</systemChrome>
		<transparent>true</transparent>
		<visible>true</visible>
		<minimizable>true</minimizable>
		<maximizable>false</maximizable>
		<resizable>false</resizable>
	</initialWindow>
</application>

AIRAutoUpdate-app.mxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
	xmlns:mx="http://www.adobe.com/2006/mxml"
	xmlns:app="br.com.horochovec.view.*"
	width="500" height="280"
	layout="absolute"
	paddingRight="0"
	paddingLeft="0"
	creationComplete="onCreationCompleteHandler(event);">
 
	<mx:Style>
	Application
	{
		background-color:"";
		background-image:"";
		padding: 0px;
	}
	</mx:Style>
 
	<mx:Script>
	<![CDATA[
 
		import air.update.events.UpdateEvent;
		import air.update.ApplicationUpdaterUI;
		import mx.controls.Alert;
		import mx.events.CloseEvent;
		import mx.events.FlexEvent;
 
		private var autoUpdater : ApplicationUpdaterUI = new ApplicationUpdaterUI();
 
		private function onCreationCompleteHandler(event:FlexEvent) : void
		{
			this.main.addEventListener( MouseEvent.MOUSE_DOWN, onMouseDownHandler );
			this.autoUpdateInit();
		}
 
 
		private function autoUpdateInit() : void
		{
			this.autoUpdater.updateURL = "http://www.horochovec.com.br/air/update-config.xml";
			this.autoUpdater.isCheckForUpdateVisible = false;
			this.autoUpdater.addEventListener(UpdateEvent.INITIALIZED, onUpdateApp);
			this.autoUpdater.addEventListener(ErrorEvent.ERROR, onErrorUpdate);
			this.autoUpdater.initialize();
		}
 
 
		private function onUpdateApp(event:UpdateEvent) : void
		{
			this.autoUpdater.checkNow();
		}
 
		private function onErrorUpdate(event:ErrorEvent) : void
		{
			Alert.show(event.toString());
		}
 
		private function onMouseDownHandler(event:MouseEvent) : void
		{
			stage.nativeWindow.startMove();
		}
	]]>
	</mx:Script>
 
	<app:Posts x="0" y="0" id="main"/>
 
</mx:Application>

Posts.mxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
	width="500"
	height="280"
	showCloseButton="true"
	creationComplete="creationCompleteHandler(event);"
	close="onCloseHandler(event);"
	title="Feeds Imasters - Seção Flex">
 
	<mx:Script>
	<![CDATA[
 
		import mx.events.ListEvent;
		import mx.collections.ArrayCollection;
		import flash.desktop.NativeApplication;
		import mx.events.CloseEvent;
		import mx.events.FlexEvent;
		import mx.rpc.events.ResultEvent;
 
		private var xmlList : XMLList;
 
		private function creationCompleteHandler(event:FlexEvent) : void
		{
			this.service.send();
		}
 
		private function serviceHandler(event:ResultEvent) : void
		{
			var result : XML = new XML(event.result);
			xmlList = result..item;
			this.grid.dataProvider = xmlList;
		}
 
		private function onCloseHandler(event:CloseEvent) : void
		{
			NativeApplication.nativeApplication.exit();
		}
 
		private function onClickGridHandler(event:MouseEvent) : void
		{
			this.btnLink.visible = true;
			this.autor.text = ((this.grid.selectedItem) as Object)['author'];
			this.descricao.text = ((this.grid.selectedItem) as Object)['description'];
		}
 
		private function onBtnLinkClickHandler(event:MouseEvent) : void
		{
			navigateToURL(new URLRequest(((this.grid.selectedItem) as Object)['guid'].toString()), "_blank");
		}
 
		private function onComboChangeHandler(event:ListEvent) : void
		{
			this.service.url = (this.combo.selectedItem as Object)['url'].toString();
			this.service.send();
		}
	]]>
	</mx:Script>
 
	<mx:HTTPService id="service" url="http://imasters.uol.com.br/feed/secao/flex" showBusyCursor="true" resultFormat="e4x" result="serviceHandler(event)"/>
 
	<mx:VBox bottom="5" left="5" right="5" top="5">
 
		<mx:ComboBox width="100%" labelField="label" id="combo" change="onComboChangeHandler(event);">
			<mx:ArrayCollection>
				<mx:Object label="Flex" url="http://imasters.uol.com.br/feed/secao/flex"/>
				<mx:Object label="PHP" url="http://imasters.uol.com.br/feed/secao/php"/>
			</mx:ArrayCollection>
		</mx:ComboBox>
 
		<mx:AdvancedDataGrid id="grid" designViewDataType="flat" bottom="5" left="5" right="5" top="5" height="50%" width="100%" click="onClickGridHandler(event);">
			<mx:columns>
				<mx:AdvancedDataGridColumn headerText="Titulo do artigo" dataField="title"/>
			</mx:columns>
		</mx:AdvancedDataGrid>
 
		<mx:Canvas height="50%" width="100%" borderColor="#DADADA" borderStyle="solid">
			<mx:Label x="6" y="6" id="autor" fontWeight="bold"/>
			<mx:TextArea height="53" id="descricao" bottom="6" left="6" right="6" borderStyle="none" selectable="false" mouseFocusEnabled="false" mouseEnabled="false"/>
			<mx:LinkButton x="383" y="4" label="Ler artigo" visible="false" id="btnLink" click="onBtnLinkClickHandler(event);"/>
		</mx:Canvas>
	</mx:VBox>
 
</mx:TitleWindow>

I hope it was clear the automatic update process in AIR.

About the Author: Stefan Horochovec

1 Comment + Add Comment

  • Good afternoon. I have a trouble with using property:
    layout=”absolute”
    Here is part of my code (flex 4)

    And i have an error:
    Инициализатор для “layout”: невозможно представить в тексте значение с типом spar.layout.supportClasses.LayoutBase

    Sorry for my bad English. What can it be?
    Thanks.

Leave a comment