Last night I read over Rich Tretola's excellent Geolocation for AIR/Mobile example. He demonstrates how to a) check and see if Geolocation is available (it may not be) and how to handle the response from the GPS system on the device. I thought it might be interesting to tie that to a change I made to the Adobe Groups remote API last night. I added a method that would return user groups based sorted by distance based on a longitude and latitude. For my testing I had done typed in values (everyone knows their longitude and latitude, right?) but I figured it would be even easier with AIR.

Let's take a look at the application I built. All in all it is pretty unrealistic since it has a grand total of one page, but I wanted something I could write in a few minutes.

<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" title="UG Locator" viewActivate="init()">

<fx:Declarations> <mx:HTTPService id="groupsHTTPService" url="http://groups.adobe.com/api/group.cfc" method="get" result="handleResult(event)" fault="handleFault(event)" /> </fx:Declarations>

<fx:Script> <![CDATA[ import com.adobe.serialization.json.*;

import flash.events.GeolocationEvent; import flash.sensors.Geolocation;

import mx.collections.ArrayCollection; import mx.collections.ArrayList; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent;

private var geoLocation:Geolocation;

[Bindable] private var groups:ArrayCollection;

private function init():void { groups = new ArrayCollection();

locate(); /* used for testing... / / var evt:GeolocationEvent = new GeolocationEvent(GeolocationEvent.UPDATE,false,false,30.2009,-92.0572); handleLocationRequest(evt); */ }

public function handleFault(evt:FaultEvent):void { //bug.text = evt.toString(); geoLocation.removeEventListener(GeolocationEvent.UPDATE, handleLocationRequest); }

public function handleResult(evt:ResultEvent):void { var data:Object = JSON.decode(evt.result as String); groups.source = data as Array; geoStatus.text = "There were "+data.length + " groups within 200 miles of your location."; }

private function locate():void { if(Geolocation.isSupported==true){ geoLocation = new Geolocation(); geoLocation.addEventListener(GeolocationEvent.UPDATE, handleLocationRequest); } else { geoStatus.text = "Geolocation feature not supported"; } }

private function handleLocationRequest(event:GeolocationEvent):void { geoLocation.removeEventListener(GeolocationEvent.UPDATE, handleLocationRequest); geoStatus.text="Finding groups near your location... "; var lat:Number = event.latitude; var lon:Number = event.longitude; var req:Object = new Object(); req.method="getgroupswithindistance"; req.longitude = lon; req.latitude = lat; req.range = 200; groupsHTTPService.request = req; groupsHTTPService.send(); }

]]> </fx:Script>

<s:VGroup width="100%" height="100%" paddingLeft="5" paddingRight="5" paddingTop="5">

<s:Label id="geoStatus" width="100%"/>

<s:List id="groupList" dataProvider="{groups}" width="100%" height="100%"> <s:itemRenderer> <fx:Component> <s:MobileIconItemRenderer labelFunction="displayGroup" messageFunction="displayMessage" iconField="AVATAR"> <fx:Declarations> <mx:NumberFormatter id="myNumFormat" precision="0" /> </fx:Declarations> <fx:Script> <![CDATA[ private function displayGroup(ob:Object):String { return ob.NAME; } private function displayMessage(ob:Object):String { return myNumFormat.format(ob.DISTANCE) + " miles away."; }

]]> </fx:Script> </s:MobileIconItemRenderer> </fx:Component> </s:itemRenderer>

</s:List> </s:VGroup>

</s:View>

The application begins by running a call to my locate method. The code here is a complete copy of Rich's code but essentially it just does the hardware check. As soon as you create a Geolocation object you can then begin listening for the result. I do that in handleLocationRequest. Once I've got your location I stop listening. (I assume you are standing still, motionless, like I am most of the day unfortunately.) Once I've got that I then just fire off a HTTP request to the API. Unfortunately my API for Groups is JSON only (for now), but it's easy enough to work with JSON if you use as3corelib. Once I push my data to my list - it gets rendered. I'm still learning Flex 4 but I love how I was able to have a mini "component" right within the page itself. Very convenient! And the result?

I've added APK as a mime type to IIS so hopefully the download link will work. I'd include the full source but outside of the XML that's it. Don't forget that in AIR for Mobile apps you have to be cognizant of the additional permissions your application may require. Within my android manifest block I added:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Enjoy! Oh - two quick notes.

While pushing out my code to the phone I noticed, for the first time, that it seemed to 'latch' on to the application. I had to manually delete the application one time. I didn't see that again so maybe it was just a fluke. But keep that in mind if you don't see your application refreshing when running on the hardware.

Lastly - I still need to share the source code for the Death Clock application I blogged about before MAX. Will do so soon.

Download attached file.