Monday, July 2, 2012

Android - Location Manager

Hello


Users bring their mobile devices with them almost everywhere. One of the unique features available to mobile applications is location awareness. Knowing the location and using the information wisely can bring a more contextual experience to your users.
This class teaches you how to incorporate location based services in your Android application. You'll learn a number of methods to receive location updates and related best practices.

1. Using the Location Manager

Before your application can begin receiving location updates, it needs to perform some simple steps to set up access. In this lesson, you'll learn what these steps entail.

Declare Proper Permissions in Android Manifest

The first step of setting up location update access is to declare proper permissions in the manifest. If permissions are missing, the application will get a SecurityException at runtime.
Depending on the LocationManager methods used, either ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission is needed. For example, you need to declare the ACCESS_COARSE_LOCATION permission if your application uses a network-based location provider only. The more accurate GPS requires the ACCESS_FINE_LOCATION permission. Note that declaring the ACCESS_FINE_LOCATION permission implies ACCESS_COARSE_LOCATION already.
Also, if a network-based location provider is used in the application, you'll need to declare the internet permission as well.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />


Get a Reference to LocationManager

LocationManager is the main class through which your application can access location services on Android. Similar to other system services, a reference can be obtained from calling the getSystemService() method. If your application intends to receive location updates in the foreground (within an Activity), you should usually perform this step in the onCreate() method.
LocationManager locationManager =
        (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

Pick a Location Provider

While not required, most modern Android-powered devices can receive location updates through multiple underlying technologies, which are abstracted to an application as LocationProvider objects. Location providers may have different performance characteristics in terms of time-to-fix, accuracy, monetary cost, power consumption, and so on. Generally, a location provider with a greater accuracy, like the GPS, requires a longer fix time than a less accurate one, such as a network-based location provider.
Depending on your application's use case, you have to choose a specific location provider, or multiple providers, based on similar tradeoffs. For example, a points of interest check-in application would require higher location accuracy than say, a retail store locator where a city level location fix would suffice. The snippet below asks for a provider backed by the GPS.
LocationProvider provider =
        locationManager.getProvider(LocationManager.GPS_PROVIDER);
Alternatively, you can provide some input criteria such as accuracy, power requirement, monetary cost, and so on, and let Android decide a closest match location provider. The snippet below asks for a location provider with fine accuracy and no monetary cost. Note that the criteria may not resolve to any providers, in which case a null will be returned. Your application should be prepared to gracefully handle the situation.


// Retrieve a list of location providers that have fine accuracy, no monetary cost, etc
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setCostAllowed(false);
...
String providerName = locManager.getBestProvider(criteria, true);

// If no suitable provider is found, null is returned.
if (providerName != null) {
   ...
}

Verify the Location Provider is Enabled


Some location providers such as the GPS can be disabled in Settings. It is good practice to check whether the desired location provider is currently enabled by calling the isProviderEnabled() method. If the location provider is disabled, you can offer the user an opportunity to enable it in Settings by firing an Intent with the ACTION_LOCATION_SOURCE_SETTINGS action.
In this sample the GPS provider is queried and the user is given AlertDialog in case the provider is not enabled
  

Sample source
This sample demonstrate simple use of LocationManager using GPS service.
It uses LocationManager inside onStart().  Longitude\Latitude are writen to the GUI in case GPS is enabled ,otherwise a AlertDialog is issued and the refresh button is disabled.
The user can click the Refresh button to get fresh location

LocaltionApp.zip

MainActivity.java

package com.example.localtionapp;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    
    @Override
    protected void onStart() {
        super.onStart();

        try {
   // This verification should be done during onStart() because the system calls
   // this method when the user returns to the activity, which ensures the desired
   // location provider is enabled each time the activity resumes from the stopped state.
   m_LocationManager =
           (LocationManager) getSystemService(Context.LOCATION_SERVICE);
   final boolean gpsEnabled = 
     m_LocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

   if (gpsEnabled) 
   {
    updateGuiLocation();
   }
   else
   {
    Button button = (Button) findViewById(R.id.ButtonRefresh);
    button.setEnabled(false);
    alert();
   }
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
    }


 private void updateGuiLocation() {
  double fLatitiude=0,fLongitude=0;
  Location loc = m_LocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

  if(loc != null) // can be null even if GPS is enabled !
  {
   fLatitiude = loc.getLatitude();
   fLongitude= loc.getLongitude();
  }
  EditText editText = (EditText) findViewById(R.id.editTextLatitiude);
  editText.setText(String.format("%1$,f", fLatitiude));
  
  editText = (EditText) findViewById(R.id.editTextLongitiude);
  editText.setText(String.format("%1$,f", fLongitude));
 }

 @SuppressWarnings("deprecation")
 private void alert() {
  showDialog(ALERT_DIALOG_ID);
 }
    
 @Override
    protected  Dialog onCreateDialog(int id, Bundle args)
 {
        switch(id)
        {
        case ALERT_DIALOG_ID:
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("Please enable GPS service");
            builder.setTitle("GPS problem");
            AlertDialog alert = builder.create();
            return(alert);
        }
        
        return null;
 }
 
 /** Called when the user selects the Refresh button */
 public void Refresh(View view) {
  updateGuiLocation();
 }
 
    final int ALERT_DIALOG_ID = 0;
    LocationManager m_LocationManager;
  }


activity_main.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
android:orientation="vertical">

 
    <TextView
        android:id="@+id/textViewLatitiude"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/Latitiude"
        tools:context=".MainActivity" />
     
        <EditText
        android:id="@+id/editTextLatitiude"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:context=".MainActivity" />
     
     
 
        <TextView
        android:id="@+id/textViewLongitiude"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/Longitiude"
        tools:context=".MainActivity" />
     
     
<EditText
        android:id="@+id/editTextLongitiude"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:context=".MainActivity" />
     
     <Button
        android:id="@+id/ButtonRefresh"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button_refresh"
        android:onClick="Refresh"  />

</LinearLayout>


AndroidManifest.xml



Run the application to create :



While running the app on your android phone you should be able to see the Latitude \ Longitude.
Clicking Refresh bring fresh location.

2. Obtaining the Current Location Using LocationListener

After setting up your application to work with LocationManager, you can begin to obtain location updates


Set Up the Location Listener

The LocationManager class exposes a number of methods for applications to receive location updates. In its simplest form, you register an event listener, identify the location manager from which you'd like to receive location updates, and specify the minimum time and distance intervals at which to receive location updates. The onLocationChanged() callback will be invoked with the frequency that correlates with time and distance intervals.
In the sample code snippet below, the location listener is set up to receive notifications at least every 10 seconds and if the device moves by more than 10 meters. The other callback methods notify the application any status change coming from the location provider.


private
final LocationListener listener = new LocationListener() { @Override public void onLocationChanged(Location location) { // A new location update is received. Do something useful with it. ...... } ... }; mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, // 10-second interval. 10, // 10 meters. listener);


Handle Multiple Sources of Location Updates

Generally speaking, a location provider with greater accuracy (GPS) requires a longer fix time than one with lower accuracy (network-based). If you want to display location data as quickly as possible and update it as more accurate data becomes available, a common practice is to register a location listener with both GPS and network providers. In the onLocationChanged() callback, you'll receive location updates from multiple location providers that may have different timestamps and varying levels of accuracy. You'll need to incorporate logic to disambiguate the location providers and discard updates that are stale and less accurate. 


Use getLastKnownLocation() Wisely

The setup time for getting a reasonable location fix may not be acceptable for certain applications. You should consider calling the getLastKnownLocation() method which simply queries Android for the last location update previously received by any location providers. Keep in mind that the returned location may be stale. You should check the timestamp and accuracy of the returned location and decide whether it is useful for your application. If you elect to discard the location update returned from getLastKnownLocation() and wait for fresh updates from the location provider(s), you should consider displaying an appropriate message before location data is received.



Terminate Location Updates

When you are done with using location data, you should terminate location update to reduce unnecessary consumption of power and network bandwidth. For example, if the user navigates away from an activity where location updates are displayed, you should stop location update by calling removeUpdates() in onStop(). (onStop() is called when the activity is no longer visible. If you want to learn more about activity lifecycle, read up on the Stopping and Restarting an Activity lesson.
protected void onStop() {
    super.onStop();
    mLocationManager.removeUpdates(listener);
}
Note: For applications that need to continuously receive and process location updates like a near-real time mapping application, it is best to incorporate the location update logic in a background service and make use of the system notification bar to make the user aware that location data is being used.


3. Displaying the Location Address



As shown in previous section, location updates are received in the form of latitude and longitude coordinates. While this format is useful for calculating distance or displaying a pushpin on a map, the decimal numbers make no sense to most end users. If you need to display a location to user, it is much more preferable to display the address instead.


Perform Reverse Geocoding

Reverse-geocoding is the process of translating latitude longitude coordinates to a human-readable address. The Geocoder API is available for this purpose. Note that behind the scene, the API is dependent on a web service. If such service is unavailable on the device, the API will throw a "Service not Available exception" or return an empty list of addresses. A helper method called isPresent() was added in Android 2.3 (API level 9) to check for the existence of the service.
The following code snippet demonstrates the use of the Geocoder API to perform reverse-geocoding. 
        // Bypass reverse-geocoding if the Geocoder service is not available on the
        // device. The isPresent() convenient method is only available on Gingerbread or above.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD && Geocoder.isPresent()) {
            // ;
        }
};

// AsyncTask encapsulating the reverse-geocoding API.  Since the geocoder API is blocked,
// we do not want to invoke it from the UI thread.
private class ReverseGeocodingTask extends AsyncTask<Location, Void, Void> {
    Context mContext;

    public ReverseGeocodingTask(Context context) {
        super();
        mContext = context;
    }

    @Override
    protected Void doInBackground(Location... params) {
        Geocoder geocoder = new Geocoder(mContext, Locale.getDefault());

        Location loc = params[0];
        List<Address> addresses = null;
        try {
            // Call the synchronous getFromLocation() method by passing in the lat/long values.
            addresses = geocoder.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);
        } catch (IOException e) {
            e.printStackTrace();
            // Update UI field with the exception.
            Message.obtain(mHandler, UPDATE_ADDRESS, e.toString()).sendToTarget();
        }
        if (addresses != null &s;&s; addresses.size() > 0) {
            Address address = addresses.get(0);
            // Format the first line of address (if available), city, and country name.
            String addressText = String.format("%s, %s, %s",
                    address.getMaxAddressLineIndex() > 0 ? address.getAddressLine(0) : "",
                    address.getLocality(),
                    address.getCountryName());
            // Update the UI via a message handler.
            Message.obtain(mHandler, UPDATE_ADDRESS, addressText).sendToTarget();
        }
        return null;
    }
}



Providing Mock Location Data


Emulator

As you develop your application, you'll certainly need to test how well your model for obtaining user location works. This is most easily done using a real Android-powered device. If, however, you don't have a device, you can still test your location-based features by mocking location data in the Android emulator. There are three different ways to send your application mock location data: using Eclipse, DDMS, or the "geo" command in the emulator console.

Remarks :

  • Providing mock location data is injected as GPS location data, so you must request location updates from GPS_PROVIDER in order for mock location data to work.
  • Make sure you create AVD with GPS support


Using Eclipse

Select Window > Show View > Other > Emulator Control.
In the Emulator Control panel, enter GPS coordinates under Location Controls as individual lat/long coordinates, with a GPX file for route playback, or a KML file for multiple place marks. (Be sure that you have a device selected in the Devices panel—available from Window > Show View > Other > Devices.)







Using the "geo" command in the emulator console

To send mock location data from the command line:
  1. Launch your application in the Android emulator and open a terminal/console in your SDK's /tools directory.
  2. Connect to the emulator console:
    telnet localhost <console-port>
  3. Send the location data:
    • geo fix to send a fixed geo-location.
      This command accepts a longitude and latitude in decimal degrees, and an optional altitude in meters. For example:
      geo fix -121.45356 46.51119 4392
    • geo nmea to send an NMEA 0183 sentence.
      This command accepts a single NMEA sentence of type '$GPGGA' (fix data) or '$GPRMC' (transit data). For example:
      geo nmea $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
remark :

  • look at the console window to see the connect port



Test Provider
One can use the following :
void addTestProvider (String name, boolean requiresNetwork, boolean requiresSatellite, boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) :

  • should be called once or test for existence


setTestProviderEnabled(String provider, boolean enabled)

setTestProviderLocation(String provider, Location loc)

removeTestProvider (String provider)

One must add
 <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
to the AndroidManifest.xml inside the manifest tag

code for Mock appears here 


The following app was published using this post.

source - http://developer.android.com/training/basics/location/index.html

Nathan

No comments:

Post a Comment