Chapter 9

Location-Based Services

WHAT YOU WILL LEARN IN THIS CHAPTER

  • Displaying Google Maps in your Android application
  • Displaying zoom controls on the map
  • Switching between the different map views
  • Adding markers to maps
  • How to get the address location touched on the map
  • Performing geocoding and reverse geocoding
  • Obtaining geographical data using GPS, Cell-ID, and Wi-Fi triangulation
  • How to monitor for a location
  • Building a Location Tracker application

You have all seen the explosive growth of mobile apps in recent years. One category of apps that is very popular is location-based services, commonly known as LBS. LBS apps track your location, and may offer additional services such as locating amenities nearby, as well as offer suggestions for route planning, and so on. Of course, one of the key ingredients in an LBS app is maps, which present a visual representation of your location.

In this chapter, you will learn how to make use of Google Maps in your Android application, and how to manipulate it programmatically. In addition, you will learn how to obtain your geographical location using the LocationManager class available in the Android SDK. This chapter ends with a project to build a Location Tracker application that you can install on an Android device and can use to track the location of friends and relatives using SMS messaging.

DISPLAYING MAPS

Google Maps is one of the many applications bundled with the Android platform. In addition to simply using the Maps application, you can also embed it into your own applications and make it do some very cool things. This section describes how to use Google Maps in your Android applications and programmatically perform the following:

  • Change the views of Google Maps.
  • Obtain the latitude and longitude of locations in Google Maps.
  • Perform geocoding and reverse geocoding (translating an address to latitude and longitude and vice versa).
  • Add markers to Google Maps.

Creating the Project

To get started, you need to first create an Android project so that you can display Google Maps in your activity.

TRY IT OUT: Creating the Google APIs Project

codefile LBS.zip available for download at Wrox.com

1. Using Eclipse, create an Android project and name it LBS.

image

NOTE In order to use Google Maps in your Android application, you need to ensure that you check the Google APIs as your build target. Google Maps is not part of the standard Android SDK, so you need to find it in the Google APIs add-on.

2. Once the project is created, note the additional JAR file (maps.jar) located under the Google APIs folder (see Figure 9-1).

How It Works

This simple activity created an Android project that uses the Google APIs add-on. The Google APIs add-on, besides including the standard Android and USB libraries, also includes the Maps library, packaged within the maps.jar file.

Obtaining the Maps API Key

Beginning with the Android SDK release v1.0, you need to apply for a free Google Maps API key before you can integrate Google Maps into your Android application. When you apply for the key, you must also agree to Google’s terms of use, so be sure to read them carefully.

To apply for a key, follow the series of steps outlined next.

image

NOTE Google provides detailed documentation about applying for a Maps API key at http://code.google.com/android/add-ons/google-apis/mapkey.html.

First, if you are testing the application on the Android emulator or an Android device directly connected to your development machine, locate the SDK debug certificate located in the default folder (C:Users<username>.android for Windows 7 users). You can verify the existence of the debug certificate by going to Eclipse and selecting Window ⇒ Preferences. Expand the Android item and select Build (see Figure 9-2). On the right side of the window, you can see the debug certificate’s location.

image

NOTE For Windows XP users, the default Android folder is C:Documents and Settings<username>Local SettingsApplication DataAndroid.

The filename of the debug keystore is debug.keystore. This is the certificate that Eclipse uses to sign your application so that it may be run on the Android emulator or devices.

Using the debug keystore, you need to extract its MD5 fingerprint using the Keytool.exe application included with your JDK installation. This fingerprint is needed to apply for the free Google Maps key. You can usually find the Keytool.exe in the C:Program FilesJava<JDK_version_number>in folder.

Issue the following command (see also Figure 9-3) to extract the MD5 fingerprint:

keytool.exe -list -alias androiddebugkey -keystore
"C:Users<username>.androiddebug.keystore" -storepass android
-keypass android -v

In this example, my MD5 fingerprint is 5C:67:CE:30:82:C3:58:08:88:2D:CE:56:27:80:50:EB.

In the preceding command, the following arguments are used:

  • -list — Shows details about the specified keystore
  • -alias — The alias for the keystore, which is “androiddebugkey” for debug.keystore
  • -keystore — Specifies the location of the keystore
  • -storepass — Specifies the password for the keystore, which is “android” for debug.keystore
  • -keypass — Specifies the password for the key in the keystore, which is “android” for debug.keystore

Copy the MD5 certificate fingerprint and navigate your web browser to: http://code.google.com/android/maps-api-signup.html. Follow the instructions on the page to complete the application and obtain the Google Maps key. When you are done, you should see something similar to what is shown in Figure 9-4.

image

NOTE Although you can use the MD5 fingerprint of the debug keystore to obtain the Maps API key for debugging your application on the Android emulator or devices, the key will not be valid if you try to deploy your Android application as an APK file. Once you are ready to deploy your application to the Android Market (or other methods of distribution), you need to reapply for a Maps API key using the certificate that will be used to sign your application. Chapter 12 discusses this topic in more detail.

Displaying the Map

You are now ready to display Google Maps in your Android application. This involves two main tasks:

  • Modify your AndroidManifest.xml file by adding both the <uses-library> element and the INTERNET permission.
  • Add the MapView element to your UI.

The following Try It Out shows you how.

TRY IT OUT: Displaying Google Maps

1. Using the project created in the previous section, replace the TextView with the following lines in bold in the main.xml file. (Be sure to replace the value of the android:apiKey attribute with the API key you obtained earlier):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
<com.google.android.maps.MapView 
    android:id="@+id/mapView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:enabled="true"
    android:clickable="true"
    android:apiKey="0AeGR0UwGH4pYmhcwaA9JF5mMEtrmwFe8RobTHA" />
 
</LinearLayout>

2. Add the following lines in bold to the AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.learn2develop.LBS"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk android:minSdkVersion="14" />
    <uses-permission android:name="android.permission.INTERNET"/>
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <uses-library android:name="com.google.android.maps" />
        <activity
            android:label="@string/app_name"
            android:name=".LBSActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    
</manifest>

3. Add the following statements in bold to the LBSActivity.java file. Note that LBSActivity should now extend the MapActivity base class.

package net.learn2develop.LBS;
 
import com.google.android.maps.MapActivity;
import android.os.Bundle;
 
public class LBSActivity extends MapActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
 
    @Override
    protected boolean isRouteDisplayed() {
        // TODO Auto-generated method stub
        return false;
    }
}

4. Press F11 to debug the application on the Android emulator. Figure 9-5 shows Google Maps displaying in the activity of the application.

How It Works

In order to display Google Maps in your application, you first needed the INTERNET permission in your manifest file. You then added the <com.google.android.maps.MapView> element to your UI file to embed the map within your activity. It is very important that your activity extends the MapActivity base class, which itself is an extension of the Activity class. For the MapActivity class, you need to implement one method: isRouteDisplayed(). This method is used for Google’s accounting purposes, and you should return true for this method if you are displaying routing information on the map. For most simple cases, you can simply return false.

In order to test your application on the Android emulator, be sure to create an AVD with the Google Maps API chosen as the selected target.

CAN’T SEE THE MAP?

If instead of seeing Google Maps displayed you see an empty screen with grids, then most likely you are using the wrong API key in the main.xml file. It is also possible that you omitted the INTERNET permission in your AndroidManifest.xml file. Finally, ensure that you have Internet access on your emulator/devices.

If your program does not run (i.e., it crashes), then you probably forgot to add the following statement to the AndroidManifest.xml file:

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

Note its placement in the file; it should be located within the <Application> element.

Displaying the Zoom Control

The previous section showed how you can display Google Maps in your Android application. You can pan the map to any desired location and it will be updated on-the-fly. However, on the emulator there is no way to zoom in or out from a particular location (on a real Android device you can pinch the map to zoom it). Thus, in this section, you will learn how you can enable users to zoom in or out of the map using the built-in zoom controls.

TRY IT OUT: Displaying the Built-In Zoom Controls

1. Using the project created in the previous section, add the following statements in bold:

package net.learn2develop.LBS;
 
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
 
import android.os.Bundle;
 
public class LBSActivity extends MapActivity {
    MapView mapView;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);
    }
 
    @Override
[[[MPS:codecolormagenta]]]    protected boolean isRouteDisplayed() {
        // TODO Auto-generated method stub
        return false;
    }
}

2. Press F11 to debug the application on the Android emulator. Observe the built-in zoom controls that appear at the bottom of the map when you click and drag the map (see Figure 9-6). You can click the minus (−) icon to zoom out of the map, and the plus (+) icon to zoom into the map.

How It Works

To display the built-in zoom controls, you first get a reference to the MapView and then call the setBuiltInZoomControls() method:

mapView = (MapView) findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);

Besides displaying the zoom controls, you can also programmatically zoom in or out of the map using the zoomIn() or zoomOut() method of the MapController class. The following Try It Out shows you how to achieve this.

TRY IT OUT: Programmatically Zooming In or Out of the Map

1. Using the project created in the previous section, add the following statements in bold to the LBSActivity.java file:

package net.learn2develop.LBS;
 
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
 
import android.os.Bundle;
import android.view.KeyEvent;
 
public class LBSActivity extends MapActivity {
    MapView mapView;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);
    }
 
    public boolean onKeyDown(int keyCode, KeyEvent event) 
    {
        MapController mc = mapView.getController(); 
        switch (keyCode) 
        {
            case KeyEvent.KEYCODE_3:
                mc.zoomIn();
                break;
            case KeyEvent.KEYCODE_1:
                mc.zoomOut();
                break;
        }
        return super.onKeyDown(keyCode, event);
    }
    
    @Override
[[[MPS:codecolormagenta]]]    protected boolean isRouteDisplayed() {
        // TODO Auto-generated method stub
codecolormagenta">        return false;
    }
}

2. Press F11 to debug the application on the Android emulator. You can now zoom into the map by clicking the numeric 3 key on the emulator. To zoom out of the map, click the numeric 1 key.

How It Works

To handle key presses on your activity, you handle the onKeyDown event:

    public boolean onKeyDown(int keyCode, KeyEvent event) 
    {
        MapController mc = mapView.getController(); 
        switch (keyCode) 
        {
            case KeyEvent.KEYCODE_3:
                mc.zoomIn();
                break;
            case KeyEvent.KEYCODE_1:
                mc.zoomOut();
                break;
        }
        return super.onKeyDown(keyCode, event);
    }

To manage the panning and zooming of the map, you need to obtain an instance of the MapController class from the MapView object. The MapController class contains the zoomIn() and zoomOut() methods (plus some other methods to control the map) to enable users to zoom in or out of the map, respectively.

Note that if you deploy the application on a real Android device, you may not be able to test the zooming feature, as most Android devices today do not have a physical keyboard.

Changing Views

By default, Google Maps is displayed in map view, which is basically drawings of streets and places of interest. You can also set Google Maps to display in satellite view using the setSatellite() method of the MapView class:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);
        mapView.setSatellite(true);
    }

Figure 9-7 shows Google Maps displayed in satellite view.

If you want to display traffic conditions on the map, use the setTraffic() method:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);
        mapView.setSatellite(true);
        mapView.setTraffic(true);
    }

Figure 9-8 shows the map displaying the current traffic conditions (you have to zoom in to see the roads). The different colors reflect the varying traffic conditions. In general, green equates to smooth traffic of about 50 miles per hour, yellow equates to moderate traffic of about 25–50 miles per hour, and red equates to slow traffic about less than 25 miles per hour.

Note that the traffic information is available only in major cities in the United States, France, Britain, Australia, and Canada, with new cities and countries frequently added.

Navigating to a Specific Location

By default, Google Maps displays the map of the United States when it is first loaded. However, you can set Google Maps to display a particular location. To do so, you can use the animateTo() method of the MapController class.

The following Try It Out shows how you can programmatically animate Google Maps to a particular location.

TRY IT OUT: Navigating the Map to Display a Specific Location

1. Using the project created in the previous section, add the following statements in bold to the LBSActivity.java file:

package net.learn2develop.LBS;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
 
import android.os.Bundle;
import android.view.KeyEvent;
 
public class LBSActivity extends MapActivity {
    MapView mapView;
    MapController mc;
    GeoPoint p;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);
        mapView.setSatellite(true);
        mapView.setTraffic(true);
        
        mc = mapView.getController();
        String coordinates[] = {"1.352566007", "103.78921587"};
        double lat = Double.parseDouble(coordinates[0]);
        double lng = Double.parseDouble(coordinates[1]);
 
        p = new GeoPoint(
            (int) (lat * 1E6),
            (int) (lng * 1E6));
 
        mc.animateTo(p);
        mc.setZoom(13);
        mapView.invalidate();
    }
 
    public boolean onKeyDown(int keyCode, KeyEvent event) 
    {
        //...
    }
    
    @Override
    protected boolean isRouteDisplayed() {
        //...
    }
}

2. Press F11 to debug the application on the Android emulator. When the map is loaded, observe that it now animates to a particular location in Singapore (see Figure 9-9).

How It Works

In the preceding code, you first obtained a map controller from the MapView instance and assigned it to a MapController object (mc). You then used a GeoPoint object to represent a geographical location. Note that for this class, the latitude and longitude of a location are represented in micro degrees. This means that they are stored as integer values. For a latitude value of 40.747778, for example, you need to multiply it by 1e6 (which is one million) to obtain 40747778.

To navigate the map to a particular location, you can use the animateTo() method of the MapController class. The setZoom() method enables you to specify the zoom level at which the map is displayed (the bigger the number, the more details you see on the map). The invalidate() method forces the MapView to be redrawn.

Adding Markers

Adding markers to a map to indicate places of interest enables your users to easily locate the places they are looking for. The following Try It Out shows you how to add a marker to Google Maps.

TRY IT OUT: Adding Markers to the Map

1. Create a GIF image containing a pushpin (see Figure 9-10) and copy it into the res/drawable-mdpi folder of the existing project. For the best effect, make the background of the image transparent so that it does not block parts of the map when the image is added to the map.

2. Using the project created in the previous activity, add the following statements in bold to the LBSActivity.java file:

package net.learn2develop.LBS;
 
import java.util.List;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.os.Bundle;
import android.view.KeyEvent;
 
public class LBSActivity extends MapActivity {
    MapView mapView;
    MapController mc;
    GeoPoint p;
    
    private class MapOverlay extends com.google.android.maps.Overlay
    {
        @Override
        public boolean draw(Canvas canvas, MapView mapView,
        boolean shadow, long when)
        {
            super.draw(canvas, mapView, shadow);
 
            //---translate the GeoPoint to screen pixels---
            Point screenPts = new Point();
            mapView.getProjection().toPixels(p, screenPts);
 
            //---add the marker---
            Bitmap bmp = BitmapFactory.decodeResource(
                getResources(), R.drawable.pushpin);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y-50, null);
            return true;
        }
    }
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);
        mapView.setSatellite(true);
        mapView.setTraffic(true);
        
        mc = mapView.getController();
        String coordinates[] = {"1.352566007", "103.78921587"};
        double lat = Double.parseDouble(coordinates[0]);
        double lng = Double.parseDouble(coordinates[1]);
 
        p = new GeoPoint(
            (int) (lat * 1E6),
            (int) (lng * 1E6));
 
        mc.animateTo(p);
        mc.setZoom(13);
        
        //---Add a location marker---
        MapOverlay mapOverlay = new MapOverlay();
        List<Overlay> listOfOverlays = mapView.getOverlays();
        listOfOverlays.clear();
        listOfOverlays.add(mapOverlay);
 
        mapView.invalidate();
    }
 
    public boolean onKeyDown(int keyCode, KeyEvent event) 
    {
        //...
    }
    
    @Override
    protected boolean isRouteDisplayed() {
        //...
    }
}

3. Press F11 to debug the application on the Android emulator. Figure 9-11 shows the marker added to the map.

How It Works

To add a marker to the map, you first needed to define a class that extends the Overlay class:

 private class MapOverlay extends com.google.android.maps.Overlay
    {
        @Override
        public boolean draw(Canvas canvas, MapView mapView,
        boolean shadow, long when)
        { 
            //...
        }
    }

An overlay represents an individual item that you can draw on the map. You can add as many overlays as you want. In the MapOverlay class, you overrode the draw() method so that you could draw the pushpin image on the map. In particular, note that you needed to translate the geographical location (represented by a GeoPoint object, p) into screen coordinates:

    //---translate the GeoPoint to screen pixels---
            Point screenPts = new Point();
            mapView.getProjection().toPixels(p, screenPts);

Because you want the pointed tip of the pushpin to indicate the position of the location, you need to deduct the height of the image (which is 50 pixels) from the y coordinate of the point (see Figure 9-12) and draw the image at that location:

    //---add the marker---
            Bitmap bmp = BitmapFactory.decodeResource(
                getResources(), R.drawable.pushpin);
            canvas.drawBitmap(bmp, screenPts.x, screenPts.y-50, null);

To add the marker, you created an instance of the MapOverlay class and added it to the list of overlays available on the MapView object:

//---Add a location marker---
        MapOverlay mapOverlay = new MapOverlay();
        List<Overlay> listOfOverlays = mapView.getOverlays();
        listOfOverlays.clear();
        listOfOverlays.add(mapOverlay);

Getting the Location That Was Touched

After using Google Maps for a while, you may want to know the latitude and longitude of a location corresponding to the position on the screen that was just touched. Knowing this information is very useful, as you can determine a location’s address, a process known as reverse geocoding (you will learn how this is done in the next section).

If you have added an overlay to the map, you can override the onTouchEvent() method within the MapOverlay class. This method is fired every time the user touches the map. The method has two parameters: MotionEvent and MapView. Using the MotionEvent parameter, you can determine whether the user has lifted his or her finger from the screen using the getAction() method. In the following code snippet, if the user has touched and then lifted the finger, you display the latitude and longitude of the location touched:

package net.learn2develop.LBS;
 
import java.util.List;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.Toast;
 
public class LBSActivity extends MapActivity {
    MapView mapView;
    MapController mc;
    GeoPoint p;
    
    private class MapOverlay extends com.google.android.maps.Overlay
    {
        @Override
        public boolean draw(Canvas canvas, MapView mapView,
        boolean shadow, long when)
        {
            //...
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event, MapView mapView)
        {
            //---when user lifts his finger---
            if (event.getAction() == 1) {
                GeoPoint p = mapView.getProjection().fromPixels(
                    (int) event.getX(),
                    (int) event.getY());
                    Toast.makeText(getBaseContext(),
                        "Location: "+
                        p.getLatitudeE6() / 1E6 + "," +
                        p.getLongitudeE6() /1E6 , 
                        Toast.LENGTH_SHORT).show();
            }
            return false;
        }
    }   
    //...
}

The getProjection() method returns a projection for converting between screen-pixel coordinates and latitude/longitude coordinates. The fromPixels() method then converts the screen coordinates into a GeoPoint object.

Figure 9-13 shows the map displaying a set of coordinates when the user clicks a location on the map.

Geocoding and Reverse Geocoding

As mentioned in the preceding section, if you know the latitude and longitude of a location, you can find out its address using a process known as reverse geocoding. Google Maps in Android supports this via the Geocoder class. The following code snippet shows how you can retrieve the address of a location just touched using the getFromLocation() method:

package net.learn2develop.LBS;
 
import java.io.IOException;
import java.util.List;
import java.util.Locale;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.location.Address;
import android.location.Geocoder;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.Toast;
 
public class LBSActivity extends MapActivity {
    MapView mapView;
    MapController mc;
    GeoPoint p;
    
    private class MapOverlay extends com.google.android.maps.Overlay
    {
        @Override
        public boolean draw(Canvas canvas, MapView mapView,
        boolean shadow, long when)
        {
            //...
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event, MapView mapView)
        {
            //---when user lifts his finger---
            if (event.getAction() == 1) {
                GeoPoint p = mapView.getProjection().fromPixels(
                        (int) event.getX(),
                        (int) event.getY());
 
                /*
                    Toast.makeText(getBaseContext(),
                        "Location: "+
                        p.getLatitudeE6() / 1E6 + "," +
                        p.getLongitudeE6() /1E6 ,
                        Toast.LENGTH_SHORT).show();
                 */
 
                Geocoder geoCoder = new Geocoder(
                        getBaseContext(), Locale.getDefault());
                try {
                    List<Address> addresses = geoCoder.getFromLocation(
                            p.getLatitudeE6()  / 1E6,
                            p.getLongitudeE6() / 1E6, 1);
 
                    String add = "";
                    if (addresses.size() > 0) 
                    {
                        for (int i=0; i<addresses.get(0).getMaxAddressLineIndex();
                                i++)
                            add += addresses.get(0).getAddressLine(i) + "n";
                    }
                    Toast.makeText(getBaseContext(), add, Toast.LENGTH_SHORT).show();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }   
                return true;
            }
            return false;
        }
    }   
    //...
}

The Geocoder object converts the latitude and longitude into an address using the getFromLocation() method. Once the address is obtained, you display it using the Toast class. Figure 9-14 shows the application displaying the address of a location that was touched on the map.

If you know the address of a location but want to know its latitude and longitude, you can do so via geocoding. Again, you can use the Geocoder class for this purpose. The following code shows how you can find the exact location of the Empire State Building by using the getFromLocationName() method:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);
        mapView.setSatellite(true);
        mapView.setTraffic(true);
        
        mc = mapView.getController();
                
        /*
        String coordinates[] = {"1.352566007", "103.78921587"};
        double lat = Double.parseDouble(coordinates[0]);
        double lng = Double.parseDouble(coordinates[1]);
 
        p = new GeoPoint(
            (int) (lat * 1E6),
            (int) (lng * 1E6));
 
        mc.animateTo(p);
        mc.setZoom(13);
        */
        
        //---geo-coding---
        Geocoder geoCoder = new Geocoder(this, Locale.getDefault());
        try {
            List<Address> addresses = geoCoder.getFromLocationName(
                "empire state building", 5);
            
            if (addresses.size() > 0) {
                p = new GeoPoint(
                        (int) (addresses.get(0).getLatitude() * 1E6),
                        (int) (addresses.get(0).getLongitude() * 1E6));
                mc.animateTo(p);
                mc.setZoom(20);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        //---Add a location marker---
        MapOverlay mapOverlay = new MapOverlay();
        List<Overlay> listOfOverlays = mapView.getOverlays();
        listOfOverlays.clear();
        listOfOverlays.add(mapOverlay);
 
        mapView.invalidate();
    }

Figure 9-15 shows the map navigating to the location of the Empire State Building.

GETTING LOCATION DATA

Nowadays, mobile devices are commonly equipped with GPS receivers. Because of the many satellites orbiting the earth, you can use a GPS receiver to find your location easily. However, GPS requires a clear sky to work and hence does not always work indoors or where satellites can’t penetrate (such as a tunnel through a mountain).

Another effective way to locate your position is through cell tower triangulation. When a mobile phone is switched on, it is constantly in contact with base stations surrounding it. By knowing the identity of cell towers, it is possible to translate this information into a physical location through the use of various databases containing the cell towers’ identities and their exact geographical locations. The advantage of cell tower triangulation is that it works indoors, without the need to obtain information from satellites. However, it is not as precise as GPS because its accuracy depends on overlapping signal coverage, which varies quite a bit. Cell tower triangulation works best in densely populated areas where the cell towers are closely located.

A third method of locating your position is to rely on Wi-Fi triangulation. Rather than connect to cell towers, the device connects to a Wi-Fi network and checks the service provider against databases to determine the location serviced by the provider. Of the three methods described here, Wi-Fi triangulation is the least accurate.

On the Android platform, the SDK provides the LocationManager class to help your device determine the user’s physical location. The following Try It Out shows how this is done in code.

TRY IT OUT: Navigating the Map to a Specific Location

1. Using the same project created in the previous section, add the following statements in bold to the LBSActivity.java file:

package net.learn2develop.LBS;
 
import java.io.IOException;
import java.util.List;
import java.util.Locale;
 
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
 
import android.content.Context;
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.location.Address;
import android.location.Geocoder;
 
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
 
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.Toast;
 
public class LBSActivity extends MapActivity {
    MapView mapView;
    MapController mc;
    GeoPoint p;
    
    LocationManager lm;
    LocationListener locationListener;
 
    private class MapOverlay extends com.google.android.maps.Overlay
    {
        //...
    }   
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.setBuiltInZoomControls(true);
        mapView.setSatellite(true);
        mapView.setTraffic(true);
        
        mc = mapView.getController();
                
        /*
        String coordinates[] = {"1.352566007", "103.78921587"};
        double lat = Double.parseDouble(coordinates[0]);
        double lng = Double.parseDouble(coordinates[1]);
 
        p = new GeoPoint(
            (int) (lat * 1E6),
            (int) (lng * 1E6));
 
        mc.animateTo(p);
        mc.setZoom(13);
        */
        
        //---geo-coding---
        Geocoder geoCoder = new Geocoder(this, Locale.getDefault());
        try {
            //...
        }
        
        //---Add a location marker---
        MapOverlay mapOverlay = new MapOverlay();
        List<Overlay> listOfOverlays = mapView.getOverlays();
        listOfOverlays.clear();
        listOfOverlays.add(mapOverlay);
 
        mapView.invalidate();
        
        //---use the LocationManager class to obtain locations data---
        lm = (LocationManager)
            getSystemService(Context.LOCATION_SERVICE);
 
        locationListener = new MyLocationListener();
    }
 
    @Override
    public void onResume() {    
        super.onResume();
        
        //---request for location updates---
        lm.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,
                0,
                0,
                locationListener);
    }
    
    @Override
    public void onPause() {    
        super.onPause();
        
        //---remove the location listener---
        lm.removeUpdates(locationListener);
    }
        
    private class MyLocationListener implements LocationListener
    {
        public void onLocationChanged(Location loc) {
            if (loc != null) {
                Toast.makeText(getBaseContext(),
                        "Location changed : Lat: " + loc.getLatitude() +
                        " Lng: " + loc.getLongitude(),
                        Toast.LENGTH_SHORT).show();
 
                p = new GeoPoint(
                        (int) (loc.getLatitude() * 1E6),
                        (int) (loc.getLongitude() * 1E6));
 
                mc.animateTo(p);
                mc.setZoom(18);
            }
        }
 
        public void onProviderDisabled(String provider) {
        }
 
        public void onProviderEnabled(String provider) {
        }
 
        public void onStatusChanged(String provider, int status,
            Bundle extras) {
        }
    }
    
    public boolean onKeyDown(int keyCode, KeyEvent event) 
    {
        //...
    }
    
    @Override
    protected boolean isRouteDisplayed() {
        //...
    }
}

2. Add the following line in bold to the AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.learn2develop.LBS"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk android:minSdkVersion="14" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <uses-library android:name="com.google.android.maps" />
        <activity
            android:label="@string/app_name"
            android:name=".LBSActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    
</manifest>

3. Press F11 to debug the application on the Android emulator.

4. To simulate GPS data received by the Android emulator, you use the Location Controls tool (see Figure 9-16) located in the DDMS perspective of Eclipse.

5. Ensure that you have first selected the emulator in the Devices tab. Then, in the Emulator Control tab, locate the Location Controls tool and select the Manual tab. Enter a latitude and longitude and click the Send button.

6. Observe that the map on the emulator now animates to another location (see Figure 9-17). This proves that the application has received the GPS data.

How It Works

In Android, location-based services are provided by the LocationManager class, located in the android.location package. Using the LocationManager class, your application can obtain periodic updates of the device’s geographical locations, as well as fire an intent when it enters the proximity of a certain location.

In the LBSActivity.java file, you first obtained a reference to the LocationManager class using the getSystemService() method. You did this in the onCreate() method of the LBSActivity:

//---use the LocationManager class to obtain locations data---
        lm = (LocationManager)
            getSystemService(Context.LOCATION_SERVICE);
 
        locationListener = new MyLocationListener();

Next, you created an instance of the MyLocationListener class, which you defined next.

The MyLocationListener class implements the LocationListener abstract class. You need to override four methods in this implementation:

  • onLocationChanged(Location location) — Called when the location has changed
  • onProviderDisabled(String provider) — Called when the provider is disabled by the user
  • onProviderEnabled(String provider) — Called when the provider is enabled by the user
  • onStatusChanged(String provider, int status, Bundle extras) — Called when the provider status changes

In this example, you’re more interested in what happens when a location changes, so you wrote some code in the onLocationChanged() method. Specifically, when a location changes, you display a small dialog on the screen showing the new location information: latitude and longitude. You show this dialog using the Toast class:

public void onLocationChanged(Location loc) {
            if (loc != null) {
                Toast.makeText(getBaseContext(),
                        "Location changed : Lat: " + loc.getLatitude() +
                        " Lng: " + loc.getLongitude(),
                        Toast.LENGTH_SHORT).show();
 
                p = new GeoPoint(
                        (int) (loc.getLatitude() * 1E6),
                        (int) (loc.getLongitude() * 1E6));
 
                mc.animateTo(p);
                mc.setZoom(18);
            }
        }

In the preceding method, you also navigate the map to the location that you have received.

To be notified whenever there is a change in location, you needed to register a request for location changes so that your program can be notified periodically. This is done via the requestLocationUpdates() method. You did this in the onResume() method of the activity:

    @Override
    public void onResume() {    
        super.onResume();
        
        //---request for location updates---
        lm.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,
                0,
                0,
                locationListener);
    }

The requestLocationUpdates() method takes four arguments:

  • provider — The name of the provider with which you register. In this case, you are using GPS to obtain your geographical location data.
  • minTime — The minimum time interval for notifications, in milliseconds. 0 indicates that you want to be continually informed of location changes.
  • minDistance — The minimum distance interval for notifications, in meters. 0 indicates that you want to be continually informed of location changes.
  • listener — An object whose onLocationChanged() method will be called for each location update

Finally, in the onPause() method, you remove the listener when the activity is destroyed or goes into the background (so that the application no longer listens for changes in location, thereby saving the battery of the device). You did that using the removeUpdates() method:

    @Override
    public void onPause() {    
        super.onPause();
        
        //---remove the location listener---
        lm.removeUpdates(locationListener);
    }

If you want to use Cell-ID and Wi-Fi triangulation (important for indoor use) to obtain your location data, you can use the network provider, like this:

    @Override
    public void onResume() {    
        super.onResume();
        
        //---request for location updates---
        lm.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,
                0,
                0,
                locationListener);
    }

To use the network provider, you need to add the ACCESS_COARSE_LOCATION permission to the AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.learn2develop.LBS"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk android:minSdkVersion="14" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <uses-library android:name="com.google.android.maps" />
        <activity
            android:label="@string/app_name"
            android:name=".LBSActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    
</manifest>
image

NOTE The network provider will not work on the Android emulator. If you test the preceding code on the emulator, it will result in an illegal argument exception. You need to test the code on a real device.

You can combine both the GPS location provider with the network location provider within your application:

    @Override
    public void onResume() {
        super.onResume();        
        
        //---request for location updates---
        lm.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,
                0,
                0,
                locationListener);        
        
        //---request for location updates---
        lm.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER,
                0,
                0,
                locationListener);
    }

However, be aware that doing so will cause your application to receive two different sets of coordinates, as both the GPS provider and the NETWORK provider will try to get your location using their own methods (GPS versus Wi-Fi and Cell ID triangulation). Hence, it is important that you monitor the status of the two providers in your device and use the appropriate one. You can check the status of the two providers by implementing the following three methods (shown in bold) of the MyLocationListener class:

    private class MyLocationListener implements LocationListener
    {
        @Override
        public void onLocationChanged(Location loc) {
            if (loc != null) {
                Toast.makeText(getBaseContext(),
                        "Location changed : Lat: " + loc.getLatitude() +
                        " Lng: " + loc.getLongitude(),
                        Toast.LENGTH_SHORT).show();
 
                p = new GeoPoint(
                        (int) (loc.getLatitude() * 1E6),
                        (int) (loc.getLongitude() * 1E6));
 
                mc.animateTo(p);
                mc.setZoom(18);
            }
        }
 
        //---called when the provider is disabled---
        public void onProviderDisabled(String provider) {            
            Toast.makeText(getBaseContext(),
                    provider + " disabled",
                    Toast.LENGTH_SHORT).show();          
        }
 
        //---called when the provider is enabled---
        public void onProviderEnabled(String provider) {
            Toast.makeText(getBaseContext(),
                    provider + " enabled",
                    Toast.LENGTH_SHORT).show();
        }
 
        //---called when there is a change in the provider status---
        public void onStatusChanged(String provider, int status,
            Bundle extras) {
            String statusString = "";            
            switch (status) {
                case android.location.LocationProvider.AVAILABLE:
                    statusString = "available";
                case android.location.LocationProvider.OUT_OF_SERVICE:
                    statusString = "out of service";
                case android.location.LocationProvider.TEMPORARILY_UNAVAILABLE:
                    statusString = "temporarily unavailable";
            }
            
            Toast.makeText(getBaseContext(),
                    provider + " " + statusString,
                    Toast.LENGTH_SHORT).show();
        }
    }

MONITORING A LOCATION

One very cool feature of the LocationManager class is its ability to monitor a specific location. This is achieved using the addProximityAlert() method.

The following code snippet shows how to monitor a particular location such that if the user is within a five-meter radius from that location, your application will fire an intent to launch the web browser:

import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
 
        //---use the LocationManager class to obtain locations data---
        lm = (LocationManager)
            getSystemService(Context.LOCATION_SERVICE);
 
        //---PendingIntent to launch activity if the user is within
        // some locations---
        PendingIntent pendingIntent = PendingIntent.getActivity(
            this, 0, new
            Intent(android.content.Intent.ACTION_VIEW,
              Uri.parse("http://www.amazon.com")), 0);
 
        lm.addProximityAlert(37.422006, -122.084095, 5, -1, pendingIntent);

The addProximityAlert() method takes five arguments: latitude, longitude, radius (in meters), expiration (duration for which the proximity alert is valid, after which it is deleted; -1 for no expiration), and the pending intent.

Note that if the Android device’s screen goes to sleep, the proximity is also checked once every four minutes in order to preserve the battery life of the device.

PROJECT — BUILDING A LOCATION TRACKER

Now that you have seen how to build a location-based Android application, you can put that knowledge to good use by combining the techniques covered in this chapter with the techniques covered in Chapter 8, “Messaging,” to build a cool working application. You will build a location tracker application that can be installed on a user’s Android device. You can then send an SMS message containing a specific code to the user’s device, and it will automatically return the location of the device through a return SMS message. This type of location tracker application could be used to keep track of your child or any elderly relative who lives alone (and it can be done without the person’s knowledge).

image

WARNING Before you roll out this application to your users, note that in some countries it is illegal to track the location of a person without his or her knowledge. If you install the location tracker application on a user’s phone, that device will automatically return its location information to whomever sends it an SMS message beginning with the words “Where are you?” Therefore, if you want to use this project in real life, you must alert potential users about the application’s functionality, so that they have the option to not reveal their location.

TRY IT OUT: Invoking an Activity

1. Using Eclipse, create a new Android project and name it LocationTracker.

2. Add the following lines in bold to the AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="net.learn2develop.LocationTracker"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="14" />
    
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.SEND_SMS" />    
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".LocationTrackerActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <!-- put this here so that even if the app is not running,
        your app can be woken up when there is an incoming SMS message -->
        <receiver android:name=".SMSReceiver"> 
            <intent-filter android:priority="100"> 
                <action
                    android:name="android.provider.Telephony.SMS_RECEIVED" /> 
            </intent-filter> 
        </receiver>
    </application>
</manifest>

3. Add a new Java class to the package name of the project and name it SMSReceiver. You should now have a Java file named SMSReceiver.java under the package name of your project (see Figure 9-18).

4. Populate the SMSReceiver.java file with the following lines in bold:

package net.learn2develop.LocationTracker;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
 
public class SMSReceiver extends BroadcastReceiver
{    
    LocationManager lm;
    LocationListener locationListener;
    String senderTel;
 
    @Override
    public void onReceive(Context context, Intent intent) 
    {        
        //---get the SMS message that was received---
        Bundle bundle = intent.getExtras();        
        SmsMessage[] msgs = null;
        String str="";            
        if (bundle != null)
        {
            senderTel = "";
            //---retrieve the SMS message received---
            Object[] pdus = (Object[]) bundle.get("pdus");
            msgs = new SmsMessage[pdus.length];
            for (int i=0; i<msgs.length; i++){
                msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
                if (i==0) {
                    //---get the sender address/phone number---
                    senderTel = msgs[i].getOriginatingAddress();
                } 
                //---get the message body---
                str += msgs[i].getMessageBody().toString();                    
            }
 
            if (str.startsWith("Where are you?")) {                
                //---use the LocationManager class to obtain locations data---
                lm = (LocationManager) 
                        context.getSystemService(Context.LOCATION_SERVICE);    
 
                //---request location updates---
                locationListener = new MyLocationListener();
                lm.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER, 
                        60000, 
                        1000, 
                        locationListener);
 
                //---abort the broadcast; SMS messages won't be broadcasted---
                this.abortBroadcast();
            }
        }                         
    }
 
    private class MyLocationListener implements LocationListener 
    {
        @
        public void onLocationChanged(Location loc) {
            if (loc != null) {
                //---send a SMS containing the current location---
                SmsManager sms = SmsManager.getDefault();
                sms.sendTextMessage(senderTel, null,
                        "http://maps.google.com/maps?q=" + loc.getLatitude() + "," +
                                loc.getLongitude(), null, null);
 
                //---stop listening for location changes---
                lm.removeUpdates(locationListener);                
            }
        }
 
        public void onProviderDisabled(String provider) {
        }
 
        public void onProviderEnabled(String provider) {
        }
 
        public void onStatusChanged(String provider, int status, 
                Bundle extras) {
        }
    }
}

5. To test the application, first deploy it onto a real Android device. Then, use another phone (any type of mobile phone that can send SMS messages) to send an SMS message to it with the message “Where are you?”

6. After the SMS message has been sent, wait for the Android device to send back an SMS message. Figure 9-19 shows the reply from the Android device, containing its location (shown here using an iPhone).

7. Most smartphones recognize URL data in the SMS message; therefore, if you click the URL in the SMS message, you will see the location through Google Maps (see Figure 9-20).

How It Works

This project combined the concepts that you learned in this chapter with the material about SMS messaging covered in Chapter 8 into one complete application. Once installed, the application will listen for incoming SMS messages that contain the text “Where are you?” It then intercepts these SMS messages, so the user won’t be able to see the message in the Messaging application on the Android device.

When an SMS message is received, you first extract the phone number of the sender so that you can use it later to send a reply containing the location of the device:

    //---retrieve the SMS message received---
            senderTel = "";
 
            //---retrieve the SMS message received---
            Object[] pdus = (Object[]) bundle.get("pdus");
            msgs = new SmsMessage[pdus.length];
            for (int i=0; i<msgs.length; i++){
                msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
                if (i==0) {
                    //---get the sender address/phone number---
                    senderTel = msgs[i].getOriginatingAddress();
                } 
                //---get the message body---
                str += msgs[i].getMessageBody().toString();                    
            }

You then examine the content of the SMS message. If it starts with the sentence “Where are you?”, you then request for location updates using the LocationManager class:

    if (str.startsWith("Where are you?")) {                
                //---use the LocationManager class to obtain locations data---
                lm = (LocationManager) 
                        context.getSystemService(Context.LOCATION_SERVICE);    
 
                //---request location updates---
                locationListener = new MyLocationListener();
                lm.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER, 
                        60000, 
                        1000, 
                        locationListener);
 
                this.abortBroadcast();
            }

Note that in this example, I have used the network provider to obtain my location because it does not require a line of sight to the sky (which is required by the GPS provider). However, using the network provider does require the device to have an Internet connection, so your application will not work if the device lacks one.

Once the location is obtained, you send a returning SMS message containing the location of the device, using a URL that points to Google Maps:

public void onLocationChanged(Location loc) {
            if (loc != null) {
                //---send a SMS containing the current location---
                SmsManager sms = SmsManager.getDefault();
                sms.sendTextMessage(senderTel, null,
                        "http://maps.google.com/maps?q=" + loc.getLatitude() + "," +
                                loc.getLongitude(), null, null);
 
                //---stop listening for location changes---
                lm.removeUpdates(locationListener);                
            }
        }

Once the SMS message is sent, you immediately remove the location updates so that you do not listen for location changes anymore.

Note that I have used 60,000 ms and 1,000 meters for the minTime and minDistance arguments of the requestLocationUpdates() method, respectively. If you recall from the earlier part of this chapter, I used 0 for both arguments, because I wanted to be continually informed of location changes. However, in this project you should not do this, as it would cause the application to continuously send several SMS messages back to the sender at once. This is because by the time you stop listening for location updates, the Location Manager would have called the onLocationChanged() method several times, reporting the device’s smallest changes in location and resulting in multiple SMS messages. In real testing, the number of SMS messages ranges from 10 to 60. Thus, you should set the minTime and minDistance arguments to a more reasonable value so that the Location Manager does not have a chance to fire the onLocationChanged() method repeatedly.

SUMMARY

This chapter took a whirlwind tour of the MapView object, which displays Google Maps in your Android application. You have learned the various ways in which the map can be manipulated, and you have also seen how you can obtain geographical location data using the various network providers: GPS, Cell-ID, or Wi-Fi triangulation. Finally, you learned to build a Location Tracker application that enables you to track a user’s location using SMS messaging.

EXERCISES

1. If you have embedded the Google Maps API into your Android application but it does not show the map when the application is loaded, what could be the likely reasons?

2. What is the difference between geocoding and reverse geocoding?

3. Name the two location providers that you can use to obtain your location data.

4. What method is used for monitoring a location?

Answers to the exercises can be found in Appendix C.

• WHAT YOU LEARNED IN THIS CHAPTER

TOPIC KEY CONCEPTS
Displaying the MapView
<com.google.android.maps.MapView
    android:id="@+id/mapView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:enabled="true"
    android:clickable="true"
    android:apiKey="YOUR_MAPS_API_KEY" />
Referencing the Map library
<uses-library android:name="com.google.android.maps" />
Displaying the zoom controls
mapView.setBuiltInZoomControls(true);
Programmatically zooming in or out of the map
mc.zoomIn();
mc.zoomOut();
Changing views
mapView.setSatellite(true);
mapView.setTraffic(true);
Animating to a particular location
mc = mapView.getController();
String coordinates[] = {"1.352566007", "103.78921587"};
double lat = Double.parseDouble(coordinates[0]);
double lng = Double.parseDouble(coordinates[1]);
p = new GeoPoint(
     (int) (lat * 1E6),
     (int) (lng * 1E6));
 mc.animateTo(p);
Adding markers
Implement an Overlay class and override the draw() method
Getting the location of the map touched
                GeoPoint p = mapView.getProjection().fromPixels(
                    (int) event.getX(),
                    (int) event.getY());
Geocoding and reverse geocoding
Use the Geocoder class
Obtaining location data
private LocationManager lm;
//...
        lm = (LocationManager)
            getSystemService(Context.LOCATION_SERVICE);
        locationListener = new MyLocationListener();
        lm.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,
                0,
                0,
                locationListener);
//...
    private class MyLocationListener implements LocationListener
    {
        public void onLocationChanged(Location loc) {
            if (loc != null) {
            }
        }
        public void onProviderDisabled(String provider) {
        }
        public void onProviderEnabled(String provider) {
        }
        public void onStatusChanged(String provider, int status,
            Bundle extras) {
        }
    }
Monitoring a location
lm.addProximityAlert(37.422006, -122.084095, 5, -1, 
    pendingIntent);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset