Air for Android: Geolocation


As Air for Android development continues to pick up I’m seeing a tidal wave of Android development coming out of Flash developers. I am so freakin’ excited about it!

I’m going to talk about some Air for Android APIs that many Flash developers my not be used to seeing.

Today: Geolocation.

If you are starting with this post I recommend actually starting with a previous post introducing AIR for Android Development. There is a ANT file there that you will want to have.


Memory, Power, and Performance Considerations
Unlike previous Air and Flex development Air for Android development is unique in the fact that you must be conscientious about battery life and depletion. As such, with Geolocation services you don’t want to keep tasking the GPS subsystems continuously as they are power hogs. When looking at the code look at a specific method:

_geo.setRequestedUpdateInterval(60000);

This method sets the timer for a moment to get the user’s current location. If you set this value low you will get quicker access to the current user’s locations however the battery will run down that much faster. There isn’t a hard and fast rule to follow, just get the location whenever is truly necessary.

Also you should remember that many devices can have their GPS muted. When the GPS is muted you shouldn’t even look for the GPS values, saving power and computation cycles.

The code I am showing checks for when the GPS status changes and then adds and removes listeners appropriately when the GPS systems are on or off.

The Geo App
The app itself is really simple, it displays your current GPS location. The entire example is written in pure AS3 but could also easily work in Flex.

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.GeolocationEvent;
    import flash.events.StatusEvent;
    import flash.sensors.Geolocation;
    import flash.text.TextField;

    public class Geo extends Sprite
    {
        //---------------------------------------------------------------------
        //
        //  Constructor
        //
        //---------------------------------------------------------------------
        public function Geo()
        {
            //setup stage
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;

            //add handlers
            addEventListener(Event.ADDED_TO_STAGE, _onAddedToStage);
        }

        //---------------------------------------------------------------------
        //
        //  Private Properties
        //
        //---------------------------------------------------------------------
        private var _text:TextField;

        private var _geo:Geolocation;

        private var _lat:Number;
        private var _long:Number;
        private var _alt:Number;
        private var _hor:Number;
        private var _ver:Number;
        private var _speed:Number;
        private var _heading:Number;
        private var _timestamp:Number;
        //---------------------------------------------------------------------
        //
        //  Protected Methods
        //
        //---------------------------------------------------------------------
        protected function createChildren():void
        {
            //text
            if(!_text)
            {
                _text = new TextField();
                _text.x = 10;
                _text.y = 10;
                _text.width = 400;
                _text.height = 400;
                _text.multiline = true;
                _setText();
                addChild(_text);
            }
        }

        //---------------------------------------------------------------------
        //
        //  Handler Methods
        //
        //---------------------------------------------------------------------
        private function _onAddedToStage(event:Event):void
        {
            //removes listener
            removeEventListener(Event.ADDED_TO_STAGE, _onAddedToStage);
            //draws background
            _draw();
            //creates children
            createChildren();
            //create geolocation tracker
            _createGeo();
        }

        private function _onGeo_StatusHandler(event:StatusEvent=null):void
        {
            if(_geo.muted)
            {
                _geo.removeEventListener(GeolocationEvent.UPDATE, _onGeo_UpdateHandler);
            } else {
                _geo.addEventListener(GeolocationEvent.UPDATE, _onGeo_UpdateHandler);
            }
            _setText();
        }

        private function _onGeo_UpdateHandler(event:GeolocationEvent):void
        {
            _lat = event.latitude;
            _long = event.longitude;
            _alt = event.altitude;
            _hor = event.horizontalAccuracy;
            _ver = event.verticalAccuracy;
            _speed = event.speed;
            _heading = event.heading;
            _timestamp = event.timestamp;
            _setText();
        }
        //---------------------------------------------------------------------
        //
        //  Private Methods
        //
        //---------------------------------------------------------------------
        private function _draw():void
        {
            graphics.clear();
            graphics.beginFill(0xFFFFFF,1);
            graphics.drawRect(0, 0, 480, 800);
            graphics.endFill();
        }

        private function _createGeo():void
        {
            if(Geolocation.isSupported)
            {
                _geo = new Geolocation();
                _geo.setRequestedUpdateInterval(60000);
                _geo.addEventListener(StatusEvent.STATUS, _onGeo_StatusHandler);
                _onGeo_StatusHandler();
            }
            _setText();
        }

        private function _setText():void
        {
            var s:String = "Geolocation Supported: "+Geolocation.isSupported;
            s += "Lat: "+_lat;
            s += "Long: "+_long;
            s += "Altitude: "+_alt;
            s += "Horizontal Accuracy: "+_hor;
            s += "Vertical Accuracy: "+_ver;
            s += "Speed: "+_speed;
            s += "Heading: "+_heading;
            s += "Timestamp: "+_timestamp;
            if(_geo)
                s += "Muted: "+_geo.muted;
        }
    }
}

Application Descriptor File
If you copied and pasted the previous code and hit run then you would have probably noticed that the Geolocation doesn’t work. The reason is that you need to specify that this application has access to the Geolocation hardware. You do that in the Application Descriptor file (Geo-app.xml).

The links you need to make sure to have available are as follows…

    <!-- Set the data part for manifest. Optional. -->
    <data>
          <![CDATA[
          <uses-permission android:name="android.permission.INTERNET"/>
          <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
          <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
          <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
          <supports-screens android:normalScreens="true"/>
          <uses-feature android:required="true" android:name="android.hardware.touchscreen.multitouch"/>
          ]]>
    </data>

The full Application Descriptor file is:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<application xmlns="http://ns.adobe.com/air/application/2.5">

<!-- Adobe AIR Application Descriptor File Template.

    Specifies parameters for identifying, installing, and launching AIR applications.

    xmlns - The Adobe AIR namespace: http://ns.adobe.com/air/application/2.5
            The last segment of the namespace specifies the version
            of the AIR runtime required for this application to run.

    minimumPatchLevel - The minimum patch level of the AIR runtime required to run
            the application. Optional.
-->

    <!-- A universally unique application identifier. Must be unique across all AIR applications.
         Using a reverse DNS-style name as the id is recommended. (Eg. com.example.ExampleApplication.) Required. -->
    <id>com.unitedmindset.Geo</id>

    <!-- Used as the filename for the application. Required. -->
    <filename>Geo</filename>

    <!-- The name that is displayed in the AIR application installer.
         May have multiple values for each language. See samples or xsd schema file. Optional. -->
    <name>Geo</name>

    <!-- A string value of the format <0-999>.<0-999>.<0-999> that represents application version which can be used to check for application upgrade.
         Values can also be 1-part or 2-part. It is not necessary to have a 3-part value.
         An updated version of application must have a versionNumber value higher than the previous version. Required for namespace >= 2.5 . -->
    <versionNumber>1.0.0</versionNumber>

    <!-- A string value (such as "v1", "2.5", or "Alpha 1") that represents the version of the application, as it should be shown to users. Optional. -->
    <!-- <versionLabel></versionLabel> -->

    <!-- Description, displayed in the AIR application installer.
         May have multiple values for each language. See samples or xsd schema file. Optional. -->
    <!-- <description></description> -->

    <!-- Copyright information. Optional -->
    <!-- <copyright></copyright> -->

    <!-- Publisher ID. Used if you're updating an application created prior to 1.5.3 -->
    <!-- <publisherID></publisherID> -->

    <!-- Settings for the application's initial window. Required. -->
    <initialWindow>
        <!-- The main SWF or HTML file of the application. Required. -->
        <!-- Note: In Flash Builder, the SWF reference is set automatically. -->
        <content>[This value will be overwritten by Flash Builder in the output app.xml]</content>

        <!-- The title of the main window. Optional. -->
        <!-- <title></title> -->

        <!-- The type of system chrome to use (either "standard" or "none"). Optional. Default standard. -->
        <!-- <systemChrome></systemChrome> -->

        <!-- Whether the window is transparent. Only applicable when systemChrome is none. Optional. Default false. -->
        <!-- <transparent></transparent> -->

        <!-- Whether the window is initially visible. Optional. Default false. -->
        <visible>true</visible>

        <!-- Whether the user can minimize the window. Optional. Default true. -->
        <!-- <minimizable></minimizable> -->

        <!-- Whether the user can maximize the window. Optional. Default true. -->
        <!-- <maximizable></maximizable> -->

        <!-- Whether the user can resize the window. Optional. Default true. -->
        <!-- <resizable></resizable> -->

        <!-- The window's initial width in pixels. Optional. -->
        <width>400</width>

        <!-- The window's initial height in pixels. Optional. -->
        <height>800</height>

        <!-- The window's initial x position. Optional. -->
        <!-- <x></x> -->

        <!-- The window's initial y position. Optional. -->
        <!-- <y></y> -->

        <!-- The window's minimum size, specified as a width/height pair in pixels, such as "400 200". Optional. -->
        <!-- <minSize></minSize> -->

        <!-- The window's initial maximum size, specified as a width/height pair in pixels, such as "1600 1200". Optional. -->
        <!-- <maxSize></maxSize> -->
    </initialWindow>

    <!-- We recommend omitting the supportedProfiles element, -->
    <!-- which in turn permits your application to be deployed to all -->
    <!-- devices supported by AIR. If you wish to restrict deployment -->
    <!-- (i.e., to only mobile devices) then add this element and list -->
    <!-- only the profiles which your application does support. -->
    <!-- <supportedProfiles>desktop extendedDesktop mobileDevice extendedMobileDevice</supportedProfiles> -->

    <!-- The subpath of the standard default installation location to use. Optional. -->
    <!-- <installFolder></installFolder> -->

    <!-- The subpath of the Programs menu to use. (Ignored on operating systems without a Programs menu.) Optional. -->
    <!-- <programMenuFolder></programMenuFolder> -->

    <!-- The icon the system uses for the application. For at least one resolution,
         specify the path to a PNG file included in the AIR package. Optional. -->
    <!-- <icon>
        <image16x16></image16x16>
        <image32x32></image32x32>
        <image36x36></image36x36>
        <image48x48></image48x48>
        <image72x72></image72x72>
        <image128x128></image128x128>
    </icon> -->

    <!-- Whether the application handles the update when a user double-clicks an update version
    of the AIR file (true), or the default AIR application installer handles the update (false).
    Optional. Default false. -->
    <!-- <customUpdateUI></customUpdateUI> -->

    <!-- Whether the application can be launched when the user clicks a link in a web browser.
    Optional. Default false. -->
    <!-- <allowBrowserInvocation></allowBrowserInvocation> -->

    <!-- Listing of file types for which the application can register. Optional. -->
    <!-- <fileTypes> -->

        <!-- Defines one file type. Optional. -->
        <!-- <fileType> -->

            <!-- The name that the system displays for the registered file type. Required. -->
            <!-- <name></name> -->

            <!-- The extension to register. Required. -->
            <!-- <extension></extension> -->

            <!-- The description of the file type. Optional. -->
            <!-- <description></description> -->

            <!-- The MIME content type. -->
            <!-- <contentType></contentType> -->

            <!-- The icon to display for the file type. Optional. -->
            <!-- <icon>
                <image16x16></image16x16>
                <image32x32></image32x32>
                <image48x48></image48x48>
                <image128x128></image128x128>
            </icon> -->

        <!-- </fileType> -->
    <!-- </fileTypes> -->

  <!-- Specify Android specific tags that get passed to AndroidManifest.xml file. -->
  <android>
  <manifestAdditions>
          <![CDATA[
            <manifest android:installLocation="auto">
                <uses-permission android:name="android.permission.INTERNET"/>
                <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
                <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
                <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
                <!--
                -->
                <uses-configuration android:reqFiveWayNav="true"/>
                <supports-screens android:normalScreens="true"/>
                <uses-feature android:required="true" android:name="android.hardware.touchscreen.multitouch"/>
                <application android:enabled="true">
                    <activity android:excludeFromRecents="false">
                        <intent-filter>
                        <action android:name="android.intent.action.MAIN"/>
                        <category android:name="android.intent.category.LAUNCHER"/>
                        </intent-filter>
                    </activity>
                </application>
            </manifest>
          ]]>
      </manifestAdditions>
    </android>
  <!-- End of the schema for adding the android specific tags in AndroidManifest.xml file -->

</application>

With all this you will have an application that works that will pull the latitude, longitude, heading (from North), speed, and horizontal/vertical accuracy.

Share

Comments (6)

[...] This post was mentioned on Twitter by Ultra Red, karannnnnnnnnnn3. karannnnnnnnnnn3 said: Air for Android: Geolocation: As Air for Android development continues to pick up I’m seeing a tidal wave of Andro… http://bit.ly/9ZuzBi [...]

[...] Geolocation the accelerometer has the setRequestedUpdateInterval method. I recommend playing with this interval [...]

StudentOctober 1st, 2010 at 6:29 am

u can also have to set uses permission in GPS location too

Jonathan CamposOctober 1st, 2010 at 8:44 am

@student I’m not sure what you’re trying to say?

Fabio BiondiNovember 15th, 2010 at 1:01 pm

From my experience following permission should be enough to use GMAp and geolocation on Android devices

Fabio

Leave a comment

Your comment