The Space For App Developers

Beyond Plain Old HTML Objects

Archive for the ‘AIR’ tag

ADC tutorial: Updating Adobe AIR applications packaged with a native installer

without comments

My tutorial about updating AIR apps packaged with a native installer was published on Adobe Developer Connection – it is available here.

It goes step-by-step through how a custom updater can be built for Windows; it also explains how a similar process could be applied for the Mac OS X platform.

This tutorial is also a good starting point to understand how my NativeApplicationUpdater library works.

Written by Piotr Walczyszyn

March 1st, 2011 at 1:38 pm

Posted in Articles,Examples

Tagged with , ,

NativeApplicationUpdater 0.5.0 released

with 3 comments

Today I published a new version (0.5.0) of the NativeApplicationUpdater library to the Google Code project.

This is a minor, mostly bug-fix release:
  • Configuring proper XML settings when parsing plist in the HdiutilHelper class (based on contribution from a Erik Pettersson)
  • The update file is downloaded into a temporary folder with its name parsed out of the download url
  • If cmd.exe is found it is used to run the update file (based on the contribution from Jeff Pace)

You can also find out more about NativeApplicationUpdater here.

Written by Piotr Walczyszyn

February 21st, 2011 at 1:43 pm

Posted in Releases

Tagged with ,

Adobe InMarket experience

without comments

Today I published my Resicon application to Adobe InMarket. At the moment Adobe InMarket is just a front end for online application stores like Adobe Marketplace or Intel AppUp. It’s goal is to make distribution and monetization of AIR based applications as simple as possible. Adobe is also working to integrate it with other online application stores as well.

The process of publishing my application was really straightforward. The first thing I had to do was to sign up to the InMarket service with my Adobe ID. Next I applied for the free code signing certificate (this is a really great promotional offer from Adobe because you get a trusted certificate completely for free). To get myself validated I had to fill in a form and have it faxed to the US phone number with my signature on it and a scan of my personal ID. I know it can be tricky to find the fax machine these days, but hey it is worth the effort ;) .

After I got the certificate I was ready to do some additional steps on my application side. It only required me to copy and paste few lines of code that enable the LicenseManager API that comes with the InMarket SDK and its licensing.swc. In addition to the distribution and licensing, InMarket also provides the functionality for application updates. To enable it I only had to create a config folder with an updateConfig.xml file in my application directory structure. The updateConfig.xml is exactly the same file as you would use for standard AIR Update Framework.

Once all of the above steps were done I was ready to compile and sign my application. Next using InMarket portal I uploaded my *.air file and provided additional information like: an application description, screenshots, a and 495×250 px promotional image that will be displayed in the Adobe Marketplace.

After that done I was able to publish Resicon targeting both Adobe Markeplace and Intel AppUp stores with just a single click from a single place!

In my case the application is free but still I find it worth publishing it via InMarket since hopefully I will get wider distribution, place for hosting my application binaries and also nice reports with downloads stats etc. Those of you that will publish paid applications you can also see reports with numbers of: Trails, Purchases, Revenue, Activations and Trial to Purchase Histogram.

Really great resource with more in-depth information about InMarket is this wiki. You can also find links to the InMarket video tutorials on this page.

Written by Piotr Walczyszyn

January 20th, 2011 at 4:26 pm

Posted in Articles

Tagged with , ,

Screencast with Toaster Lite tour

without comments

Today I recorded a screencast of a Toaster Lite tour; it quickly demonstrates how the application works and how to use it.



Written by Piotr Walczyszyn

January 13th, 2011 at 4:42 pm

Posted in Recording

Tagged with , , , ,

Toaster Lite – HTTP/AMF monitoring tool

with 15 comments

My little pet project called Toaster Lite has finally reached the point at which I can share it with the world. In its current state it is just a simple HTTP/AMF monitoring tool that lets you introspect your client-server traffic when doing your Flash/Flex development.

You may wonder why I built it. Well first of all I thought that it would be a cool example of what you can do with the ServerSocket API that came with AIR 2. Secondly I wanted to get better understanding of AMF (Action Message Format) structure. And, last and not least I have further plans for it and what I would like to achieve at some stage is functionality similar to soapUI but for AMF. (BTW: did you know that soapUI has some basic support for AMF but obviously not enough for me ;) )

So what I’m planning for the next release is the ability to create test suites and test cases based on monitored requests and being able to replay these accordingly. At some point I would like to be able to perform load/stress testing of AMF services with it. I know this is something that would require threading support but maybe the Flash Player team will solve this somehow. If not, I may endup using Java to do this part of the job and integrate it with the UI using Flerry.

When building Toaster I managed to extract part of it into a separate as3 library called amf-message-deserializer, which can deserialize AMF requests received through the ServerSocket. The library project is available here in GitHub. I want to credit my fellow evangelist James Ward for his JSAMF project, which was a great help and a starting point. Other good resources to really understand AMF0 and AMF3 are their open specs available through this site.

To install Toaster Lite just use the badge below:


This movie requires Flash Player 10

UPDATED 2011.01.13 – recorded a screencast with Toaster Lite tour:



Written by Piotr Walczyszyn

January 12th, 2011 at 5:30 pm

Posted in Releases

Tagged with , ,

Resicon – icons batch resizing tool

with 9 comments

Last night when I was working on my pet project (very soon to be released ;) ) I finally got annoyed enough to take 15 minutes off to create this simple utility tool called Resicon. It is an icon batch resizing utility application. It allows you to resize icon images into a predefined set of sizes like: 16×16, 32×32, 36×36, 48×48, 72×72 and 128×128 or use a custom one.

This is a very handy tool because whenever you build an AIR application either a desktop or a mobile one you have to specify a set of its icons in different sizes. Usually this is done by downscaling a single high resolution image to the required sizes. This repetitive task is really annoying and that is why Resicon was brought to life.

Below is an install badge for the Resicon application, the source code is available in GitHub here.

This movie requires Flash Player 10

BTW: Resicon icon was created with the very cool Icon Generator Pro app.

Written by Piotr Walczyszyn

January 11th, 2011 at 5:12 pm

Posted in News,Releases

Tagged with ,

Paged list loaded from sqlite in Adobe AIR

with 2 comments

During my recent work on the Adobe Evangelists Blogroll application I wanted to implement a lazy-loaded/paginated List component with data coming from a local SQLite database. The reason for this was, of course, a memory usage consideration. Adobe Evangelists Blogroll is a mobile application so I didn’t want to load all available Post objects for each selected blog at once. I wanted it to be loaded dynamically as the user scrolls through the list. It turned out this wasn’t really difficult but there were few gotchas that I wanted to share:

  1. You have to implement your own IList component that throws ItemPendingError whenever the item requested with getItemAt function is not available yet. Unfortunately the Flex SDK doesn’t come with one built-in, but the good news is that you can use PagedArrayList class I created; its source code is available here :)
  2. Implement a createPendingItemFunction for the AsyncListView component. This function gets called when ItemPendingError is thrown and in our case serves two purposes. One is to trigger fetching the next page of rows from the database. It also returns a String with text that will be displayed temporarily in missing rows of the List. When that data gets fetched from the database those missing rows will be replaced with the loaded ones.
  3. One gotcha for the previous point is that if you are using SQLConnection in synchronous mode you will have to start fetching rows in the next frame after the call to the createPendingItemFunction function. That is why in my example I’m using callLater to execute the fetchRows function. This problem doesn’t arise with an asynchronous database connection.
  4. The last gotcha is most likely a bug in the Flex SDK. It occurs when the AsyncListView.list property is set before its list object is initialized and its length value is set. In that case invalidIndex error is thrown inside of LinearLayoutVector. The workaround I came up with is that right after setting the length property, PagedArrayList dispatches FlexEvent.INITIALIZE event; the application logic should handle it and programmatically set the AsyncListView.list property to the PagedArrayList instance. For reference I filed a bug in Flex SDK Jira.

Below you can find the source code that resolves the gotchas above. Also the whole Flash Builder project with paged list implementation is available here.

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
					   xmlns:s="library://ns.adobe.com/flex/spark" 
					   xmlns:mx="library://ns.adobe.com/flex/mx"
					   xmlns:helpers="com.riaspace.helpers.*"
					   preinitialize="windowedApplication_preinitializeHandler(event)"
					   creationComplete="windowedApplication_creationCompleteHandler(event)">
 
	<fx:Script>
		<![CDATA[
			import mx.collections.errors.ItemPendingError;
			import mx.events.FlexEvent;
 
			protected var conn:SQLConnection;
 
			protected const PAGE_SIZE:int = 50;
 
			protected function windowedApplication_preinitializeHandler(event:FlexEvent):void
			{
				// Creating inmemory database
				conn = new SQLConnection;
				conn.open();
 
				// Creating data_tab table
				var createStmt:SQLStatement = new SQLStatement;
				createStmt.sqlConnection = conn;
				createStmt.text = "CREATE TABLE data_tab (value_col TEXT)";
				createStmt.execute();
 
				// Inserting 1000 records to the table
				var insertStmt:SQLStatement = new SQLStatement;
				insertStmt.sqlConnection = conn;
				insertStmt.text = "INSERT INTO data_tab VALUES (?)";
 
				for (var i:int = 1; i <= 1000; i++)
				{
					insertStmt.clearParameters();
					insertStmt.parameters[0] = "value " + i;
					insertStmt.execute();
				}
			}
 
			protected function windowedApplication_creationCompleteHandler(event:FlexEvent):void
			{
				// STEP 1 - query rows count
				var stmt:SQLStatement = new SQLStatement;
				stmt.sqlConnection = conn;
				stmt.text = "SELECT count(*) as rowsCount FROM data_tab";
				stmt.execute();
 
				var result:SQLResult = stmt.getResult();
 
				// STEP 2 - set PagedArrayList.length equal queried rows count 
				pagedArrayList.length = result.data[0].rowsCount;
 
				// STEP 3 - fetch actual data starting from 0 offset (1st row)
				fetchRows(0);
			}
 
			protected function fetchRows(offset:int):void
			{
				// Fetch data rows with specified limit which is our requested page size
				// and offset passed as parameter
				var stmt:SQLStatement = new SQLStatement;
				stmt.sqlConnection = conn;
				stmt.text = "SELECT * FROM data_tab LIMIT :limit OFFSET :offset";
				stmt.parameters[":limit"] = PAGE_SIZE;
				stmt.parameters[":offset"] = offset;
				stmt.execute();
 
				var result:SQLResult = stmt.getResult();
				if (result && result.data)
				{
					for(var i:int = 0; i < result.data.length; i++)
					{
						// Setting return row at offset + i position
						pagedArrayList.setItemAt(result.data[i], offset + i);
					}
				}
			}
 
			private function createPendingItemFunctionHandler(index:int, ipe:ItemPendingError):Object
			{
				// In case synchronous database mode is used fetchRows function should be called
				// after return from this function. With asynchronous mode fetchRows function can
				// be called directly.
				callLater(fetchRows, [index]);
 
				// Returning a message to display
				return "List items are being fetched from database...";
			}
 
			protected function pagedArrayList_initializeHandler(event:FlexEvent):void
			{
				// NOTICE: this is a workaround for a Flex bug that causes invalidIndex error
				// in LinearLayoutVector. It all works well when asyncListView.list property
				// is set after pagedArrayList is initialized and its length property is set.
				asyncListView.list = pagedArrayList;
			}
 
		]]>
	</fx:Script>
 
	<fx:Declarations>
		<helpers:PagedArrayList id="pagedArrayList" initialize="pagedArrayList_initializeHandler(event)" />
	</fx:Declarations>
 
	<s:List width="100%" height="100%" labelField="value_col">
		<s:AsyncListView id="asyncListView" createPendingItemFunction="createPendingItemFunctionHandler" />
	</s:List>
 
</s:WindowedApplication>

Written by Piotr Walczyszyn

December 24th, 2010 at 11:21 am

Posted in Examples

Tagged with , ,

Adobe Evangelists Blogroll preview

with 10 comments

Today I’m officially announcing my new mobile application called Adobe Evangelists Blogroll. This is a simple RSS reader type of application that aggregates blog feeds from my fellow Adobe Evangelists. I’ve developed it with a preview release of Flex Hero SDK and it works on all Android devices that can handle AIR runtime. The app is available in Android Market if you look for “Adobe Evangelists Blogroll“.

What I’m also really excited about is that I managed to repackage it into a bar file and run it on BlackBerry PlayBook emulator without doing even single change to the codebase. In the video below you can see it in action running on different devices and also on PlayBook emulator.

As I already mentioned it was built with preview release of Flex SDK and also the app itself is in beta so it may have some quirks here and there. If you have any suggestions, comments or found some bugs you can contact me through this form.

Few screens of the application (running in landscape & portrait orientation and on playbook):

Written by Piotr Walczyszyn

December 20th, 2010 at 2:55 pm

Posted in News,Releases

Tagged with , , , ,

Building NativeApplicationUpdater custom UI

with 16 comments

After I published the NativeApplicationUpdater library I received a lot of questions how to display a UI showing download and install progress. Unfortunately NAU doesn’t come with a built-in UI yet ;) So I decided to put together this short blog post to explain how you can build it yourself. Below you can see how the demo app with the custom UI looks (project source code is available here in SVN):




Below is snippet of code that you can examine to better understand how to build your own updater UI:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
					   xmlns:s="library://ns.adobe.com/flex/spark" 
					   xmlns:mx="library://ns.adobe.com/flex/mx"
					   xmlns:updater="http://updater.riaspace.com/">
 
	<fx:Script>
		<![CDATA[
			import air.update.events.DownloadErrorEvent;
			import air.update.events.StatusUpdateEvent;
			import air.update.events.UpdateEvent;
 
			import mx.controls.Alert;
 
			[Bindable]
			protected var downlaoding:Boolean = false;
 
			protected function isNewerFunction(currentVersion:String, updateVersion:String):Boolean
			{
				// Example of custom isNewerFunction function, it can be omitted if one doesn't want
				// to implement it's own version comparison logic. Be default it does simple string
				// comparison.
				return true;
			}
 
			protected function updater_errorHandler(event:ErrorEvent):void
			{
				Alert.show(event.text);
			}
 
			protected function btnCheckNow_clickHandler(event:MouseEvent):void
			{
				// First initialize NativeApplicationUpdater
				updater.initialize();
			}
 
			protected function updater_initializedHandler(event:UpdateEvent):void
			{
				// When NativeApplicationUpdater is initialized you can call checkNow function
				updater.checkNow();
			}
 
			protected function updater_updateStatusHandler(event:StatusUpdateEvent):void
			{
				if (event.available)
				{
					// In case update is available prevent default behavior of checkNow() function 
					// and switch to the view that gives the user ability to decide if he wants to
					// install new version of the application.
					event.preventDefault();
					currentState = "updaterView";
				}
				else
				{
					Alert.show("Your application is up to date!");
				}
			}
 
			protected function btnYes_clickHandler(event:MouseEvent):void
			{
 
				// In case user wants to download and install update display download progress bar
				// and invoke downloadUpdate() function.
				downlaoding = true;
				updater.addEventListener(DownloadErrorEvent.DOWNLOAD_ERROR, updater_downloadErrorHandler);
				updater.addEventListener(UpdateEvent.DOWNLOAD_COMPLETE, updater_downloadCompleteHandler);
				updater.downloadUpdate();
			}
 
			protected function btnNo_clickHandler(event:MouseEvent):void
			{
				currentState = "mainView";
			}
 
			private function updater_downloadCompleteHandler(event:UpdateEvent):void
			{
				// When update is downloaded install it.
				updater.installUpdate();
			}
 
			private function updater_downloadErrorHandler(event:DownloadErrorEvent):void
			{
				Alert.show("Error downloading update file, try again later.");
			}
 
		]]>
	</fx:Script>
 
	<s:states>
		<s:State name="mainView"/>
		<s:State name="updaterView"/>
	</s:states>
 
	<fx:Declarations>
		<updater:NativeApplicationUpdater id="updater" 
					updateURL="http://riaspace.com/native-application-updater/update-1.1.xml" 
					isNewerVersionFunction="{isNewerFunction}"
 
					initialized="updater_initializedHandler(event)"
					updateStatus="updater_updateStatusHandler(event)"
 
					error="updater_errorHandler(event)"
					downloadError="updater_errorHandler(event)"
					updateError="updater_errorHandler(event)"
					/>
	</fx:Declarations>
 
	<s:Button id="btnCheckNow" label="Check for updates" includeIn="mainView" 
			  horizontalCenter="0" verticalCenter="0" 
			  click="btnCheckNow_clickHandler(event)"/>
 
	<s:HGroup verticalCenter="0" includeIn="updaterView" horizontalCenter="0" verticalAlign="top">
		<s:BitmapImage source="@Embed(source='/assets/icon128.png')" />
		<s:VGroup width="100%" horizontalAlign="center">
			<s:Label text="New version ({updater.updateVersion}) is available." />
			<s:Label text="Do you want to download it and install?" />
			<mx:ProgressBar id="prgBar" source="{updater}" label="Downloading %3%" visible="{downlaoding}" />
			<s:HGroup>
				<s:Button id="btnYes" label="Yes" click="btnYes_clickHandler(event)" />
				<s:Button id="btnNo" label="No" click="btnNo_clickHandler(event)" />
			</s:HGroup>
		</s:VGroup>
	</s:HGroup>
 
</s:WindowedApplication>

Written by Piotr Walczyszyn

October 15th, 2010 at 3:04 pm

Posted in Examples,Releases

Tagged with ,

NativeApplicationUpdater version 0.4 released!

without comments

I just posted a new version (0.4) of the NativeApplicationUpdater library. It fixes some minor bugs and removes a workaround for the different URLStream events sequence between Win OS and other OSes.

Stay tuned for my next post that explains how to build NAU custom UI.

Written by Piotr Walczyszyn

October 15th, 2010 at 3:01 pm

Posted in Releases

Tagged with , ,