How to get last location on Glass reliably?
Asked Answered
S

3

7

Method getLastLocation() from LocationManager often return null and it's quite tricky to select best provider. The documentation says:

Warning: Do not use the LocationManager.getBestProvider() method or the constants GPS_PROVIDER or NETWORK_PROVIDER to listen for location updates. Glass uses a dynamic set of providers and listening only to a single provider may cause your application to miss location updates.

How to get best last location?

Soursop answered 26/1, 2014 at 14:34 Comment(0)
S
8

Because Glass uses dynamic set of providers, you need to get location from all of them and select the location with best accuracy:

 public static Location getLastLocation(Context context) {
      LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
      Criteria criteria = new Criteria();
      criteria.setAccuracy(Criteria.NO_REQUIREMENT);
      List<String> providers = manager.getProviders(criteria, true);
      List<Location> locations = new ArrayList<Location>();
      for (String provider : providers) {
           Location location = manager.getLastKnownLocation(provider);
           if (location != null && location.getAccuracy()!=0.0) {
               locations.add(location);
           }
      }
      Collections.sort(locations, new Comparator<Location>() {
          @Override
          public int compare(Location location, Location location2) {
              return (int) (location.getAccuracy() - location2.getAccuracy());
          }
      });
      if (locations.size() > 0) {
          return locations.get(0);
      }
      return null;
 }
Soursop answered 26/1, 2014 at 14:34 Comment(2)
I am getting null value in my glass. Am I missing something?Notification
On one of the lines, I believe that location.getAccuracy should be location.getAccuracy()Klaus
D
4

Destil's answer above correctly handles the case where at least one provider returns a valid location for getLastKnownLocation().

However, I've also seen Glass return null for getLastKnownLocation() for all providers (in XE16 in particular).

In this case, your only option is to register a LocationListener and wait for a new location update.

For example, in context of getting a location when creating a new Activity, it would look like the following:

public class MyActivity extends Activity implements LocationListener {
...
LocationManager mLocationManager;
Location mLastKnownLocation;

@Override
protected void onCreate(Bundle savedInstanceState) {
    // Activity setup
    ...
    // Use Destil's answer to get last known location, using all providers
    mLastKnownLocation = getLastLocation(this);

    if (mLastKnownLocation != null) {
        // Do something with location
        doSomethingWithLocation(mLastKnownLocation);
    } else {
        // All providers returned null - start a LocationListener to force a refresh of location
        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        List<String> providers = mLocationManager.getProviders(true);
        for (Iterator<String> i = providers.iterator(); i.hasNext(); ) {
            mLocationManager.requestLocationUpdates(i.next(), 0, 0, this);
        }
    }
    ...
}
...
}

You'll then need to handle the LocationListener callbacks:

@Override
public void onLocationChanged(Location location) {
    if (mLastKnownLocation == null) {
        // At least one location should be available now
        // Use Destil's answer to get last known location again, using all providers
        mLastKnownLocation = getLastLocation(this);

        if (mLastKnownLocation == null) {
            // This shouldn't happen if LocationManager is saving locations correctly, but if it does, use the location that was just passed in
            mLastKnownLocation = location;
        }

        // Stop listening for updates
        mLocationManager.removeUpdates(this);

        // Do something with location
        doSomethingWithLocation(mLastKnownLocation);
    }
}

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

@Override
public void onProviderEnabled(String provider) {}

@Override
public void onProviderDisabled(String provider) {}

It can be a little tricky to change to the asynchronous model to avoid blocking the UI thread while waiting for the update, and it may require moving some of your app logic around.

Duston answered 22/4, 2014 at 16:12 Comment(0)
R
1

The code in the answer should be adjusted to handle an accuracy of "0.0" which represents "NO Accuracy" known!

Here is an alternative that includes this adjustment

public static Location getLastLocation(Context context) {
    Location result = null;
    LocationManager locationManager;
    Criteria locationCriteria;
    List<String> providers;

    locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    locationCriteria = new Criteria();
    locationCriteria.setAccuracy(Criteria.NO_REQUIREMENT);
    providers = locationManager.getProviders(locationCriteria, true);

    // Note that providers = locatoinManager.getAllProviders(); is not used because the
    // list might contain disabled providers or providers that are not allowed to be called.

    //Note that getAccuracy can return 0, indicating that there is no known accuracy.

    for (String provider : providers) {
        Location location = locationManager.getLastKnownLocation(provider);
        if (result == null) {
            result = location;
        } 
        else if (result.getAccuracy() == 0.0) {
            if (location.getAccuracy() != 0.0) {
                result = location;
                break;
            } else {
                if (result.getAccuracy() > location.getAccuracy()) {
                    result = location;
                }
            }
        }
    }

    return result;
}
Regurgitation answered 7/2, 2014 at 2:50 Comment(1)
I am getting null value for the location. Am I missing something?Notification

© 2022 - 2024 — McMap. All rights reserved.