Chapter 39

Accessing Location-Based Services

A popular feature on current mobile devices is GPS capability, so the device can tell you where you are at any point in time. While the most popular uses of GPS service are for mapping and getting directions, there are other things you can do with it if you know your location. For example, you might set up a dynamic chat application based on physical location, so users can chat with those people who are nearest to them. Or, you could automatically geo-tag posts to Twitter or similar services.

GPS is not the only way a mobile device can identify your location. Alternatives include the following:

  • The European equivalent to GPS, called Galileo, which is still under development at the time of this writing
  • Cell tower triangulation, where your position is determined based on signal strength to nearby cell towers
  • Proximity to public Wi-Fi hotspots that have known geographic locations

Android devices may have one or more of these services available to them. You, as a developer, can ask the device for your location, plus details on which providers are available. There are even ways for you to simulate your location in the emulator, for use in testing your location-enabled applications.

Location Providers: They Know Where You’re Hiding

Android devices can have access to several different means of determining your location. Some will have better accuracy than others. Some may be free, while others may have a cost associated with them. Some may be able to tell you more than just your current position, such as your elevation over sea level or your current speed.

Android has abstracted all this out into a set of LocationProvider objects. Your Android environment will have zero or more LocationProvider instances, one for each distinct locating service that is available on the device. Providers know not only your location, but their own characteristics, in terms of accuracy, cost, and so on.

You, as a developer, will use a LocationManager, which holds the LocationProvider set, to figure out which LocationProvider is right for your particular circumstance. You will also need a permission in your application, or the various location APIs will fail due to a security violation. Depending on which location providers you wish to use, you may need ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, or both (see Chapter 38).

Finding Yourself

The obvious thing to do with a location service is to figure out where you are right now. To do that, you first need to get a LocationManager, so call getSystemService(LOCATION_SERVICE) from your activity or service and cast it to be a LocationManager. The next step is to get the name of the LocationProvider you want to use. Here, you have two main options:

  • Ask the user to pick a provider
  • Find the best-match provider based on a set of criteria

If you want the user to pick a provider, calling getProviders() on the LocationManager will give you a List of providers, which you can then present to the user for selection.

If you want to find the best-match provider based on a set of criteria, create and populate a Criteria object, stating the particulars of what you want out of a LocationProvider. Following are some of the methods that you can use to specify criteria:

  • setAltitudeRequired(): Indicates whether or not you need the current altitude
  • setAccuracy(): Sets a minimum level of accuracy, in meters, for the position
  • setCostAllowed(): Controls whether the provider must be free or can incur a cost on behalf of the device user

Given a filled-in Criteria object, call getBestProvider() on your LocationManager, and Android will sift through the criteria and give you the best answer. Note that not all of your criteria may be met; all but the monetary cost criterion might be relaxed if nothing matches.

You are also welcome to hard-wire in a LocationProvider name (e.g., GPS_PROVIDER), perhaps just for testing purposes.

Once you know the name of the LocationProvider, you can call getLastKnownPosition() to find out where you were recently. However, unless something else is causing the desired provider to collect fixes (e.g., unless the GPS radio is on), getLastKnownPosition() will return null, indicating that there is no known position. On the other hand, getLastKnownPosition() incurs no monetary or power cost, since the provider does not need to be activated to get the value.

These methods return a Location object, which can give you the latitude and longitude of the device in degrees as a Java double. If the particular location provider offers other data, you can get that as well:

  • For altitude, hasAltitude() will tell you if there is an altitude value, and getAltitude() will return the altitude in meters.
  • For bearing (i.e., compass-style direction), hasBearing() will tell you if there is a bearing available, and getBearing() will return it as degrees east of true north.
  • For speed, hasSpeed() will tell you if the speed is known, and getSpeed() will return the speed in meters per second.

A more likely approach to getting the Location from a LocationProvider, though, is to register for updates, as described in the next section.

On the Move

Not all location providers are necessarily immediately responsive. GPS, for example, requires activating a radio and getting a fix from the satellites before you get a location. That is why Android does not offer a getMeMyCurrentLocationNow() method. Combine that with the fact that your users may not want their movements to be reflected in your application, and you are probably best off registering for location updates and using that as your means of getting the current location.

The Internet/Weather and Service/WeatherAPI sample applications show how to register for updates—call requestLocationUpdates() on your LocationManager instance. This method takes four parameters:

  • The name of the location provider you wish to use
  • How long, in milliseconds, should have elapsed before we might get a location update
  • How far, in meters, the device must have moved before we might get a location update
  • A LocationListener that will be notified of key location-related events, as shown in the following example:
LocationListener onLocationChange=new LocationListener() {
  public void onLocationChanged(Location location) {
    if (state.weather!=null) {
      state.weather.getForecast(location, state);
    }
    else {
      Log.w(getClass().getName(), "Unable to fetch forecast – no WeatherBinder");
    }
  }

  public void onProviderDisabled(String provider) {
    // required for interface, not used
  }

  public void onProviderEnabled(String provider) {
    // required for interface, not used
  }

  public void onStatusChanged(String provider, int status,
                               Bundle extras) {
    // required for interface, not used
  }
}

Here, all we do is trigger a FetchForecastTask with the Location supplied to the onLocationChanged() callback method.

Bear in mind that the time parameter is only a guide to help steer Android from a power consumption standpoint. You may get many more location updates than this. To get the maximum number of location updates, supply 0 for both the time and distance constraints.

When you no longer need the updates, call removeUpdates() with the LocationListener you registered. If you fail to do this, your application will continue receiving location updates even after all activities and such are closed up, which will also prevent Android from reclaiming your application’s memory.

There is another version of requestLocationUpdates() that takes a PendingIntent rather than a LocationListener. This is useful if you want to be notified of changes in your position even when your code is not running. For example, if you are logging movements, you could use a PendingIntent that triggers a BroadcastReceiver (getBroadcast()) and have the BroadcastReceiver add the entry to the log. This way, your code is in memory only when the position changes, so you do not tie up system resources while the device is not moving.

Are We There Yet? Are We There Yet?

Sometimes, you are not interested in where you are now, or even when you move, but want to know when you get to where you are going. This could be an end destination, or it could be getting to the next step on a set of directions, so you can give the user the next instruction.

To accomplish this, LocationManager offers addProximityAlert(). This registers a PendingIntent, which will be fired off when the device gets within a certain distance of a certain location. The addProximityAlert() method takes the following as parameters:

  • The latitude and longitude of the position of interest.
  • A radius, specifying how close you should be to that position for the Intent to be raised.
  • A duration for the registration, in milliseconds. After this period, the registration automatically lapses. A value of -1 means the registration lasts until you manually remove it via removeProximityAlert().
  • The PendingIntent to be raised when the device is within the target zone expressed by the position and radius.

Note that it is not guaranteed that you will actually receive an Intent. There may be an interruption in location services, or the device may not be in the target zone during the period of time the proximity alert is active. For example, if the position is off by a bit, and the radius is a little too tight, the device might only skirt the edge of the target zone, or it might go by the target zone so quickly that the device’s location isn’t sampled during that time.

It is up to you to arrange for an activity or receiver to respond to the Intent you register with the proximity alert. What you do when the Intent arrives is up to you. For example, you might set up a notification (e.g., vibrate the device), log the information to a content provider, or post a message to a web site. Note that you will receive the Intent whenever the position is sampled and you are within the target zone, not just upon entering the zone. Hence, you may get the Intent several times, perhaps quite a few times, depending on the size of the target zone and the speed of the device’s movement.

Testing... Testing...

The Android emulator does not have the ability to get a fix from GPS, triangulate your position from cell towers, or identify your location by some nearby Wi-Fi signal. So, if you want to simulate a moving device, you will need to have some means of providing mock location data to the emulator.

For whatever reason, this particular area has undergone significant changes as Android itself has evolved. It used to be that you could provide mock location data within your application, which was very handy for demonstration purposes. Alas, those options were all removed as of Android 1.0.

One option for supplying mock location data is the Dalvik Debug Monitor Service (DDMS). This is an external program, separate from the emulator, which can feed the emulator single location points or full routes to traverse, in a few different formats. There is a specific permission to include in your manifest file called ACCESS_MOCK_LOCATION, to enable access to the data. You will also need to ensure that the “Allow mock locations” option is enabled under Developer Settings for your emulator. The “Step 6:Set Up the Device” section in Chapter 2 explains how to access those settings. DDMS itself is described in greater detail in Chapter 43.

..................Content has been hidden....................

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