Automating, and customizing, your Android applications

This post is more than 2 years old.

A few days ago I blogged about the creation of a simple RSS reader for the Android platform. It certainly wasn't complex, but it was good practice to get my feet wet with the Hero SDK and mobile Flex development. I'm a big fan of trying small, simple applications as a way to get experience with a platform. When I built this application though, I have to admit I had something a bit more interesting in mind. Today I'll show you what I built. I think it's pretty cool and it is a testament to the power of the tools Adobe has provided.

Whenever I present on Adobe AIR, I always take pains to mention that the SDK is 100% free. You don't have to buy any tools at all to work with AIR. Certainly Flash Builder is a damn fine tool (if you are doing Flex-based AIR development) and I recommend it, but if you don't have the money, or don't like the editor, you can use your own editor and compile both Flex and your AIR applications by hand. There are a set of command line tools that cover the entire process. To make things even easier, you can automate all of this with ANT. Terry Ryan just did a great blog post on this: Using ANT to package the same AIR app to Multiple Devices. In his post, Terry describes not only compiling the Flex application but also building the installers for both Android and the Playbook. This works great. But did you know that one of the coolest features in ANT is the ability to do replacements in text files? I didn't know this till I saw the ANT file for Model-Glue. If you combine the fact that we can make replacements in our source code with the ability to build/compile the application via ANT, you can provide a "Create your own RSS Android App" application via the web. Consider the following walkthrough:

User hits a form on your site:

They enter a site name, a URL, and a RSS URL and then hit submit. Next they see:

At this point, the APK could be emailed to them (on an Android phone you can install applications sent via email), it could be stored on the file system for download later, or any other number of possibilities. The point is - they now have a 100% customized Android application delivered completely dynamically via ColdFusion and the AIR/Flex command line tools.

Here is the code I used for the ColdFusion side. This is a proof of concept. It will fail horribly if run by multiple users. That's fixable, but beyond the scope of this blog entry.

<cfif structKeyExists(form, "submit")>

&lt;cfset pathToAnt = "C:\apache-ant-1.8.1\bin\ant.bat"&gt;
&lt;cfset pathToBuild = """C:/Users/Raymond/Documents/My Dropbox/projects/SimpleRSS/build.xml"""&gt;

&lt;cfexecute name="#pathToAnt#" arguments="-buildfile #pathToBuild# -verbose -Durl=#form.companyurl# -Drssurl=#form.rssurl#" variable="result" errorvariable="error" timeout="60" /&gt;

&lt;cfif not len(error)&gt;
	Your APK has been created.
&lt;!--- no error handling yet - too bad ---&gt;		


<h2>Make me an Android App</h2>

<form method="post"> Enter company name: <input type="text" name="company" value="test"><br/> Enter company URL: <input type="text" name="companyurl" value=""><br/> Enter RSS URL: <input type="text" name="rssurl" value=""><br/> <input type="submit" name="submit" value="Make it so..."> </form>

And here is a modified form of the build script Terry did. I removed the Playbook support, made it copy to a temp dir, and added replacements in.

<?xml version="1.0" encoding="UTF-8"?> <project name="SimpleRSS" default="main" basedir=".">

&lt;property file=""/&gt;

&lt;!-- path to the flex task libraries. --&gt;
&lt;path id="flextasks.classpath"&gt;
	&lt;fileset dir="${FLEX_HOME}/ant/lib"&gt;
		&lt;include name="*.jar"/&gt;

&lt;typedef resource="flexTasks.tasks" classpathref="flextasks.classpath" /&gt;

&lt;target name="main" depends="prepPackage,," /&gt;

&lt;target name="clean"&gt;
	&lt;echo message="Cleaning Build Space"/&gt;
	&lt;delete dir="${build.dir}"/&gt;

&lt;target name="prepPackage" depends="compile,handleDevices" /&gt;

&lt;target name="compile" depends="clean"&gt; 
	&lt;echo message="Copying to temp dir" /&gt;

	&lt;copy todir="${temp.dir}"&gt;
		&lt;fileset dir="${dev.dir}" /&gt;

	&lt;echo message="Doing replacements... " /&gt;
	&lt;echo message="Replacing company with ${company}" /&gt;
	&lt;echo message="Replacing url with ${url}" /&gt;
	&lt;echo message="Replacing rss url with ${rssurl}" /&gt;

	&lt;replace dir="${temp.dir}" failOnNoReplacements="true"&gt;
		&lt;include name="**/*.mxml" /&gt;
		&lt;replacefilter token="@@@companyText@@@" value="${company}" /&gt;
		&lt;replacefilter token="@@@url@@@" value="${url}" /&gt;
		&lt;replacefilter token="@@@rssurl@@@" value="${rssurl}" /&gt;

	&lt;echo message="Compiling swf"/&gt;

	&lt;!-- hack one - had to add full path --&gt;
	&lt;mxmlc file="C:/Users/Raymond/Documents/My Dropbox/projects/SimpleRSS/${projectFile}" output="${swfFile}"&gt; 
        &lt;load-config filename="${FLEX_HOME}/frameworks/airmobile-config.xml"/&gt; 
        &lt;source-path path-element="${FLEX_HOME}/frameworks"/&gt;
		&lt;static-link-runtime-shared-libraries /&gt;

		&lt;compiler.library-path dir="${FLEX_HOME}/frameworks" append="true"&gt;
            &lt;include name="libs/*" /&gt;

		&lt;compiler.library-path dir="." append="true"&gt;
            &lt;include name="libs/*" /&gt;


	&lt;delete dir="${temp.dir}" /&gt;


&lt;target name=""&gt;
	&lt;echo message="Creating Device Folder for Android"/&gt;
	&lt;mkdir dir="${build.dir}/android"/&gt;
	&lt;echo message="Copying SWF for Android"/&gt;
	&lt;copy file="${swfFile}" todir="${build.dir}/android"  /&gt;
	&lt;echo message="Copying Application Description File for Android"/&gt;
	&lt;copy file="${dev.dir}/${}-app.xml" 
		todir="${build.dir}/android" preservelastmodified="true" /&gt;
	&lt;echo message="Modifying Application Description File"/&gt;
	&lt;replace file="${build.dir}/android/${}-app.xml"&gt;
		&lt;replacefilter token="${contentText}" value="${}.swf" /&gt;

&lt;target name="handleDevices" depends=""/&gt;

&lt;target name=""&gt;
	&lt;echo message="Packaging for Android"/&gt;
	&lt;exec executable="${ADT}" dir="${build.dir}/android" failonerror="true"&gt;
		&lt;arg value="-package"/&gt;
		&lt;arg line="-target apk"/&gt;
		&lt;arg line="-storetype pkcs12"/&gt;
		&lt;arg line="-keystore ${cert}" /&gt;
		&lt;arg line="-storepass ${cert.password}" /&gt;
		&lt;arg value="${}"/&gt;
		&lt;arg value="${}-app.xml"/&gt;
		&lt;arg value="${}.swf"/&gt;

&lt;target name=""&gt;
		&lt;echo message="Installing onto attached Android Device"/&gt;
		&lt;exec executable="${ADB}"&gt;
			&lt;arg value="install"/&gt;
			&lt;arg value="-r"/&gt;
			&lt;arg value="${build.dir}/android/${}.apk"/&gt;



Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA

Archived Comments

Comment 1 by Brian Rinaldi posted on 12/15/2010 at 8:03 PM

The next step would be to update the ANT task to generate more than just an Android version. In theory at least, you could get it to generate a Playbook and iOS version as well based upon user choices.

Comment 2 by Raymond Camden posted on 12/15/2010 at 8:07 PM

Yep, if you look at Terry's blog post he does just that. (Well, for Playbook.) I removed it intentionally to simplify things.