Tips for building AIR for Android Mobile Apps

There were a lot of learnings in building CrossTweet for Android. Here are a few tips I’d like to share with you.

Adobe AIR for Android

Maintaining a Single Unified Codebase

With the help of conditional compilation, I’ve managed to keep a single unified codebase with separate ANT deployments. One example of where conditional compilation came in useful was when providing functionality to close the mobile app, something you wouldn’t do with the web app running in Flash Player.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Stop the compiler choking on NativeApplication when compiling for web using playerglobal.swc
CONFIG::ANDROID
import flash.desktop.NativeApplication;

private function close( event:ErrorViewEvent ):void
{
    CONFIG::ANDROID
    {
        // Only do this if compiling using AIR for Android using (airglobal.swc)
        NativeApplication.nativeApplication.exit();
    }
}

// Don't forget to set the value of CONFIG::ANDROID when compiling, see the ANT build.xml

Another example was when forcing the mobile app to automatically go fullscreen on startup, something which would produce errors if attempted within Flash Player.

1
if(CONFIG::ANDROID) stage.displayState = StageDisplayState.FULL_SCREEN;

Of course, keeping a entirely unified codebase can have it’s advantages, but may not be the best approach for every web & mobile app pairing. On a larger project, I’d probably lean towards unified models and controllers, but with separate views, something easily achieved with the dependency injection that a framework like Robotlegs provides.



Application Manifest

If you’re familiar with building desktop AIR applications, you will have created an AIR application manifest before. AIR for Android also requires a very similar manifest file, but with the addition of config which will be inserted into the native Android application manifest (native Android apps have their own manifest too and when using AIR for Android, they are effectively combined for convenience). It is here that you will need to request the specific permissions your app will require.

1
2
<!-- CrossTweet only requires the internet access permission -->
<uses-permission android:name="android.permission.INTERNET"/>

Be wary of setting the “uses-configuration” elements, they are entirely necessary if you are targeting your app at a specific type of device running Android, but if you have one set accidentally (like I originally did) your app will simply not appear in the Market via an unsupported device. It goes without saying, that I would like to see an option on Android Market to show unsupported apps with accompanying warning messages as to exactly why they’re not supported on your device!

1
2
<!-- I didn't realise this basically meant tracker ball, I originally thought a touchscreen could be classed as a five way nav! With this set, my app wouldn't appear in the Market on a Desire HD, where it would appear on a standard Desire - doh! -->
<uses-configuration android:reqFiveWayNav="true" />

By way of example, here is the full “CrossTweet-app.xml” file with additions for Android.

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
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<application xmlns="http://ns.adobe.com/air/application/2.5" minimumPatchLevel="0">
<!-- AIR Application Descriptor File. See http://www.adobe.com/go/learn_air_1.0_application_descriptor_en. -->
    <id>com.bluebarracuda.CrossTweet</id>
    <name>CrossTweet</name>
        <!-- A string value of the format <0-999>.<0-999>.<0-999> that represents application version which can be used to check for application upgrade.
            Values can also be 1-part or 2-part. It is not necessary to have a 3-part value.
            An updated version of application must have a versionNumber value higher than the previous version. Required for namespace >= 2.5 . -->
        <versionNumber>0.2.2</versionNumber>

        <!-- A string value (such as "v1", "2.5", or "Alpha 1") that represents the version of the application, as it should be shown to users. Optional. -->
        <versionLabel>v0.2.2 Beta</versionLabel>
    <filename>CrossTweet</filename>
    <description>
        <text xml:lang="en">What is CrossTweet?
 
The large horizontal words running across the middle make up a tweet, recently sent out into the digital ether by Blue Barracuda or anyone else enjoying a life #LivingDigital.
The vertical tweets are from people across the globe, simply tweeting about their everyday life.
 
The moment the two intersect is where we live. That's the place where #LivingDigital becomes core to our outlook on life as a whole.</text>
    </description>
    <copyright>Copyright 2010, Blue Barracuda</copyright>
    <supportedProfiles>mobileDevice</supportedProfiles>
    <initialWindow>
        <content>CrossTweet.swf</content>
        <title>CrossTweet</title>
        <systemChrome>standard</systemChrome>
        <transparent>false</transparent>
        <visible>true</visible>
        <minimizable>true</minimizable>
        <maximizable>true</maximizable>
        <resizable>true</resizable>
        <width>800</width>
        <height>480</height>
        <x>150</x>
        <y>150</y>
        <minSize>800 480</minSize>
        <maxSize>800 480</maxSize>
        <autoOrients>true</autoOrients>
        <renderMode>cpu</renderMode>
    </initialWindow>
    <installFolder>Blue Barracuda/CrossTweet</installFolder>
    <programMenuFolder>Blue Barracuda/CrossTweet</programMenuFolder>
   
    <customUpdateUI>false</customUpdateUI>
    <allowBrowserInvocation>false</allowBrowserInvocation>

    <icon>
        <image36x36>icons/icon36.png</image36x36>
        <image48x48>icons/icon48.png</image48x48>
        <image72x72>icons/icon72.png</image72x72>
    </icon>

    <android>
    <manifestAdditions>
          <![CDATA[
           <manifest android:installLocation="auto">
               <!-- Added for Internet and debugging support -->
               <uses-permission android:name="android.permission.INTERNET"/>
               <supports-screens android:normalScreens="true"/>
               <uses-feature android:required="true" android:name="android.hardware.touchscreen.multitouch"/>
               <application android:enabled="true">
                   <activity android:excludeFromRecents="false">
                       <intent-filter>
                       <action android:name="android.intent.action.MAIN"/>
                       <category android:name="android.intent.category.LAUNCHER"/>
                       </intent-filter>
                   </activity>
               </application>
           </manifest>
         ]]>
      </manifestAdditions>
    </android>
   
   
</application>



ANT Buildfile

The central hub of the entire project has to be the ANT buildfile (build.xml), it neatly brings all deployments together and will be your best friend whilst testing/deploying/testing/etc.

This ANT buildfile will compile a SWF for the web as well as a SWF and APK for Android. It will create a self-signed certificate and if you have your device correctly connected to your computer, there are also tasks that will install, launch and even uninstall the app onto/from your device – c’mon, what more could you ask for?!! Don’t forget you’ll require the Flex SDK 4.1 and the latest Android 2.2 SDK at your disposal.

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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
<?xml version="1.0" encoding="UTF-8"?>

<!--
************************************************************

    ANT buildfile for unified codebase AIR for Android
    and Flash Player deployments.
   
    Author:         Philip Bulley
    Last updated:   4th November 2010
   
************************************************************
-->

<project default="1) Compile SWF Web" name="CrossTweet">

    <!-- Path to Flex and Android SDKs -->
    <property name="sdk_dir"            value="C:\Program Files\flex_sdk_4.1.0.16076" />
    <property name="android_sdk_dir"    value="C:\Program Files\android-sdk-windows" />
    <property name="mxmlc"              value="${sdk_dir}/lib/mxmlc.jar" />
    <property name="adl"                value="${sdk_dir}/bin/adl.exe" />
    <property name="adt"                value="${sdk_dir}/lib/adt.jar" />
    <property name="adb"                value="${android_sdk_dir}/tools/adb.exe" />
    <property name="sdk_lib_dir"        value="${sdk_dir}\frameworks\libs" />

    <!-- Project properties -->
    <property name="app_name"           value="CrossTweet" />
    <property name="app_id"             value="com.yourcompany.CrossTweet" />       <!-- must match the id in the application descriptor -->
    <property name="app_root_dir"       value="." />
    <property name="assets_dir_name"    value="asset" />
   
    <!-- Certificate properties -->
    <property name="cert_name"          value="CrossTweetCert" />
    <property name="cert_pw"            value="INSERT_PASSWORD_HERE" />
    <property name="org_name"           value="Blue Barracuda" />
    <property name="org_unit"           value="Creative" />
    <property name="country"            value="GB" />
    <property name="key_type"           value="2048-RSA" />
    <property name="cert_validity"      value="25" />
    <property name="cert_path"          location="${app_root_dir}/${assets_dir_name}/certificate" />
    <property name="cert_file"          value="${cert_path}/${cert_name}.p12" />
   
    <!-- Project paths and filenames -->
    <property name="swf_filename"               value="${app_name}.swf" />
    <property name="android_filename"           value="${app_name}.apk" />
    <property name="app_descriptor_filename"    value="${app_name}-app.xml" />
    <property name="main_class"                 value="${app_root_dir}/src/Main.as" />
    <property name="bin_dir"                    location="${app_root_dir}/bin" />
    <property name="bin_debug_dir"              location="${app_root_dir}/bin" />           <!-- note using single bin + bin-debug dir, change if you like -->
    <property name="bin_android_dir"            location="${app_root_dir}/bin-android" />
    <property name="bin_debug_android_dir"      location="${app_root_dir}/bin-android" />   <!-- note using single bin + bin-debug dir, change if you like -->
    <property name="assets_dir"                 location="${app_root_dir}/${assets_dir_name}" />
    <property name="lib_dir"                    location="${app_root_dir}/lib" />
   

    <!-- Compile SWF to build-directory for packaging -->
    <target name="1) Compile SWF Web">
        <java jar="${mxmlc}" fork="true" failonerror="true">
            <arg value="-debug=false" />
            <arg value="+flexlib=${sdk_dir}/frameworks" />
            <arg value='-static-link-runtime-shared-libraries=true' />
            <arg value='-target-player=10.0' />
            <arg value="-file-specs=${main_class}" />
            <arg value="-output=${bin_dir}/${swf_filename}" />
            <arg value='-define+=CONFIG::ANDROID,false' />
            <arg value="-library-path=${lib_dir}/as3corelib.swc" />
            <arg value="-library-path=${lib_dir}/CasaLib_1.3.0.swc" />
            <arg value="-library-path=${lib_dir}/greensock.swc" />
            <arg value="-library-path=${lib_dir}/Library.swc" />
            <arg value="-library-path=${lib_dir}/MouseManager.swc" />
            <arg value="-library-path=${lib_dir}/robotlegs-framework-v1.1.2.swc" />
        </java>
    </target>
   
    <target name="1a) Compile SWF Android">
        <java jar="${mxmlc}" fork="true" failonerror="true">
            <arg value="-debug=false" />
            <arg value="+flexlib=${sdk_dir}/frameworks" />
            <arg value="+configname=air" />
            <arg value="-file-specs=${main_class}" />
            <arg value="-output=${bin_android_dir}/${swf_filename}" />
            <arg value='-define+=CONFIG::ANDROID,true' />
            <arg value="-library-path=${lib_dir}/as3corelib.swc" />
            <arg value="-library-path=${lib_dir}/CasaLib_1.3.0.swc" />
            <arg value="-library-path=${lib_dir}/greensock.swc" />
            <arg value="-library-path=${lib_dir}/Library.swc" />
            <arg value="-library-path=${lib_dir}/MouseManager.swc" />
            <arg value="-library-path=${lib_dir}/robotlegs-framework-v1.1.2.swc" />
        </java>
    </target>

    <!-- Compile SWF to debug directory and copy assets to it -->
    <target name="2) Compile SWF Web (Debug)">
        <java jar="${mxmlc}" fork="true" failonerror="true">
            <arg value="-debug=true" />
            <arg value="+flexlib=${sdk_dir}/frameworks" />
            <arg value='-static-link-runtime-shared-libraries=true' />
            <arg value='-target-player=10.0' />
            <arg value="-file-specs=${main_class}" />
            <arg value="-output=${bin_debug_dir}/${swf_filename}" />
            <arg value='-define+=CONFIG::ANDROID,false' />
            <arg value="-library-path=${lib_dir}/as3corelib.swc" />
            <arg value="-library-path=${lib_dir}/CasaLib_1.3.0.swc" />
            <arg value="-library-path=${lib_dir}/greensock.swc" />
            <arg value="-library-path=${lib_dir}/Library.swc" />
            <arg value="-library-path=${lib_dir}/MouseManager.swc" />
            <arg value="-library-path+=${lib_dir}/robotlegs-framework-v1.1.2.swc" />
        </java>
    </target>
   
    <target name="2a) Compile SWF Android (Debug)">
        <java jar="${mxmlc}" fork="true" failonerror="true">
            <arg value="-debug=true" />
            <arg value="+flexlib=${sdk_dir}/frameworks" />
            <arg value="+configname=air" />
            <arg value="-file-specs=${main_class}" />
            <arg value="-output=${bin_debug_android_dir}/${swf_filename}" />
            <arg value='-define+=CONFIG::ANDROID,true' />
            <arg value="-library-path=${lib_dir}/as3corelib.swc" />
            <arg value="-library-path=${lib_dir}/CasaLib_1.3.0.swc" />
            <arg value="-library-path=${lib_dir}/greensock.swc" />
            <arg value="-library-path=${lib_dir}/Library.swc" />
            <arg value="-library-path=${lib_dir}/MouseManager.swc" />
            <arg value="-library-path+=${lib_dir}/robotlegs-framework-v1.1.2.swc" />
        </java>
    </target>
   
   

    <!-- Show application without packaging -->
    <target name="3) Test Android App" depends="2a) Compile SWF Android (Debug)">
        <exec executable="${adl}">
            <arg value="${bin_debug_android_dir}/${app_descriptor_filename}" />
            <arg value="${bin_debug_android_dir}" />
        </exec>
    </target>


    <!-- Packaging the application to an air-file & save it in the publish directory -->
    <target name="4) Package Android App" depends="1a) Compile SWF Android">
        <java jar="${adt}" fork="true" failonerror="true">
            <arg value="-package" />
            <arg value="-target" />
            <arg value="apk" />
            <arg value="-storetype" />
            <arg value="pkcs12" />
            <arg value="-keystore" />
            <arg value="${cert_file}" />
            <arg value="-storepass" />
            <arg value="${cert_pw}" />

            <arg value="${bin_android_dir}/${android_filename}" />
            <arg value="${bin_android_dir}/${app_descriptor_filename}" />
            <arg value="-C" />
            <arg value="${bin_android_dir}" />
            <arg value="${swf_filename}" />

            <arg value="${bin_android_dir}/icons" />
        </java>
    </target>
   
    <target name="4a) Install Android App on Device" depends="4) Package Android App">
        <java jar="${adt}" fork="true" failonerror="true">
            <arg value="-installApp" />
            <arg value="-platform" />
            <arg value="android" />
            <arg value="-platformsdk" />
            <arg value="${android_sdk_dir}" />
            <arg value="-package" />
            <arg value="${bin_android_dir}/${android_filename}" />
        </java>
    </target>
   
    <target name="4b) Launch Android App on Device">
            <java jar="${adt}" fork="true" failonerror="true">
                <arg value="-launchApp" />
                <arg value="-platform" />
                <arg value="android" />
                <arg value="-platformsdk" />
                <arg value="${android_sdk_dir}" />
                <arg value="-appid" />
                <arg value="${app_id}" />
            </java>
        </target>
   
    <target name="4c) Uninstall Android App from Device">
        <java jar="${adt}" fork="true" failonerror="true">
            <arg value="-uninstallApp" />
            <arg value="-platform" />
            <arg value="android" />
            <arg value="-platformsdk" />
            <arg value="${android_sdk_dir}" />
            <arg value="-appid" />
            <arg value="${app_id}" />
        </java>
    </target>

    <!-- Creating a digital ID certificate -->
    <target name="5) Create New Certificate">
        <java jar="${adt}" fork="true">
            <arg value="-certificate" />
            <arg value="-cn" />
            <arg value="${cert_name}" />
            <arg value="-ou" />
            <arg value="${org_unit}" />
            <arg value="-o" />
            <arg value="${org_name}" />
            <arg value="-validityPeriod" />
            <arg value="${cert_validity}" />
           
            <arg value="-c" />
            <arg value="${country}" />
            <arg value="${key_type}" />
            <arg value="${cert_file}" />
            <arg value="${cert_pw}" />
        </java>
    </target>
   
    <target name="6) List devices attached">
        <exec executable="${adb}">
            <arg value="devices" />
        </exec>
        <echo message="echo test ${org_name}" />
    </target>
       
</project>

Another very useful reference is the Building ADOBE® AIR® Applications PDF guide. It will give you more insight on how you can configure your application, as well as recommended icon sizes, and much more.

Good luck, and if you make an AIR for Android app, I’d love to know about it.

Tags: , ,

2 Responses to “Tips for building AIR for Android Mobile Apps”

  1. [...] insight and tips on how this was built, see my Tips for building AIR for Android Mobile Apps [...]

  2. Very helpful, answered a lot of questions I had about ant build files. I ended up using your example to make my own build file for building an air app with custom java classes so that I could do task bar notifications etc… through the android os.

    It’s in the market if you have any interest, air.com.justinbuser.BuzeyTunes

Leave a Reply