Untappd – Down with Mobile Apps

Update: This was recently the topic of a post by Fred Wilson, a very well known VC. I’m riffing on it now because I had an example today that drove it home for me.

I hate that mobile apps have taken the world by storm. I hate not being able to access something because I have an Android phone and all that the site has is an iPhone app. As a developer I hate having to build for a crap-ton of different platforms to make sure my users have access to my content. If only there was some global phenomenon that was accessible on a mobile device and was completely cross-platform. Oh wait, there is, it’s called THE WEB.

Why in the hell are we building native mobile applications when we can just build a web application to accomplish the same thing? Now, I understand that there are some requirements, like camera, that force us into the native realm. But if you aren’t using the camera or some other device-specific API, you should be building a web app.

This is why I love Untappd so much. Untappd is essentially Foursquare for beer. Instead of checking into a location, you can “check-in” what beer you’re drinking. Instead of doing the native app thing, they built a mobile site. I can access it from any device. It uses the browser-based GPS API to get my location so I can attach that to my beer, and there’s no stupid install needed. Just a wonderful web app with all of the functionality I need accessible from anywhere.

I realize there are some caveats beyond the device-specific APIs, but there has to be ways around them. One of the biggies is responsiveness. I think this one is kind of a cop-out because a great web app will be able to create a UI in such a way that it feels just as responsive as a native app. jQuery mobile and Sencha Touch are great at this. The biggest thing is monetization. Currently it’s really hard to monetize web apps and it’s very easy to monetize native mobile applications. This is one of the reasons I’m so jazzed on the idea of the Chrome web store. Being able to make money on web apps could (I think) help change the tide and encourage more developers to go the web app route.

It’s a shame that we’re able to do such cool stuff on the web but that developers are jumping through hoops to lock down their content to specific devices. We’ve got technologies like PhoneGap in the interim, but the sooner we get back to the web, the better.

P.S. My username on Untappd is ryanstewart.

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.

Flash Player “Square” With IE9, Native 64-bit Support

Flash Player SquareToday you can go download the beta of IE9 and from what I’ve seen it looks like it’s pretty damn impressive. We also released a version of Flash Player, codenamed “Square” which not only has support for IE9, but includes a bunch of code collaboration that we did with Microsoft to create a really streamlined experience. The Flash Player Team Blog has a bunch of info:

As part of our collaboration with Microsoft’s Internet Explorer team over the past few months, Flash Player “Square” has been enhanced to directly support the hardware-accelerated graphics capabilities in the newest version of IE. Flash Player “Square” leverages the new GPU support available with Internet Explorer 9 Beta to deliver a faster and more responsive user experience with Flash-based content. In our internal testing, we’ve seen significant improvements in Flash Player graphics performance – exceeding 35% in Internet Explorer 9 Beta compared to Flash Player running in previous versions of IE. While the performance improvements will vary based on the type of content and how it’s created, bitmap-heavy content for Flash Player will experience the greatest benefit. Flash-enabled content that’s embedded as transparent (wmode=”transparent”) will also run more efficiently given the benefits of offloading the HTML and Flash content compositing to the GPU. Try it out by downloading the Internet Explorer 9 Beta and the Flash Player “Square” preview. We’d appreciate your feedback and observations on performance.

So right off the bat with IE9 you get hardware support for Flash. We’ve also (finally) got native 64-bit binaries for Mac, Linux, and Windows. It’s been a long time coming, but we hope you get a chance to test these versions out and give us feedback.

We’re only a couple of months from MAX, and this gives you a taste of some of the things we’ve been working on. Between the work on HTML5 with Dreamweaver and Illustrator and the work the Flash Platform teams have been doing, it’s going to be an incredible year for RIAs and for Adobe designers/developers.