PhoneGap for Flex Developers: Debugging PhoneGap Applications on Android

I’ve been spending a bit of time this weekend trying to dig into PhoneGap for obvious reasons. I’ve done a couple of basic PhoneGap apps prior to this but I wanted to get the full workflow down. Coming from a Flex/Flash background, I think I got a little spoiled. From what I’ve been able to tell, there’s almost nothing in the HTML/JS stack that can replicate what Flex/Flash Builder do. For this particular challenge I wanted to be able to debug my code on the device. Brian Leroux pointed me in some great directions but during the process I found myself a bit frustrated. There is a lot of “grab this github project, get this dependency, get this github project”, etc. That’s not to say it’s bad, it just wasn’t in a centralized place, which is what I’m used to. Simeon Bateman made an interesting comment to me over IM that I was from the “heavy, all-inclusive camp” whereas JavaScript developers came from the “lightweight, use-as-needed camp”. That’s something I need to get used to.

So here’s some general info I found while going through this process. Big disclaimer: This is more of a brain dump than anything, so it isn’t meant to be all that informative, but I wanted to jot it down for my own memory. Comments are welcome.

Aardwolf

The first thing I tried was Aardwolf, which seems really, really awesome. It’s essentially a remote debugger written in JavaScript. You open up the HTML page, which has a JavaScript console, and then load your application up on the device. It uses Node.js, which I thought was pretty slick, and by dropping a script into your page you can set breakpoints in the JavaScript console to test code. Sadly, I couldn’t get it to work. My main issue was that I had a hell of a time figuring out how to get the Node.js server exposed outside of my Mac correctly. I think I got close, but it stumped me. That’s not necessarily a knock on Aardwolf, because it looks really, really slick, I just didn’t have the Apache knowledge to pull it off. I’m going to revisit this one later.

Weinre

Weinre ended up being the winner for me. It seems like an absolutely perfect solution for the PhoneGap world because as long as you’ve exposed your Mac to web sharing, you can use the Mac client and with a simple line of code, you have access to everything about your application you’d expect from something like the Chrome Developer Tools. I think I’ll try to do a more in-depth blog post on this in the near future, but for now, here’s my rough setup:

I downloaded the Mac client from this page. I had web sharing on my Mac set up for the IP address 10.1.0.4. When it starts up, the Weinre client creates a ~/.weinre/ directory but if you want to customize the app at all you need to create a server.properties file in that directory. With this you can specify the host that the Weinre debugger will connect to. Mine was this:

boundHost:    10.1.0.4
httpPort:     8080
reuseAddr:    true
readTimeout:  1
deathTimeout: 5

Now the Weinre debugger is using the same IP address that any other devices use to connect to my Mac when they share the same network. When I read through the Weinre documentation, the examples focused on browser-apps that would be able to load a URL from that 10.1.0.4 URL. So at first I was kind of confused where PhoneGap fit in. But then I had my “duh” moment: one of the beautiful things with PhoneGap is that we’re just dealing with a web view instance in a native app. So all I had to do was take the script block that the “default” page creates for you and put that in my PhoneGap code. I started with the default PhoneGap template from Dreamweaver so I just added that line to my app:

<html>
<head>
<meta charset="UTF-8">
<title>Beer Inventory</title>
<link href="jquery-mobile/jquery.mobile-1.0a3.min.css" rel="stylesheet" type="text/css"/>
<script src="http://10.1.0.4:8080/target/target-script-min.js#anonymous"></script>
<script src="jquery-mobile/jquery-1.5.min.js" type="text/javascript"></script>
<script src="jquery-mobile/jquery.mobile-1.0a3.min.js" type="text/javascript"></script>
<!-- This reference to phonegap.js will allow for code hints as long as the current site has been configured as a mobile application.
      To configure the site as a mobile application, go to Site -> Mobile Applications -> Configure Application Framework... -->
<script src="/phonegap.js" type="text/javascript"></script>
<script src="scripts/test.js" type="text/javascript"></script>
</head>

It’s that first script block that gives me access to the Weinre debugger. Then I built that app and packaged it into an .apk file and installed it on my phone.

Voila! I automatically had access to all of the Weinre features for my PhoneGap app!

Fantastic!….err…..sort of

Being able to inspect elements and change them on the fly is awesome, but then I wanted to debug some JavaScript code. Turns out you can’t do that with Weinre (yet). This is one of the things that Aardwolf is really great at, but it has to tie into Node.js to get the breakpoints to work. Plus you have to manually enter an array of breakpoints in the JavaScript console to get it to work. I wasn’t able to get past the step of making Aardwolf and my local Node.js server accessible to my device, so I wasn’t able to test it. Plus I’m not totally sure how Aardwolf would work with PhoneGap, but I *think* it should. My plan this week is to try and get Aardwolf and my app running on Heroku so I don’t have to worry about making my local Node.js server accessible but I wasn’t able to do that tonight.

Welcome to the edge

As far as I can tell, we’re getting into some edgy territory here. Even the Google searches are pretty sparse. So if you’re coming from the Flash/Flex world, I think there will be some learning curves.

But I set out to do this with Android. For iOS, arguably the platform that is the most important, it’s way easy because the tools are much better. Since all PhoneGap projects are native projects, you can use the exact same debugging/deployment workflow as other iOS apps. That means you can leverage the power of Xcode to help out. I haven’t spent a ton of time in Xcode, so I’ll be spending some cycles on that this week as well, but it looks much, much nicer.

I’m really excited to spend a lot more time on PhoneGap. I think building apps with HTML/JS is kind of fun, even if there are some rough edges. Based on my experiments so far, the Flex/Flash story with AIR is way, way, WAY better than anything on the HTML/JS side for building mobile apps, including PhoneGap. But the world is embracing HTML and JavaScript and by learning JavaScript, as a developer, you will never be more versatile. So in that respect, PhoneGap is a great way to start bridging the gap between Flex/Flash and the HTML/JS ecosystem. So far I’m having fun.

AIR for Android Application – 100 Days of Exercise

100 Days of Exercise

100 Days of Exercise

Part of the evangelism team has been spending the past few weeks diving in and creating AIR mobile applications. We didn’t want them to look like just any other app so we hired the folks at The1stMovement and had them come up with some very cool designs across a few different application ideas. They gave us the PSD files and then we went to work developing them. It has been a great experience and I think it touches on something that AIR and Flex do really well; expressive, beautiful applications. This is an area where HTML/JS can’t quite cut it and that is pretty tough to do with native toolkits on iOS or Android. But it’s exactly the kind of thing that Flex and Flash excel at. Plus with AIR these apps can be taken and deployed to other platforms (you’ll see a version of all of these on the Apple App Store before too long). The apps themselves are awesome. Terry Ryan built a helpful item-finder called Finicky which incorporates a very cool grunge design. Renaun Erickson built the best looking Caltrain app on the market for finding train schedules and the closest stop to you based on GPS. Both are really spectacular.

Profile Screen

Profile Creation Screen

My contribution is 100 Days of Exercise, an app that will help track exercise progress over 100 days. Evidence has shown that if you do something 100 days in a row, it becomes a habit, and this app helps track that. It’s also supposed to help redefine what exercise means. For a lot of people, they think it means 3 hours at the gym, or an hour run, but it doesn’t have to. Just commit to something small for as long as you feel like you can do it. The goal is to get to the end of 100 days, not to throw yourself into a completely new lifestyle.

Wheel of Death

The Date Selector

One of the things I’m most proud of is the date picker. This is going to be an application for both Android and iOS and those platforms do date pickers very differently. Android has the buttons for up and down and iOS has the scroller. I wanted something that was very high design that would translate across both platforms. They came up with the “wheel of death” as a way to represent the month and date. So I took some of Evtim’s wheel layout code and created a custom Flex list layout that did what I wanted. It looks nice, is unique, performs well, and translates across any platform.

It’s been a fun project and I’m looking forward to adding a few things down the road. If you have any feedback, definitely drop me an email and let me know.

Dismissing a Flex Mobile PopUp With the Back Button

In an application I’m building for Android I use a lot of pop up windows (because that’s how Android does combo boxes) and I was having a tough time figuring out how to get them to close when the user pushes the back button. By default, the framework will call navigator.popView() whenever the back button gets pushed and the only way to override that is by doing it at the application level, which isn’t really ideal.

So here’s what I came up with.

Basically I intercept the removing event on the view, which is the last event that gets called before the view goes away. Using that event I check to see if any of my PopUps are on the screen using the isPopUp property and if they are, I use event.preventDefault() to stop the view from being removed and instead just remove the PopUp.

protected function view1_removingHandler(event:ViewNavigatorEvent):void
{
     if(exerciseList.isPopUp)
     {
          PopUpManager.removePopUp(exerciseList);
          event.preventDefault();
     }
 
     if(timeList.isPopUp)
     {
          PopUpManager.removePopUp(timeList);
          event.preventDefault();
     }
}

I’m not sure if this is really the best way to solve this issue, so if anyone has something that they’ve done I’d love to hear about it in the comments.

Presenting at the Portland Adobe User Group January 20th

PDX RIAI’ll be doing a presentation on the very diverse world of mobile and the Flash Platform at the Portland Adobe User Group on January 20th. I’ll be covering the iOS Packager for Flash Professional, Flex Mobile, AIR for Android, and the RIM tablet. It’s going to be a mix of hello world samples so that you can see the workflows as well as some techniques I have found for optimizing applications to get the best performance out of your apps. It will probably focus mostly on RIM and Flex Mobile/Android because that’s what I’ve spent the most time on.

So if you’re in Portland, come out and join us. I’m hoping to be able to answer any questions people have about building mobile apps with AIR and Flash, so if you’ve got questions, come armed with those as well. It’s also my birthday so we’ll be finding a place to grab beers and geek out afterwards (it is a PDX RIA meeting after all).

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.

Android, Flash, Free Beer, Pizza

If you’re in San Francisco this week you’ll want to swing by the free Droid Does Flash event that we’re putting on with Motorola on Thursday, September 16th, at the Adobe offices in San Francisco.

Droid Does Flash

It’s going to be a chance to check out some of Motorola’s new devices, win some software, drink free beer, and hear about optimizing content for Flash and devices. I’ll be talking about where Flash is in the mobile world and how we got here including some of the things we did to make the runtime sing on Android. After that, it’s all about networking, hanging out with some of the folks from Adobe, and drinking free beer. We’ve also got a pretty cool beer mug for all of the attendees that lets you show your malty-enthusiasm for Flash and Android.

Registrations are starting to fill up, so I encourage you to hit the Eventbrite site and sign yourself up.

Me on the Android Central Podcast

I did a quick semi-informal podcast today with the guys from Android Central on Flash Player and Android. It was a lot of fun to do and special thanks to Phil and Jerry for having me on.

The feedback after the Android Summit we had has been great and I have to say, I think the Android community is as great as the Flash community when it comes to smart, passionate and fun people.

Slides and Demos from FITC San Francisco

FITC was an absolutely awesome event. Props to Shawn, Rick and team for another great event. I’m looking forward to them coming back to San Francisco next year. Thanks to everyone who attended my talk. I got some great questions and as I said in the sessions, if you have anything cool you’re doing with mapping and Flash, drop me an email.

Below are the slides I used and I’ve also posted all of the code for the demos I did. I haven’t really cleaned them up at all, but if you have any questions, let me know.