Android 3.0 Application Development: GPS, Locations, and Maps

Exclusive offer: get 50% off this eBook here
Android 3.0 Application Development Cookbook

Android 3.0 Application Development Cookbook — Save 50%

Over 70 working recipes covering every aspect of Android development

$26.99    $13.50
by Kyle Merrifield Mew | July 2011 | Open Source

One of the most remarkable aspects of modern smartphones is the way they can detect their location either through a Global Positioning System (GPS), or cell towers and WiFi signal strength; and more often than not, applications use both.

In this article by Kyle Merrifield Mew, author of Android 3.0 Application Development Cookbook, we will cover the following topics:

  • Detecting a device's location
  • Listening for location changes
  • Setting up Google Maps
  • Zooming in on a MapView
  • Setting a map's location with a GeoPoint
  • Marking a location on a map with an overlay

 

Android 3.0 Application Development Cookbook

Android 3.0 Application Development Cookbook

Design and develop rich smartphone and tablet applications for Android 3.0

        Read more about this book      

(For more resources on Android, see here.)

Introduction

For managing location based information, Android provides the android.location package which in turn gives us the LocationManager class that gives us access to location based functions such as the latitude and longitude of a device's position. Tracking a device over time is made equally convenient and the LocationListener class monitors changes in location as they occur.

Listening for location changes is only a part of the story, as Google provides APIs for managing Google Maps data and displaying and manipulating maps through the use of the MapView and MapController classes. These powerful tools require us to sign up with Google first, and once done enable us to zoom in and out of maps, pan to any location that we are looking for, and when we want to, include application information on a map, and even add our own layers to maps and mark locations on a Google map.

 

Detecting a device's location

Android locations are expressed in terms of latitude and longitude coordinates. The default format is degrees. The Location object can also be used to store a time-stamp and other information such as speed and distance traveled.

Although obtaining a device's last known location does not always yield the most accurate information, it is often the first reading that we may want. It is fast, simple to employ, and makes a good introduction to the LocationManager.

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

How to do it...

  1. Use the TextView provided in the main.xml file and give it a resource ID:
    android:id="@+id/text_view"
  2. Declare a TextView as a class-wide field in the Java activity code:
    TextView textView;
  3. Then, find it in the usual way, from within the onCreate() method:
    textView = (TextView) findViewById(R.id.text_view);
  4. Next, and still within onCreate(), declare and define our LocationManager:

    LocationManager manager =
    (LocationManager) getSystemService(Context.LOCATION_SERVICE);

  5. Then, to retrieve the last known location using GPS and display this in the text view, add these lines:

    Location loc =
    manager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
    textView.setText("latitude: " + loc.getLatitude()
    + "\nlongitude: " + loc.getLongitude());

  6. Run the code on a handset or emulator to obtain its location:

Android 3.0 Application Development Cookbook

How it works...

The use of a LocationManager to obtain the device's last known location is very straightforward. As with other system services, we obtained it with getSystemService() and the getLastKnownLocation() method returns the Location object itself, which can be further queried to provide latitude and longitude coordinates. We could have done more with the Location object, for example Location.getAltitude() will return altitude and getDistance(Location) and getBearing(Location) will return distance and bearing to another Location.

It is possible to send mock locations to an emulator using the DDMS perspective in Eclipse:

Android 3.0 Application Development Cookbook

Before sending location data this way, make sure that you have set the emulator to allow mock locations under Settings | Applications | Development.

It is worth noting that although use of the getLastKnownLocation() method may not always be accurate, particularly if the device has been switched off for some time, it does have the advantage of yielding almost immediate results.

There's more...

Using GPS to obtain a location has a couple of drawbacks. Firstly, it does not work indoors; and secondly, it is very demanding on the battery. Location can be determined by comparing cell tower signal strengths, and although this method is not as accurate, it works well indoors and is much more considerate to the device's battery.

Obtaining a location with a network provider

The network provider is set up in exactly the same way as the previous GPS example, simply exchange the Location declaration with:

Location loc =
manager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

You will also need to change, or amend, the permission in the manifest file with:

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

 

Listening for location changes

Obtaining the last known location as we did in the previous recipe is all well and good and handy for retrieving a Location quickly, but it can be unreliable if the handset has been switched off or if the user is on the move. Ideally we want to be able to detect location changes as they happen and to do this we employ a LocationListener.

In this recipe we will create a simple application that keeps track of a mobile device's movements.

Getting ready

This task can be performed most easily by starting where the previous one left off. If you have not completed that task yet, do so now—it is very short—then return here. If you have already completed the recipe then simply open it up to proceed.

How to do it...

  1. First, move the declaration of our LocationManager so that it is a class-wide field:
    LocationManager manager;
  2. In the main Java activity code, before the TextView.setText() call, add the following three lines:

    LocationListener listener = new MyLocationListener();
    manager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
    30000, 50, listener);
    Location location =
    manager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

  3. Now create an inner class called MyLocationListener that implements LocationListener:
    LocationListener:
    public class MyLocationListener implements LocationListener {
    }
  4. Eclipse will most likely insist that you add some unimplemented methods and you should do so.
  5. For now, only complete one of them, the onLocationChanged() callback:

    @Override
    public void onLocationChanged(Location l) {
    textView.setText("/n/nlatitude: " +
    l.getLatitude() + "\nlongitude: " + l.getLongitude());
    }

  6. Leave the others as they are:
    @Override
    public void onProviderDisabled(String provider) {}
    @Override
    public void onProviderEnabled(String provider) {}
    @Override
    public void onStatusChanged(String provider,
    int status, Bundle extras) {}
  7. If you want to test this code on an emulator, then go right ahead. However, this code will create a serious drain on the battery of a handset, and it is wise to switch our listener off when it is not needed. Here we have used the activity's onPause() and onResume() functions to control this. You may wish to include these statements in any part of your activity's life cycle that suits your application's purpose:

    @Override
    protected void onResume() {
    super.onResume();
    manager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
    30000, 50, listener);
    }
    @Override
    protected void onPause() {
    super.onPause();
    manager.removeUpdates(this);
    }

  8. If you have not already tested this application, do so now. You will need to move around if you are testing it on a real device, or send mock locations to an emulator to see the code in action:

Android 3.0 Application Development Cookbook

How it works...

In this recipe we used the LocationManager to provide location updates roughly every 30 seconds (30000 milliseconds) or whenever the location changed by more than 50 meters. We say 'roughly' because these values work only as a guide and the actual frequency of updates often varies from the values we set. Nevertheless, setting these two parameters of the requestLocationUpdates() method to high values can make a big difference to the amount of battery power the GPS provider consumes. Hopefully the use of the provider and the LocationListener as the other two parameters is self explanatory.

The LocationListener operates very much as other listeners do and the purpose of the onProviderEnabled() and onProviderDisabled() should be clear. The onStatusChanged() method is called whenever a provider becomes unavailable after a period of availability or vice versa. The int, status can represent 0 = OUT_OF_SERVICE, 1 = TEMPORARILY_UNAVAILABLE, or 2 = AVAILABLE.

 

Android 3.0 Application Development Cookbook Over 70 working recipes covering every aspect of Android development
Published: July 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

 

        Read more about this book      

(For more resources on Android, see here.)

Setting up Google Maps

When it comes to displaying Google Maps from within our own applications Android makes this wonderfully simple by providing the MapView widget, which we can treat just like we would any other View.

Unfortunately, because the data we are using belongs to Google, before we can begin working with maps we have to register for a Google API key. This is free and simple to do, as this recipe will demonstrate.

Getting ready

Before we start, you will need to know the whereabouts of the files that you use when signing an application. These are debug.keystore and keytool.exe. The debug.keystore file can usually be found somewhere like C:\Users\<user>\.android\ on most PCs and keytool.exe should be in your Java program files; on my machine it was in C:\Program Files\Java\jdk1.6.0_25\bin\.

This exercise is designed to be run on an emulator. If you wish to run it on a handset then you will need to substitute debug.keystore with your own keystore file, which you will have set up when you registered as a developer with Google.

You will also need to check whether you have installed the Google APIs with the SDK. Although it is almost certain that you have as they install as default.

How to do it...

  1. From the command prompt enter the following line, substituting the location of your keystore where different:
    keytool.exe -list -alias androiddebugkey -keystore "C:\
    Users\<user>\.android\debug.keystore" -storepass android -keypass
    android
  2. After a moment you should see the MD5 fingerprint, which you should copy to your clipboard:

    Android 3.0 Application Development Cookbook

  3. Next, visit http://code.google.com/android/maps-api-signup.html and follow the instructions there.
  4. If all is successful you should receive your API key, which will be a long string of seemingly random alphanumeric characters. Save this somewhere secure.
  5. Now start up a new Android project in Eclipse, but instead of selecting an Android API level as the Build Target, select Google APIs:

    Android 3.0 Application Development Cookbook

  6. For Google Maps to work we need to set Internet permissions in the Manifest file as a child of the <manifest> element:

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

  7. We also need to inform the system that we are using a library. As a child of the <application> element include the following line:

    <uses-library
    android:name="com.google.android.maps" />

  8. We can create a layout from a single MapView, so edit the main.xml file to match the code here:

    <?xml version="1.0" encoding="utf-8"?>
    <com.google.android.maps.MapView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true"
    android:apiKey="[your API key goes here]" />

  9. Finally, in the Java activity code simply change the class extension from Activity to MapActivity:
    public class MapViewer extends MapActivity {
  10. This will cause Eclipse to ask you to implement isRouteDisplayed(). Ignore this for now by having it simply return false:

    @Override
    protected boolean isRouteDisplayed() {
    return false;
    }

  11. This code will now display Google Maps, although all we can do for the moment is pan around the map:

Android 3.0 Application Development Cookbook

How it works...

Signing up with Google this way is a task that needs to be performed only once and is fairly straightforward. The purpose is mainly security, and MD5 is a 128 bit Message-Digest algorithm that is widely used for checking file integrity.

Obviously a map with no zoom function is not much use and now that we have the API sign up process completed, we can concentrate on having a bit more fun with Google Maps.

 

Zooming in on a MapView

A map is of any use only if we can view it at particular scales and the Google Maps API allows us to achieve this by having zoom controls built into the MapView widget.

Here we will build a small application that will open Google Maps with built-in zoom controls that will allow us to view any area of the map at any allowable scale.

Getting ready

Start a new Android project in Eclipse but, as with any map based application, select a Google API as the build target.

How to do it...

  1. Open the manifest file of your project and add the following library <uses> definition to the <application> element:

    <uses-library
    android:name="com.google.android.maps" />

  2. Now add Internet permission to the <manifest> element:

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

  3. Open the main.xml file and set it up as follows:

    <?xml version="1.0" encoding="utf-8"?>
    <com.google.android.maps.MapView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/map_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true"
    android:apiKey="[your API key goes here]" />

  4. In the Java activity class, declare a MapView as a class field:
    MapView mapView;
  5. In the onCreate() method of the Java activity class and after the setContentView() statement add the following three lines.
    mapView = (MapView) findViewById(R.id.map_view);
    mapView.setBuiltInZoomControls(true);
    mapView.setSatellite(true);
  6. Next, change the activity class itself from an Activity extension to a MapActivity extension, like so:
    public class MapViewer extends MapActivity {
  7. Finally, override the isRouteDisplayed() method that Eclipse will insist you implement:

    @Override
    protected boolean isRouteDisplayed() {
    return false;
    }

  8. Now run the program on an emulator or handset to test the zoom controls:

Android 3.0 Application Development Cookbook

How it works...

This is another wonderful example of how simple it can be to incorporate Google Maps into our own applications. Once set up, the MapView widget is very easy to use and it took only a single statement to include the built in zoom controls and another to change to satellite mode.

We can gain further control still over our map with the help of a MapController which, amongst other things, allows us to zoom in and out programmatically. To declare a MapController for the MapView in the example above add a line like the one below after the setSatellite() statement:

MapController mapController = mapView.getController();

To zoom in or out one level at a time use mapController.zoomIn() or mapController.zoomOut(). To set the scale to a specific zoom level we can use mapController.setZoom(int) where int is a value between 1 and 21 with 1 being the largest scale and 21 the smallest:

Android 3.0 Application Development Cookbook

There's more...

The MapController provides another handy function that allows us to zoom to a set point on the map. This is a point measured in pixels and is not the same as setting a geographical location which will be covered in the next section.

Zooming to a fixed point on a MapView

To change the zoom focus from its default center point, use zoomInFixing(int x, int y) with x and y being a distance in pixels from the top-left corner of the MapView.

 

Android 3.0 Application Development Cookbook Over 70 working recipes covering every aspect of Android development
Published: July 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

 

        Read more about this book      

(For more resources on Android, see here.)

Setting a map's location with a GeoPoint

It is pleasant enough to be able to pan around and zoom into Google Maps but most useful applications require that a map opens at a specific location, either the user's location or a location set by the developer.

Here we will use the GeoPoint object to control the location displayed by our map.

Getting ready

Start up a library project by setting the build target as a Google API and, if you are not planning on testing this an a real handset, set up an Android Virtual Device to match.

How to do it...

  1. Start by adding the following <uses-library> declaration to the <application> element of the Android Manifest file of the project:

    <uses-library
    android:name="com.google.android.maps" />

  2. Also include Internet permission as a child of the <manifest> element itself:

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

  3. In the main.xml file create a MapView widget. Make it clickable and include your API key within it along with an ID:

    <com.google.android.maps.MapView
    android:id="@+id/map_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true"
    android:apiKey="[your API key goes here]" />

  4. Now, open the main Java activity file and change the extension type from Activity to MapActivity:
    public class MapViewer extends MapActivity {
  5. This will mean overriding the isRouteDisplayed() method, but having it simply return false will effectively lead to it being ignored:

    @Override
    protected boolean isRouteDisplayed() {
    return false;
    }

  6. Within the onCreate() method create and associate a local MapView and set it to display in satellite mode:
    MapView mapView = (MapView) findViewById(R.id.map_view);
    mapView.setSatellite(true);
  7. We will also need a map controller:
    MapController mapControl = mapView.getController();
  8. Next, define two doubles to act as our latitude and longitude. Here we have used a location in a central London park, but any legitimate coordinates will do:
    double lat = 51.50773;
    double lng = -0.16582;
  9. Then, convert these values to a GeoPoint, like so:
    GeoPoint gPoint =
    new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
  10. Now we can call on the MapController to animate to our designated GeoPoint:
    mapControl.animateTo(gPoint);
    mapControl.setZoom(17);
    mapView.invalidate();
  11. Run the code on your handset or emulator to view the location set by the GeoPoint:

Android 3.0 Application Development Cookbook

How it works...

The way we set this project up is the same as the other library projects, which is simply a matter of building against a Google API library and adding the <uses-library> to the application node of the manifest. Again we used the MapController class to take us to the geographic point with animateTo(GeoPoint). GeoPoints are constructed using integer units called micro-degrees which, as their names suggests, represent a millionth of a degree. One might just as easily have used GeoPoint point = new GeoPoint(51507730, -165820).

Note that although the invalidate() call is not always necessary, it does guarantee that our view will be redrawn.

Of course, it is not a difficult matter to take data provided by the GPS or network location finder to display a user's current location on the map, and this is left as an exercise for the reader.

 

Marking a location on a map with an overlay

Above all else one of the best features of the Google Maps API is the ability to add our own content to maps by overlaying them with our own material.

In this final exercise we will display a map at a particular location and then overlay it with our own imagery. We will also see how to translate from geographical locations to screen positions.

Getting ready

This task is a continuation of the previous one, so make sure you have completed this first and have it open in Eclipse.

Here we have used the built in icon.png as our graphic but if you want to use your own, then add this first to a res/drawable folder, ideally as a PNG file.

How to do it...

  1. Mostly what is required here is a new class, which we can add as an inner class, but first we need to convert our MapView and MapController to class wide fields:
    MapView mapView;
    MapController mapControl;
  2. Now it is simply a matter of adding a new class to our MapActivity. It should be defined like this:
    class MyMapOverlay extends com.google.android.maps.Overlay {
    }
  3. To take control of our overlay class we can override its draw() method:

    @Override
    public boolean draw(Canvas canvas, MapView mapView,
    boolean shadow, long when) {
    super.draw(canvas, mapView, shadow);
    double lat = 51.50778;
    double lng = -0.16590;
    GeoPoint oPoint =
    new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
    Point sPoint = new Point();
    mapView.getProjection().toPixels(oPoint, sPoint);
    Bitmap bmp = BitmapFactory.decodeResource(
    getResources(), R.drawable.icon);
    canvas.drawBitmap(bmp, sPoint.x, sPoint.y, null);
    return true;
    }

  4. This is now ready to run, and as we are providing the locations ourselves, it does not matter whether this code is tested on a real handset or an AVD:

Android 3.0 Application Development Cookbook

How it works...

The class of real interest here is Overlay, which is a library class and belongs to com.google.android.maps. Once established, all we had to do was override the draw() method to add our own layer to the map. The image is drawn onto a Canvas which makes the first parameter and the desired MapView the second. The boolean, shadow, shows only the shadow view if true and the long, when, controls the time that the overlay will be displayed in milliseconds after the instruction is received. If this argument is ignored in the call to the super class, then our layer will be displayed as soon as possible.

The GeoPoint of the overlay was set only meters away from the map location itself and one micro-meter is equal to around 10 centimeters, but do note that it is the top left corner of our image that the GeoPoint will locate at this point and we normally would have to adjust this to accommodate our overlay's dimensions.

Handiest of all of the MapView's methods is getProjection().toPixels which lets us easily convert real-world geographical coordinates to screen position in pixels and so mark any location we desire on our map.

Summary

In this article we saw how to detect device location and include Google maps in applications.


Further resources on this subject:


About the Author :


Kyle Merrifield Mew

Kyle Merrifield Mew is a writer, journalist, and developer who has been programming since the early 80s and who has been following the Android operating system, since its purchase by Google, with great interest. Kyle has always believed that open source platforms would one day provide lucrative opportunities for anyone with programming skills and that Android is that platform.

Books From Packt


Android Application Testing Guide
Android Application Testing Guide

Flash Development for Android Cookbook
Flash Development for Android Cookbook

Android User Interface Development: Beginner's Guide
Android User Interface Development: Beginner's Guide

iPhone JavaScript Cookbook
iPhone JavaScript Cookbook

jQuery Mobile First Look
jQuery Mobile First Look

Rhomobile Beginner's Guide
Rhomobile Beginner's Guide

Xcode 4 iPhone Development Beginner's Guide
Xcode 4 iPhone Development Beginner's Guide

MeeGo 1.0 Mobile Application Development Cookbook
MeeGo 1.0 Mobile Application Development Cookbook


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
j
5
n
T
5
W
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software