Creating first app for Android using Adobe Air

To illustrate the workflow of developing Android apps in Adobe Air I created a very simple application that can react to screen orientation changes, gestures and touches – it’s written in ActionScript.

The user interface consists of checkbox, label and a button.
If the user clicks the button or make a swipe gesture the background color will change to a random color.
Checkbox enables the screen orientation – this is an interesting feature since we can decide whether or not to let the screen rotate.

I used a set of Android-like components created by Kevin Hoyt, Adobe’s Platform Evengelist. They are very easy to use and mimic the Android look & feel pretty good.

Hello Android Air screen

The process is made of following steps

  • create Flex Project
  • generate the certificate
  • package the Air application into an apk file
  • install the apk to device / emulator

I assume that you have all the necessary dependencies already installed (Flex SDK with AIR 2.5 , Android SDK and an emulator /device), if not then check my previous post.

Creating Flex Builder project

1. Create a new Flex Project. Be sure to use Flex SDK merged with AIR 2.5 SDK.

Adobe Air app wizard - Flash Builder step 1

In the second step change the main application file extention from ‘.mxml’ to “.as”, since we won’t be using Flex framework, only ActionScript 3.

Using Flex for mobile applications is possible but the performance would suffer. Until mobile version of Flex 4.5 isn’t released (should be available later this year) it’s better to stick to ActionScript only.

Adobe Air app wizard - Flash Builder step 2

Modify application’s descriptor xml

Flash Builder has generated all the necessary files, there are only few thing to modify in the xml descriptor  - set the <visible> tag to “true” and specify Android manifest – if your application is going to use Android APIs (camera, wifi, file access, etc.). If you don’t specify the functionality you need, the application won’t work properly.

The application main class looks like this:

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package
{
	import com.greensock.TweenMax;
	import com.greensock.easing.Cubic;
 
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
 
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.StageOrientationEvent;
	import flash.events.TransformGestureEvent;
 
	import flash.geom.Point;
 
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
 
 
 
	/**
	 * Adobe AIR Android application example
	 *
	 * @author Piotr Koscierzynski, flashsimulations.com
	 *
	 * */
	[SWF(backgroundColor='0xf8f8f8',frameRate='31')]
	public class HelloAndroid extends Sprite
	{
		/* Android style components by Kevin Hoyt */
		private var superButton:Button;
		private var autoOrientCheckbox:CheckBox;
		private var footerLabel:Label;
 
		//
		private var uiCreated:Boolean = false;
 
		private var labels:Array = ['Click me!', "Welcome to Android world!", 'Life is like a box of chocolates.', 'Have a nice day.'];
		private var counter:uint = 0;
 
		private static const TWEEN_TIME:Number = 0.6;
		private var tweenFunction:Function = com.greensock.easing.Cubic.easeInOut;
 
		private var backgroundSprite:Sprite;
 
 
		public function HelloAndroid()
		{
			addEventListener(Event.ADDED_TO_STAGE, onAdded, false, 0, true);
		}
 
 
		private final function onAdded(e:Event):void {
 
			removeEventListener(Event.ADDED_TO_STAGE, onAdded);
 
			init();
		}
 
		/**
		 * Stage resize handler.
		 * */
		private final function onResize(e:Event = null):void {
 
			//no stage instance available
			if (!stage) return;
 
			//ui not instantiated
			if (!uiCreated) return;
 
			var stageWidth:uint = stage.stageWidth;
			var stageHeight:uint = stage.stageHeight;
 
			//background
			backgroundSprite.width = stageWidth;
			backgroundSprite.height = stageHeight;
			backgroundSprite.x = 0;
			backgroundSprite.y = 0;
 
 
 
			//checkbox position
			var autoOrientCheckbox_x:int = autoOrientCheckbox.width - 30;
			var autoOrientCheckbox_y:int =  10;
 
			TweenMax.killTweensOf(autoOrientCheckbox);
			TweenMax.to(autoOrientCheckbox, TWEEN_TIME, {x:autoOrientCheckbox_x, y: autoOrientCheckbox_y, ease:tweenFunction});
 
			//center the button
			superButton.setWidth(stageWidth - 20);
			var superButton_x:int = (stageWidth - superButton.width) >> 1;
			var superButton_y:int = (stageHeight - superButton.height) >> 1;
 
			TweenMax.killTweensOf(superButton);
			TweenMax.to(superButton, TWEEN_TIME, {x:superButton_x, y: superButton_y, ease:tweenFunction});
 
			var footerLabel_x:int = (stageWidth - footerLabel.width) >> 1;
			var footerLabel_y:int = (stageHeight - footerLabel.height - 10);
 
			TweenMax.killTweensOf(footerLabel);
			TweenMax.to(footerLabel, TWEEN_TIME, {x:footerLabel_x, y: footerLabel_y, ease:tweenFunction});
 
		}
 
		/**
		 * If the <autoOrients>true</autoOrients> tag is set to true then the application will receive this event.
		 * We can block the screen rotation by calling e.preventDefault().
		 *
		 * */
		private final function onOrientationChanging(e:StageOrientationEvent):void {
 
			//prevent the screen from rotating
			if (autoOrientCheckbox.selected == false)
			{
				e.preventDefault();
			}
		}
 
		/**
		 * Init stage align and listeners.
		 *
		 * */
		private final function init():void {
 
			buildUI();
 
			if (stage) {
 
				stage.align = StageAlign.TOP_LEFT;
				stage.scaleMode = StageScaleMode.NO_SCALE;
				stage.addEventListener(Event.RESIZE, onResize, false, 0, true);
				stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGING, onOrientationChanging, false, 0, true);
				stage.addEventListener(TransformGestureEvent.GESTURE_SWIPE, onSwipe, false, 0, true);
 
			}
 
 
			//set UI elements positions
			onResize();
 
		}
 
 
		/**
		 * Handle screen swipe - change the background of the screen.
		 *
		 * */
		private final function onSwipe(e:TransformGestureEvent):void {
 
 
				changeBgColor();
 
		}
 
		/**
		 * Build the user interface
		 * */
		private final function buildUI():void {
 
			//background
			backgroundSprite = new Sprite();
			backgroundSprite.graphics.beginFill(0xFFFFFF, 1);
			backgroundSprite.graphics.drawRect(0,0, 1, 1);
			backgroundSprite.graphics.endFill();
			backgroundSprite.mouseChildren = false;
			backgroundSprite.mouseEnabled = false;
			addChildAt(backgroundSprite, 0);
 
			//button
			superButton = new Button(labels[0], 300);
			superButton.addEventListener(MouseEvent.CLICK, handleButtonClick, false, 0, true);
			addChild(superButton);
 
 
			//checkbox - auto orientation enabled
			autoOrientCheckbox = new CheckBox('Auto orient screen');
			autoOrientCheckbox.selected = false;
			addChild(autoOrientCheckbox);
 
			//label for footer
			footerLabel = new Label('flashsimulations.com');
			footerLabel.addEventListener(MouseEvent.CLICK, handleFooterClick, false, 0, true);
			footerLabel.buttonMode = true;
			footerLabel.mouseChildren = false;
			addChild(footerLabel);
 
			uiCreated = true;
		}
 
		private function changeBgColor():void {
 
 
			TweenMax.to(backgroundSprite, 0.4, {tint:Math.random()*0xFFFFFF});
		}
 
		private final function handleButtonClick(e:Event):void {
			superButton.label = labels[(++counter)%labels.length];
			changeBgColor();
 
		}
 
		private final function handleFooterClick(e:Event):void {
			navigateToURL(new URLRequest('http://flashsimulations.com'));
		}
 
 
	}
}
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
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<application xmlns="http://ns.adobe.com/air/application/2.5">
	<id>com.flashsimulations.air.HelloAndroid</id>
	<filename>HelloAndroid</filename>
	<name>Hello AndroAir</name>
	<versionNumber>1.0.0</versionNumber>
	<description>This application demonstrates the new Flash API for mobile devices (gestures, screen orientation). Created using Adobe AIR 2.5.</description>
	<copyright>Piotr Koscierzynski, flashsimulations.com</copyright>
	<supportedProfiles>mobileDevice</supportedProfiles>
 
	<!-- Settings for the application's initial window. Required. -->
	<initialWindow>
		<content>[This value will be overwritten by Flash Builder in the output app.xml]</content>
		<visible>true</visible>
		<autoOrients>true</autoOrients>
	</initialWindow>
 
	<android>
			<manifestAdditions>
			<![CDATA[ <manifest>
								<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
								<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
								<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
								<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
								<uses-permission android:name="android.permission.CAMERA" />
								<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
								<uses-permission android:name="android.permission.INTERNET" />
								<uses-permission android:name="android.permission.READ_PHONE_STATE" />
								<uses-permission android:name="android.permission.RECORD_AUDIO" />
								<uses-permission android:name="android.permission.WAKE_LOCK" />
								<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
			</manifest>
			]]>
			</manifestAdditions>
	</android>
</application>

2. Create an apk file and deploy it to the device / emulator.

Packaging Adobe Air for Android

Copy ‘HelloAndroid.swf’ and ‘HelloAndroid-app.xml’ into FLEX_SDK\bin directory, then run these commands

cd /Users/piotr/SDK/flex_sdk_4.1_air_2.5/bin
./adt -certificate -cn flashsimulations -ou piotrkoscierzynski -c PL -validityPeriod 25 2048-RSA android_cert.p12 SecretPassword
./adt -package -target apk-emulator -storetype pkcs12 -keystore android_cert.p12  -storepass SecretPassword HelloAndroid-emu.apk HelloAndroid-app.xml HelloAndroid.swf
cp HelloAndroid-emu.apk /Users/piotr/SDK/Android_SDK/tools
cd /Users/piotr/SDK/Android_SDK/tools
./adb devices
./adb -e install -r HelloAndroid-emu.apk

The application looks like this

Sources: Hello Android Air Flash Builder project

5 thoughts on “Creating first app for Android using Adobe Air

  1. Hi, I’m just starting to port AIR apps onto Android. It’s all fine apart from the fact that I can’t seem to access TransformGestureEvents in the emulator. I’m tracing out on screen whether Multitouch.supportsGestureEvents and it’s always coming back as false.
    Did you have any problems like this, and if so how did you get round it?
    Great post by the way, great to help people out!

    • The emulator has many limitations and some part of mobile API will work only on the device ( this includes gesture events, etc.). Please refer to mobile AIR (Developer’s Guide and Best Practices) release notes for details.