Previous section   Next section

Mobile Phone Handheld Hardware Hardware Rick Rogers John Lombardo O'Reilly Media, Inc. O'Reilly Media Android Application Development, 1st Edition

9.6. Location Without Maps

What if your Activity needs to access location information, but it doesn't include a MapView? When you use a MapView, Android makes everything very easy with MyLocationOverlay, but even if you don't need a map, it isn't difficult to get location information. The code in this section is not part of MJAndroid, but it shows how you would go about getting location information without a map.

Let's look at a very simple, one-Activity application that displays the current location in a TextView.

9.6.1. The Manifest and Layout Files

An appropriate AndroidManifest.xml file follows. We created this file using the Android SDK and the Android Manifest Editor that comes as part of the SDK. The only change we needed to make with the editor was to add the uses-permission tag for android.per⁠mission.ACCESS_FINE_LOCATION (in the next-to-last line of the file). We always need this permission in order to get location information from a GPS location provider:

<?xml version="1.0" encoding="utf-8"?>
   <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.microjobsinc.dloc"
         android:versionCode="1"
         android:versionName="1.0.0">
       <application android:icon="@drawable/icon" android:label="@string/app_name">
           <activity android:name=".Main"
                     android:label="@string/app_name">
               <intent-filter>
                   <action android:name="android.intent.action.MAIN" />
                   <category android:name="android.intent.category.LAUNCHER" />
               </intent-filter>
           </activity>
       </application>

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

We'll use a very simple layout file with four TextViews: one label and one text box each for latitude and longitude:

<?xml version="1.0" encoding="utf-8"?>
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:orientation="vertical"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       >
   <TextView
       android:id="@+id/lblLatitude"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:text="Latitude:"
       />
   <TextView
       android:id="@+id/tvLatitude"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       />
   <TextView
       android:id="@+id/lblLongitude"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:text="Longitude:"
       />
   <TextView
       android:id="@+id/tvLongitude"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       />
   </LinearLayout>

9.6.2. Connecting to a Location Provider and Getting Location Updates

Let's start with an Activity that just connects with the GPS LocationProvider and gets and displays our current location (no updates). The procedure is pretty straightforward:

package com.microjobsinc.dloc;

import android.app.Activity;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.widget.TextView;

public class Main extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // find the TextViews
        TextView tvLatitude = (TextView)findViewById(R.id.tvLatitude);
        TextView tvLongitude = (TextView)findViewById(R.id.tvLongitude);

        // get handle for LocationManager
        LocationManager lm = (LocationManager) 
          getSystemService(Context.LOCATION_SERVICE);

        // connect to the GPS location service
        Location loc = lm.getLastKnownLocation("gps");

        // fill in the TextViews
        tvLatitude.setText(Double.toString(loc.getLatitude()));
        tvLongitude.setText(Double.toString(loc.getLongitude()));
    }
}

Here are some of the highlights of the code:

But we also want to get periodic location updates from the LocationManager so we can track our location as we move about. For that we need to add a listener routine and ask the LocationManager to call it when it has an update.

Location updates from the LocationManager are accessible to an application through a DispLocListener class, so we will create an instance of this class in the onCreate method of our main Activity. We are required to override a number of methods in DispLocListener to meet the LocationListener interface definition, but we don't need them for this application, so we'll leave the definitions empty.

The full implementation follows:

package com.microjobsinc.dloc;

import android.app.Activity;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.widget.TextView;

public class Main extends Activity {
    private LocationManager lm;
    private LocationListener locListenD;
    public TextView tvLatitude;
    public TextView tvLongitude;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // find the TextViews
        tvLatitude = (TextView)findViewById(R.id.tvLatitude);
        tvLongitude = (TextView)findViewById(R.id.tvLongitude);

        // get handle for LocationManager
        LocationManager lm = (LocationManager) 
          getSystemService(Context.LOCATION_SERVICE);

        // connect to the GPS location service
        Location loc = lm.getLastKnownLocation("gps");

        // fill in the TextViews
        tvLatitude.setText(Double.toString(loc.getLatitude()));
        tvLongitude.setText(Double.toString(loc.getLongitude()));

        // ask the Location Manager to send us location updates
        locListenD = new DispLocListener();
        lm.requestLocationUpdates("gps", 30000L, 10.0f, locListenD);
    }

    private class DispLocListener implements LocationListener {

        @Override
        public void onLocationChanged(Location location) {
            // update TextViews
            tvLatitude.setText(Double.toString(location.getLatitude()));
            tvLongitude.setText(Double.toString(location.getLongitude()));
        }

        @Override
        public void onProviderDisabled(String provider) {
        }

        @Override
        public void onProviderEnabled(String provider) {
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
    }
}

Our onCreate method creates an instance of DispLocListener and requests that the LocationManager update it as needed using requestLocationUpdates. This method takes four parameters:


String provider

Which location provider to use. We assume GPS is available in this case.


long minTime

Minimum update time, in milliseconds. The LocationManager will wait at least this long between updates. Here's an opportunity to tune your application for battery life: more frequent updates means more battery usage.


float minDistance

Minimum distance, in meters, required to trigger an update. The LocationMan⁠ager will update us only if we've moved at least this far since the last update.


LocationListener listener

The name of the listener method to call when there is an update. This is the DispLocListener instance we just created.

Finally, we want to add the onPause and onResume code to turn off location updates when we're not actually displaying on the user's screen, and then turn them back on when we are:

/**
 *  Turn off location updates if we're paused
 */
@Override
public void onPause() {
    super.onPause();
    lm.removeUpdates(locListenD);
}

/**
 * Resume location updates when we're resumed
 */
@Override
public void onResume() {
    super.onResume();
    lm.requestLocationUpdates("gps", 30000L, 10.0f, locListenD);
}

9.6.3. Updating the Emulated Location

While you are developing and debugging an application like the one just shown, you're normally running on the emulator. It would be nice (maybe even essential) to be able to update the current location that the emulator uses as it's running your code. Such a Mock Location Provider can get very fancy, but Android provides some built-in ways of updating the emulated location:

  • The geo program built into the Android shell

  • One-time updates via DDMS

  • Tracks that are sequentially updated via DDMS

We'll look at each of these.

9.6.3.1. Using geo to update location

The geo utility is built into the Android image that runs on the emulator. It has a number of capabilities, the most important of which is geo fix:


geo fix

You can use the geo fix command to send a location to Android by telnetting to the console of the emulated Android. The LocationProvider will then use this as the current location:

telnet localhost 5554
Android Console: type 'help' for a list of commands
OK
geo fix -122.842232 38.411908 0
OK

geo fix takes three parameters:


longitude

Specified in decimal


latitude

Also specified in decimal


altitude

Specified in meters

9.6.3.2. Using DDMS to update location

We talked a lot about DDMS (the Dalvik Debug Monitor Service) in Chapter 5, but two features are related to location updates. The Emulator Control pane of the DDMS screen provides several ways of controlling the running emulator. After switching to the DDMS perspective (click on DDMS in the upper right of the Eclipse window), you should see the Emulator Control pane in the middle left of the DDMS window (Figure 9-1). You will probably have to scroll down in that pane to see the controls related to Location Controls.

Figure 9-1. DDMS Emulator Control pane


To send a one-time update of a location to the emulator, just enter the longitude and latitude in the appropriate boxes and click Send.

If you click on the GPX or KML tabs, you will be able to load a GPX or KML file that describes a path, as shown in Figure 9-2. Here we've already loaded the file OR.kml, which is included on the website for this book. It traces a path near O'Reilly headquarters in Sebastopol, California.

Figure 9-2. DDMS Emulator with KML location updates


You can create GPX tracks with many GPS navigation software tools, and KML tracks with Google Earth or many other navigation programs. The OR.kml file was generated by plotting a series of Google Earth Placemarks and concatenating them together into a single file. Here's an excerpt from OR.kml:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.2">
<Document>
    <name>OR1.kml</name>
    <StyleMap id="msn_ylw-pushpin">
        <Pair>
            <key>normal</key>
            <styleUrl>#sn_ylw-pushpin</styleUrl>
        </Pair>
        <Pair>
            <key>highlight</key>
            <styleUrl>#sh_ylw-pushpin</styleUrl>
        </Pair>
    </StyleMap>
    <Style id="sh_ylw-pushpin">
        <IconStyle>
            <scale>1.3</scale>
            <Icon>
              <href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
            </Icon>
            <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
        </IconStyle>
        <ListStyle>
        </ListStyle>
    </Style>
    <Style id="sn_ylw-pushpin">
        <IconStyle>
            <scale>1.1</scale>
            <Icon>
              <href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
            </Icon>
            <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
        </IconStyle>
        <ListStyle>
        </ListStyle>
    </Style>
    <Placemark>
        <name>OR1</name>
        <LookAt>
            <longitude>-122.7583711698369</longitude>
            <latitude>38.38922415809942</latitude>
            <altitude>0</altitude>
            <range>14591.7166300043</range>
            <tilt>0</tilt>
            <heading>0.04087372005871314</heading>
            <altitudeMode>relativeToGround</altitudeMode>
        </LookAt>
        <styleUrl>#msn_ylw-pushpin</styleUrl>
        <Point>
            <coordinates>-122.8239277647483,38.40273084940345,0</coordinates>
        </Point>
    </Placemark>
    <Placemark>
        <name>OR2</name>
        <LookAt>
            <longitude>-122.7677364592949</longitude>
            <latitude>38.3819544049429</latitude>
            <altitude>0</altitude>
            <range>11881.3330990845</range>
            <tilt>0</tilt>
            <heading>-8.006283077460853e-010</heading>
            <altitudeMode>relativeToGround</altitudeMode>
        </LookAt>
        <styleUrl>#msn_ylw-pushpin</styleUrl>
        <Point>
            <coordinates>-122.8064486052584,38.40786910573772,0</coordinates>
        </Point>
    </Placemark>
    <Placemark>
        <name>OR3</name>
        <LookAt>
            <longitude>-122.7677364592949</longitude>
            <latitude>38.3819544049429</latitude>
            <altitude>0</altitude>
            <range>11881.3330990845</range>
            <tilt>0</tilt>
            <heading>-8.006283077460853e-010</heading>
            <altitudeMode>relativeToGround</altitudeMode>
        </LookAt>
        <styleUrl>#msn_ylw-pushpin</styleUrl>
        <Point>
            <coordinates>-122.7911077944045,38.41500788727795,0</coordinates>
        </Point>
    </Placemark>
 ...
          
      Previous section   Next section