Using Location Manager and Location Provider

When building location-aware applications on the Android platform, there are several key classes you will use very often. A LocationProvider provides location data using several metrics, and its data is accessed through a LocationManager.

LocationManager, along with returning the available providers, also allows you to attach a LocationListener to be updated when the device location changes and/or directly fire an Intent based on the proximity to a specified latitude and longitude. The last-known Location is also available directly from the manager.

The Location class is a bean that represents all the location data available from a particular snapshot in time. Depending on the provider used to populate it, a Location may or may not have all the possible data present (it might not include speed or altitude, for example).

To get our Wind and Waves sample application started and to demonstrate the related concepts, the first thing we need to do is get a handle on the LocationManager.

11.2.1 Accessing location data with LocationManager

The central class that you will use to interact with location-related data on Android is the LocationManager. Before you can check which providers are available or query the last-known Location, you need to get the manager from the system service. The code to do this is shown in listing 11.3, which includes a portion of the MapViewActiv-ity that will drive our Wind and Waves application.

Listing 11.3 Start of MapViewActivity public class MapViewActivity extends MapActivity { <1-1 Extend private static final int MENU_SET_SATELLITE = 1; O MapActivity private static final int MENU_SET_MAP = 2; private static final int MENU_BUOYS_FROM_MAP_CENTER = 3; private static final int MENU_BACK_TO_LAST_LOCATION = 4;

. . . Handler and LocationListeners omitted here for brevity - shown in later listings private MapController mapController; private LocationManager locationManager; private LocationProvider locationProvider; private MapView mapView; private ViewGroup zoom; private Overlay buoyOverlay; private ProgressDialog progressDialog; private Drawable defaultMarker; private ArrayList<BuoyOverlayItem> buoys;

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

this . setContentView (R. layout. mapview_activity);

this.mapView = (MapView) this.findViewByld(R.id.map_view); this.zoom = (ViewGroup) findViewByld(R.id.zoom); this.zoom.addView(this.mapView.getZoomControls());

this.defaultMarker =

getResources().getDrawable(R.drawable.redpin); this.defaultMarker.setBounds(0, 0, this.defaultMarker.getlntrinsicWidth(), this.defaultMarker.getlntrinsicHeight());

this.buoys = new ArrayList<BuoyOverlayItem>();

@Override

C Define I LocationManager

| Define D LocationProvider public void onStart() { super.onStart(); this.locationManager =

(LocationManager) Q Instantiate

I LocationManager this.getSystemService(Context. LOCATION_SERVICE) ; <-' SyStem SerVice this.locationProvider =

this.locationManager.getProvider(

LocationManager . GPS_PROVIDER) ; <—n Assign GPS

// LocationListeners omitted here for brevity F LocationProvider

GeoPoint lastKnownPoint = this.getLastKnownPoint(); this.mapController = this.mapView.getController(); this.mapController.setZoom(10); this.mapController.animateTo(lastKnownPoint); this.getBuoyData(lastKnownPoint);

G Set up map

. . . onResume and onPause omitted for brevity . . . other portions of MapViewActivity are included in later listings in this chapter private GeoPoint getLastKnownPoint () { GeoPoint lastKnownPoint = null;

Location lastKnownLocation = this . locationManager. getLastKnownLocation ( ^^ Get the last

LocationManager. GPS_PROVIDER) ; <1-1 known Location if (lastKnownLocation != null) {

lastKnownPoint = LocationHelper.getGeoPoint(lastKnownLocation);

lastKnownPoint = LocationHelper.GOLDEN_GATE;

return lastKnownPoint;

The first thing to note with the MapViewActity is that it extends MapActivity O-Although we aren't focusing on the MapActivity details yet (that will be covered in section 11.3), this extension is still important to note. Once we get the class started, we declare member variables for LocationManager C and LocationProvider ©.

In order to instantiate the LocationManager we use the Activity getSystemSer-vice (String name) method Q. LocationManager is a system service, so we don't directly create it; we let the system return it. After we have the LocationManager, we also assign the LocationProvider we want to use with the manager's getProvider method Q. In this case we are using the GPS provider. We will talk more about the LocationProvider class in the next section.

Once we have the manager and provider in place, we use the onCreate method of our Activity to instantiate a MapController and set initial state for the screen ©. A MapController and the MapView it manipulates are also items we will cover more in section 11.3.

Along with helping you set up the provider you need, LocationManager supplies quick access to the last-known Location Q. This method is very useful if you need a quick fix on the last location, as opposed to the more involved techniques for registering for periodic location updates with a listener (a topic we will cover in section 11.2.3).

Though we don't use it in this listing, or in the Wind and Waves application at all, the LocationManager additionally allows you to directly register for proximity alerts. If you need to fire an Intent based on proximity to a defined location, you will want to be aware of the addProximityAlert method. This method lets you set the location you are concerned about with latitude and longitude, and then it lets you specify a radius and a Pendinglntent. If the device comes within the range, the Pendinglntent is fired. (There is a corresponding removeProximityAlert method as well.)

Getting back to the main purpose for which we will use the LocationManager with Wind and Waves, we next need to look a bit more closely at the GPS LocationProvider.

11.2.2 Using a LocationProvider

LocationProvider is an abstract class that helps define the capabilities of a given provider implementation. Different provider implementations, which are responsible for returning location information, may be available on different devices and in different circumstances.

So what are the different providers, and why are multiple providers necessary? Those are really context-sensitive questions, meaning the answer is, "it depends." Which provider implementations are available depends on the hardware capabilities of the device—does it have a GPS receiver, for example? It also depends on the situation; even if the device has a GPS receiver, can it currently receive data from satellites, or is the user somewhere that's not possible (an elevator or a tunnel)?

At runtime you will need to query for the list of providers available and use the most suitable one (or ones—it can often be advantageous to fall back to a less-accurate provider if your first choice is not available or enabled). The most common provider, and the only one available in the Android Emulator, is the LocationManager. GPS_PROVIDER provider (which uses the GPS receiver). Because it is the most common (and most accurate) and what is available in the emulator, this is the provider we are going to use for Wind and Waves. Keep in mind, though, at runtime in a real device, there will normally be multiple providers, including the LocationManager. NETWORK_PROVIDER provider (which uses cell tower and Wi-Fi access points to determine location data).

In listing 11.3 we showed how you can obtain the GPS provider directly using the getProvider(String name) method. Some alternatives to this approach of directly accessing a particular provider are shown in table 11.2.

Different providers may support different location-related metrics and have different costs or capabilities. The Criteria class helps to define what each provider instance can handle. Among the metrics available are the following: latitude and longitude, speed, bearing, altitude, cost, and power requirements.

Table 11.2 Methods for obtaining a LocationProvider reference

LocationProvider code snippet

Description

List<String> providers =

locationManager.getAllProviders();

Get all of the providers registered on the device.

List<String> enabledProviders =

locationManager.getAllProviders(true);

Get all of the currently enabled providers.

locationProvider =

locationManager.getProviders(true).get(0);

A shortcut to get the first enabled provider, regardless of type.

locationProvider =

this.locationManager.getBestProvider( myCriteria, true);

An example of getting a

LocationProvider using a specified Criteria. (You can create a criteria instance and specify whether bearing or altitude or cost and other metrics are required or not.)

Another important aspect of working with location data and LocationProvider instances is Android permissions. Location-related permissions need to be in your manifest depending on the providers you want to use. Listing 11.4 shows the Wind and Waves manifest XML file, which includes both COARSE- and FINE-grained location-related permissions.

Listing 11.4 A manifest file showing COARSE and FINE location-related permissions

<?xml version= "1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.msi.manning.windwaves">

<application android :icon="@drawable/wave_45" android :label="@string/app_name" android :theme="@android:style/Theme.Black">

<activity android :name="StartActivity"

android :label="@string/app_name"> <intent-filter>

<action android :name="android.intent.action.MAIN" /> <category android :name="android.intent.category.LAUNCHER" /> </intent-filter>

</activity>

<activity android : name= "MapViewActivity" /> <activity android :name="BuoyDetailActivity" />

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

</application>

<uses-permission android :name=

android.permission. ACCESS_COARSE_LOCATION" / >

<uses-permission android :name= © Include GPS

"android.permission.ACCESS_FINE_LOCATION" /> <-' provider

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

In terms of location permissions, we are including both the ACCESS_COARSE_ LOCATION O, and ACCESS_FINE_LOCATION © permissions in our manifest. The COARSE permission corresponds to the LocationManager.NETWORK_PROVIDER provider (cell and Wi-Fi based data), and the FINE permission corresponds to the LocationManager.GPS_PROVIDER provider. We aren't using the network provider in Wind and Waves, but we have noted that a worthwhile enhancement would be to fall back to the network provider if the GPS provider is unavailable or disabled—this permission would allow that.

Once you understand the basics of LocationManager and LocationProvider, the next step is to unleash the real power and register for periodic location updates in your application with the LocationListener class.

11.2.3 Receiving location updates with LocationListener

One way to keep abreast of the device location from within an Android application is to create a LocationListener implementation and register it to receive updates. LocationListener is a very flexible and powerful interface that lets you filter for many types of location events based on various properties. You have to implement the interface and register your instance to receive location data callbacks.

Listing 11.5 brings all of the pieces we have covered thus far into scope as we create several LocationListener implementations for the Wind and Waves MapViewActiv-ity (the parts we left out of listing 11.3) and then register those listeners using the LocationManager and LocationProvider.

Listing 11.5 Creation of LocationListener implementations in MapViewActivity

. start of class in Listing 11.3

O Create private final LocationListener locationListenerGetBuoyData = an°nym°us new LocationListener () { <J LocationListener public void onLocationChanged ( © Implement final Location loc) { <-1 onLocationChanged int lat = (int) (loc.getLatitude()

int lon = (int) (loc .getLongitude () © Get latitude

* LocationHelper .MILLION) ; <-1 and longitude

GeoPoint geoPoint = new GeoPoint (lat, lon) ; <1—O Create GeoPoint getBuoyData (geoPoint) ; <1-1

} | Update map public void onProviderDisabled(String s) { © pins (buoy data) }

public void onProviderEnabled(String s) { }

public void onStatusChanged(String s, int i, Bundle b) { }

private final LocationListener locationListenerRecenterMap = new LocationListener() {

public void onLocationChanged(final Location loc) { int lat = (int) (loc.getLatitude()

* LocationHelper.MILLION); int lon = (int) (loc.getLongitude()

* LocationHelper.MILLION);

GeoPoint geoPoint = new GeoPoint (lat, lon); G Move map to mapController.animateTo(geoPoint);

new location

public void onProviderDisabled(String s) { }

public void onProviderEnabled(String s) { }

public void onStatusChanged(String s, int i, Bundle b) { }

@Override public void onStart() { super.onStart(); this.locationManager = (Loc at i onManage r)

this.getSystemService(Context.LOCATION_SERVICE); this.locationProvider = this.locationManager.getProvider(LocationManager.GPS_PROVIDER);

if (locationProvider != null) {

this.locationManager.requestLocationUpdates( locationProvider.getName(), 3000, 185000, this.locationListenerGetBuoyData); this.locationManager.requestLocationUpdates( locationProvider.getName(), 3000, 1000, this.locationListenerRecenterMap); } else {

Toast.makeText(this, "Wind and Waves cannot continue, + " the GPS location provider is not available" + " at this time.", Toast. LENGTH_SHORT).show(); this.finish();

. . remainder of repeated code omitted (see listing 11.3)

Register locationListenerGetBuoyData

Register locationListener-RecenterMap

When implementing the LocationListener interface, it is often practical to use an anonymous inner class O- For our MapViewActivity we have created two Location-Listener implementations because we want to register them both using different settings, as we will show momentarily.

Within the first listener, locationListenerGetBuoyData, we see how the onLocationChanged method is implemented C. In that method we get the latitude and longitude from the Location sent in the callback Q We then use the data to create a

GeoPoint O after multiplying the latitude and longitude by 1 million (1e6). The 1e6 format is necessary because GeoPoint requires microdegrees for coordinates.

After we have the data, we update the map (using a helper method that resets a map Overlay, the details of which we will cover in the next section) ©. In the second listener, locationListenerRecenterMap, we perform a different task—we center the map ©.

The reason we are using two listeners becomes crystal clear when you see how listeners are registered with the requestLocationUpdates method of the LocationManager class. Here we are registering the first one, locationListenerGetBuoyData, to fire only when the new device location is a long way off from the previous one (185000 meters; we chose this number to stayjust under 100 nautical miles, which is the radius we will use to pull buoy data for our map; we don't need to redraw the buoy data on the map if the user moves less than 100 nautical miles) ©. We are registering the second one, locationListenerRecenterMap, to fire more frequently (so the map view recenters if the user stays inside our application but moves more than 1000 meters) ©. Using separate listeners like this allows us to fine-tune the event processing (rather than having to build in our own logic to do different things based on different values with one listener).

Register location listeners carefully

The time parameter to the requestLocationUpdates method should be used carefully. Getting location updates too frequently (less than 60000 ms per the documentation) can wear down the battery and make the application too noisy. In this sample we have used an extremely low value for the time parameter for debugging purposes (3000 ms). You should never use a value lower than the recommended 60000 ms in production code.

Although our implementation here works, and it is the most straightforward example, keep in mind that our registration of LocationListener instances could be made even more robust by implementing the onProviderEnabled and onProviderDisabled methods. Using those methods and different providers, you can see how you could provide useful messages to the user and also provide a graceful fallback through a set of providers (if GPS becomes disabled, try the network, and so on). With LocationManager, LocationProvider, and LocationListener instances in place, the next thing we need to address is more detail concerning the MapActivity and MapView we are using.

+1 0

Responses

  • Gherardo
    Which class help to get a location provider of your own ccchoice?
    1 year ago
  • Isembard
    What is location providers?
    3 months ago

Post a comment