What Happens When Your Users Install an AIR For Android Application Without AIR

I’ve gotten this question a couple of times so figured it might be worth a blog post (because I wasn’t sure until I tested it). Now that the AIR runtime is on the Market, there are going to be apps that need it. If you’re one of those application developers who creates one, what’s the user experience for someone who hasn’t yet installed AIR for Android? It’s actually pretty nice.

The application will install just fine without any issues. When the user tries to run that application, they’ll be presented with this screen:

After that, they can click the install link and it will take them to Adobe AIR in the Android Market where they can download and install it. After that, the application will work just fine.

Publishing AIR Apps to the Android Market

Now that the AIR for Android runtime has been released it means you can publish applications that you’ve created with the AIR for Android prerelease to the Android Market. If you’ve published to the App Store, fear not, this is muuuuch easier.

The first thing you need to do is get your developer account set up. To do that, browse to http://market.android.com/publish/Home and you’ll be prompted to fill out some information about yourself as well as pay $25 via Google Checkout. It seems like the developer name has to be unique, which seems odd, but when I put in “Ryan Stewart” it said it was taken.

Once the process is completed you’ll be able to start uploading applications. The initial screen will show nothing but an empty blue box and a button for uploading apps. That takes you to the pretty self-explanatory upload page.

Now is a good time to see about creating the certificate and, if you haven’t already, compiling the application. The Android Market lets you use a self-signed certificate and using adt you can create your own self-signed certificate. (Remember that you’ll need to be using adt from the AIR for Android prerelease). The only requirement is that it has to be valid for 25 years. The format is this:

adt -certificate -cn name [-ou org_unit][-o org_name]
[-c country] [-validityPeriod number_of_years]
key_type pfx_file password

And the example I used is this:

./adt -certificate -cn MyFirstSelfCert -ou ryanstewart
-validityPeriod 25 2048-RSA ~/Certificates/newselfcert.p12
mypassword

This creates a 2048-RSA certificate in my Certificates directory with the name newselfcert.p12 and a password of mypassword. Now that you have that certificate created you can use it to package your APK file. Assuming you’ve already created a release version of your AIR for Android application you can use adt to compile an apk file with the following command (or use the much simpler and better Package Assistant Pro by Serge):

./adt -package -target apk -storetype pkcs12 -keystore
PATH_TO_YOUR_CERTIFICATE -storepass
YOUR_CERTIFICATE_PASSWORD YOUR_APPLICATION_NAME.apk
YOUR_APP_XML_FILE-app.xml YOUR_SWF_FILE.swf

That command (when you replace the capital letters) will build an APK file (that’s the -target) out of the app-xml file and the .swf file and use the certificate you just created to sign it.

And you’re pretty much all set. Upload the APK to the Android Market, give it some snazzy screenshots, a great description, and start making money!

The Camera API and Geolocation Exif Data on AIR for Android

As part of my MAX session, I’ve been playing around with the camera API and wanted to pull some of the Exif data around geolocation. My first thought was that I could use the geolocation APIs in AIR to inject Exif data into the image from the camera. But the way that the AIR for Android Camera API works (which makes sense) is that when you take a picture, it creates a MediaPromise, which is a new class in AIR for Android that is similar to a FilePromise. That code includes a bunch of Exif data that Android adds. The only issue with the GPS coordinates are that they’re in sexagesimal format and need to be converted into decimal.

There are actually two ways to get images into your AIR application. The first is using the flash.media.CameraUI class. That brings up the camera controls and lets the user take a picture directly. When the user clicks “OK” on the camera, it saves the file to the SD card and then passes the file reference back to AIR where you can program an event handler to respond:

public var cameraUI:CameraUI;
 
protected function onCreationComplete():void
{
 
if(CameraUI.isSupported)
{
cameraUI = new CameraUI();
cameraUI.addEventListener(MediaEvent.COMPLETE,onComplete);
}
 
}

Then all that has to be done is to call the launch() method and that pops up the camera controls. When the user confirms the photo, the MediaEvent.COMPLETE handler deals with the photo. Or in this case, the file reference.

protected function btn_clickHandler(event:MouseEvent):void
{
cameraUI.launch(MediaType.IMAGE);
}

The other way to get access to photos is to pull them from the phone’s memory. You can do that with the flash.media.CameraRoll API. It’s a very similar process to the CameraUI. Instead of a complete event, listen for a select event, and to bring up the library of images, call the browseForImage() method.

public var cameraRoll:CameraRoll;
 
protected function onCreationComplete():void
{
if(CameraRoll.supportsBrowseForImage)
{
cameraRoll = new CameraRoll();
cameraRoll.addEventListener(MediaEvent.SELECT,onSelect);
}
}
 
protected function btn_clickHandler(event:MouseEvent):void
{
cameraRoll.browseForImage();
}

Now once the image has been taken or selected, we can pull the Exif data from it. Both the complete event and the select event will look the same because they both create a MediaPromise. I’m using the Exif library here, which uses the load() method and an event handler to deal with the Exif data when it loads.

protected function onSelect(event:MediaEvent):void
{
var request:URLRequest = new URLRequest(event.data.file.url);
var exif:ExifLoader = new ExifLoader();
exif.addEventListener(Event.COMPLETE,onExifComplete);
exif.load(request);
}

The parsing of the Exif data is pretty straightforward. Once it has been parsed, pulling out the specific tags by name for Latitude and Longitude, all that is left is to convert the sexagesimal latitude and longitude to decimal degrees.

protected function onExifComplete(event:Event):void
{
var exif:ExifInfo = event.currentTarget.exif as ExifInfo;
var gpsIfd:IFD = exif.ifds.gps;
 
// get the array of GPS coordinates from the Exif data
var exifLat:Array = gpsIfd["GPSLatitude"] as Array;
var exifLon:Array = gpsIfd["GPSLongitude"] as Array;
 
// get the decimal degrees for latitude/longitude
var latitude:Number = convertSexagesimalToDecimal(exifLat[0],exifLat[1],exifLat[2],gpsIfd["GPSLatitudeRef"]);
var longitude:Number = convertSexagesimalToDecimal(exifLon[0],exifLon[1],exifLon[2],gpsIfd["GPSLongitudeRef"]);
}
 
protected function convertSexagesimalToDecimal(degrees:int,minutes:int,seconds:Number,reference:String):Number
{
// do the conversion to decimal degrees
var decimal:Number = degrees + (minutes/60) + (seconds / 3600);
 
// figure out whether we need to use negative latitude or longitude
if(reference == "S" || reference == "E")
{
return decimal * -1;
} else {
return decimal;
}
}

So that’s a crash course in using the camera API on AIR for Android and then extracting decimal degrees out of Android’s existing Exif data.