Tuesday, December 25, 2007

Cruising around with Android



Happy holidays, my fellow code slingers! For the past three weeks, I've plunged myself into the world of Android, the new mobile-platform SDK from our friends at Google. I'm really excited about the possibilities of this platform. The telephony API will be broken wide open for our fun and amusement.

Do you want to sync your phone contacts to a remote data source? No problem, use the bundled HttpClient and write a service. Do you have a buddy that likes to drunk dial you at 3:00 a.m.? Why not have a service that can filter his calls out during certain hours. Things like this would be hard or downright impossible in the past. Now we have Android!

I thought I'd do a little tutorial showcasing the Location API that ships with android. This is definitely a fun API to play with, so allow me to introduce the TrivialGPS application.

The aptly named TrivialGPS application will display a MapView, and center it on our current location as we move through the bay in "real-time". We use the observer pattern with the LocationManager, so our application can receive updates about changes in our current position and update the MapView accordingly.

At this point, I'm going to assume that you have either looked at the Android tutorials and have at least a rudimentary understanding of the framework, or that you're so damn intelligent that you don't need to.

First off, your manifest file must declare a couple of permissions for this application to work.

  • INTERNET
  • ACCESS_FINE_LOCATION
The TrivialGPS is a single activity that displays a map, so to do this our activity must extend MapActivity.

We need three member fields: The MapView which we will be displaying, a MapController which can center the map, and a LocationManager which we can query for providers and request geo information from.

public class TrivialGPS extends MapActivity {

private MapController mapController;
private MapView mapView;
private LocationManager locationManager;

...

Our onCreate method starts out very simple. We create a new MapView, set the zoom level to 22 (pretty close up, so we can see the streets), store a reference to the MapController, and then tell Android to display the map. We'll be revisiting this method a little bit later.

@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);

mapView = new MapView(this);
mapController = mapView.getController();
mapController.zoomTo(22);
setContentView(mapView);
}

In order to receive notifications about location updates, we need a LocationListener. A LocationListener is basically a callback that will be executed whenever there's a location change event. The simplest way to do this is with an inner class. The class must implement several methods, but the onLocationChanged method is the only one that will actually do any work. Will receive the coordinates, convert them to microdegrees, int a GeoPoint instance from these, and then uses the MapController to center the view on the new point.

public class LocationUpdateHandler implements LocationListener {

@Override
public void onLocationChanged(Location loc) {
int lat = (int) (loc.getLatitude()*1E6);
int lng = (int) (loc.getLongitude()*1E6);
GeoPoint point = new GeoPoint(lat, lng);
mapController.setCenter(point);
setContentView(mapView);
}

@Override
public void onProviderDisabled(String provider) {}

@Override
public void onProviderEnabled(String provider) {}

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

As promised, we now return to onCreate method.

The first thing we've added is a call to initialize the LocationManager. We do this with a simple getSystemService call. After this we just tell the locationManager that we wish to receive continous updates (the 0,0 parameters), from the hardware GPS and pass it a reference to our LocationListener.


public void onCreate(Bundle icicle) {
super.onCreate(icicle);

// create a map view
mapView = new MapView(this);
mapController = mapView.getController();
mapController.zoomTo(22);
setContentView(mapView);

// get a hangle on the location manager
locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);

locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,
new LocationUpdateHandler());

I hope some of you can find this useful. It took me several hours to figure this stuff out and get it working in my own application, so perhaps this tutorial will be a nice time saver for some of you out there, that way you can get back to your eggnog and Xmas toys.

The complete source for this tutorial is available here :
http://code.google.com/p/trivial-gps/
or on github if you prefer:
http://github.com/jasonhudgins/TrivialGPS/tree/master