diff options
| -rw-r--r-- | main/src/cgeo/geocaching/CgeoApplication.java | 12 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/MainActivity.java | 3 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/maps/CGeoMap.java | 2 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/playservices/LocationProvider.java | 5 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/sensors/GeoData.java | 63 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/sensors/GeoDataProvider.java | 129 |
6 files changed, 105 insertions, 109 deletions
diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java index 556acb8..73eaaee 100644 --- a/main/src/cgeo/geocaching/CgeoApplication.java +++ b/main/src/cgeo/geocaching/CgeoApplication.java @@ -16,6 +16,8 @@ import cgeo.geocaching.utils.RxUtils; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; +import org.eclipse.jdt.annotation.NonNull; + import rx.Observable; import rx.functions.Action1; import rx.functions.Func1; @@ -35,13 +37,15 @@ public class CgeoApplication extends Application { private Observable<IGeoData> geoDataObservableLowPower; private Observable<Float> directionObservable; private Observable<Status> gpsStatusObservable; - private volatile IGeoData currentGeo = GeoData.dummyLocation(); + @NonNull private volatile IGeoData currentGeo = GeoData.DUMMY_LOCATION; + private volatile boolean hasValidLocation = false; private volatile float currentDirection = 0.0f; private boolean isGooglePlayServicesAvailable = false; private final Action1<IGeoData> rememberGeodataAction = new Action1<IGeoData>() { @Override public void call(final IGeoData geoData) { currentGeo = geoData; + hasValidLocation = true; } }; @@ -82,6 +86,7 @@ public class CgeoApplication extends Application { } // ensure initialization of lists DataStore.getLists(); + // Check if Google Play services is available if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS) { isGooglePlayServicesAvailable = true; @@ -150,10 +155,15 @@ public class CgeoApplication extends Application { return gpsStatusObservable; } + @NonNull public IGeoData currentGeo() { return currentGeo; } + public boolean hasValidLocation() { + return hasValidLocation; + } + public Float distanceNonBlocking(final ICoordinates target) { if (target.getCoords() == null) { return null; diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java index 8377091..7049712 100644 --- a/main/src/cgeo/geocaching/MainActivity.java +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -213,6 +213,9 @@ public class MainActivity extends AbstractActionBarActivity { super.onResume(locationUpdater.start(GeoDirHandler.UPDATE_GEODATA | GeoDirHandler.LOW_POWER), app.gpsStatusObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(satellitesHandler)); updateUserInfoHandler.sendEmptyMessage(-1); + if (app.hasValidLocation()) { + locationUpdater.updateGeoData(app.currentGeo()); + } startBackgroundLogin(); init(); } diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index c9894e2..4b91fff 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -898,7 +898,7 @@ public class CGeoMap extends AbstractMap implements ViewFactory { // minimum change of location in fraction of map width/height (whatever is smaller) for position overlay update private static final float MIN_LOCATION_DELTA = 0.01f; - Location currentLocation = new Location(""); + Location currentLocation = CgeoApplication.getInstance().currentGeo().getLocation(); float currentHeading; private long timeLastPositionOverlayCalculation = 0; diff --git a/main/src/cgeo/geocaching/playservices/LocationProvider.java b/main/src/cgeo/geocaching/playservices/LocationProvider.java index 4035078..ab2c057 100644 --- a/main/src/cgeo/geocaching/playservices/LocationProvider.java +++ b/main/src/cgeo/geocaching/playservices/LocationProvider.java @@ -124,7 +124,10 @@ public class LocationProvider implements ConnectionCallbacks, OnConnectionFailed * @param context the context used to retrieve the system services */ private LocationProvider(final Context context) { - subject.onNext(GeoData.dummyLocation()); + final IGeoData initialLocation = GeoData.getInitialLocation(context); + if (initialLocation != null) { + subject.onNext(initialLocation); + } locationClient = new LocationClient(context, this, this); locationClient.connect(); } diff --git a/main/src/cgeo/geocaching/sensors/GeoData.java b/main/src/cgeo/geocaching/sensors/GeoData.java index 1291d3c..fabf0f6 100644 --- a/main/src/cgeo/geocaching/sensors/GeoData.java +++ b/main/src/cgeo/geocaching/sensors/GeoData.java @@ -2,20 +2,44 @@ package cgeo.geocaching.sensors; import cgeo.geocaching.enumerations.LocationProviderType; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.utils.Log; +import android.content.Context; import android.location.Location; import android.location.LocationManager; +import javax.annotation.Nullable; + public class GeoData extends Location implements IGeoData { public static final String INITIAL_PROVIDER = "initial"; public static final String FUSED_PROVIDER = "fused"; public static final String LOW_POWER_PROVIDER = "low-power"; + // Some devices will not have the last position available (for example the emulator). In this case, + // rather than waiting forever for a position update which might never come, we emulate it by placing + // the user arbitrarly at Paris Notre-Dame, one of the most visited free tourist attractions in the world. + final public static GeoData DUMMY_LOCATION = new GeoData(new Location(INITIAL_PROVIDER)); + static { + DUMMY_LOCATION.setLatitude(48.85308); + DUMMY_LOCATION.setLongitude(2.34962); + } + public GeoData(final Location location) { super(location); } + @Nullable + static Location best(@Nullable final Location gpsLocation, @Nullable final Location netLocation) { + if (isRecent(gpsLocation) || !(netLocation != null)) { + return gpsLocation; + } + if (!(gpsLocation != null)) { + return netLocation; + } + return gpsLocation.getTime() >= netLocation.getTime() ? gpsLocation : netLocation; + } + @Override public Location getLocation() { return this; @@ -48,13 +72,36 @@ public class GeoData extends Location implements IGeoData { return new Geopoint(this); } - // Some devices will not have the last position available (for example the emulator). In this case, - // rather than waiting forever for a position update which might never come, we emulate it by placing - // the user arbitrarly at Paris Notre-Dame, one of the most visited free tourist attractions in the world. - public static GeoData dummyLocation() { - final Location location = new Location(INITIAL_PROVIDER); - location.setLatitude(48.85308); - location.setLongitude(2.34962); - return new GeoData(location); + @Nullable public static GeoData getInitialLocation(final Context context) { + final LocationManager geoManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + if (geoManager == null) { + Log.w("No LocationManager available"); + return null; + } + try { + // Try to find a sensible initial location from the last locations known to Android. + final Location lastGpsLocation = geoManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); + final Location lastNetworkLocation = geoManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + final Location bestLocation = best(lastGpsLocation, lastNetworkLocation); + if (bestLocation != null) { + bestLocation.setProvider(INITIAL_PROVIDER); + return new GeoData(bestLocation); + } else { + Log.i("No last known location available"); + return null; + } + } catch (final Exception e) { + // This error is non-fatal as its only consequence is that we will start with a dummy location + // instead of a previously known one. + Log.e("Error when retrieving last known location", e); + return null; + } } + + + + public static boolean isRecent(@Nullable final Location location) { + return location != null && System.currentTimeMillis() <= location.getTime() + 30000; + } + } diff --git a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java index 19eef6a..4694c62 100644 --- a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java +++ b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java @@ -3,6 +3,8 @@ package cgeo.geocaching.sensors; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.RxUtils.LooperCallbacks; +import org.apache.commons.lang3.StringUtils; + import rx.Observable; import android.content.Context; @@ -15,29 +17,11 @@ import java.util.concurrent.TimeUnit; public class GeoDataProvider extends LooperCallbacks<IGeoData> { + private final Context context; private final LocationManager geoManager; - private final LocationData gpsLocation = new LocationData(); - private final LocationData netLocation = new LocationData(); - private final Listener networkListener = new Listener(LocationManager.NETWORK_PROVIDER, netLocation); - private final Listener gpsListener = new Listener(LocationManager.GPS_PROVIDER, gpsLocation); - - private static class LocationData { - public Location location; - public long timestamp = 0; - - public void update(final Location location) { - this.location = location; - timestamp = System.currentTimeMillis(); - } - - public boolean isRecent() { - return isValid() && System.currentTimeMillis() < timestamp + 30000; - } - - public boolean isValid() { - return location != null; - } - } + private Location latestGPSLocation = null; + private final Listener networkListener = new Listener(); + private final Listener gpsListener = new Listener(); /** * Build a new geo data provider object. @@ -49,7 +33,8 @@ public class GeoDataProvider extends LooperCallbacks<IGeoData> { */ protected GeoDataProvider(final Context context) { super(2500, TimeUnit.MILLISECONDS); - geoManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + this.context = context.getApplicationContext(); + geoManager = (LocationManager) this.context.getSystemService(Context.LOCATION_SERVICE); } public static Observable<IGeoData> create(final Context context) { @@ -58,14 +43,20 @@ public class GeoDataProvider extends LooperCallbacks<IGeoData> { @Override public void onStart() { - subscriber.onNext(findInitialLocation()); + final IGeoData initialLocation = GeoData.getInitialLocation(context); + if (initialLocation != null) { + subscriber.onNext(initialLocation); + } Log.d("GeoDataProvider: starting the GPS and network listeners"); - for (final Listener listener : new Listener[]{networkListener, gpsListener}) { - try { - geoManager.requestLocationUpdates(listener.locationProvider, 0, 0, listener); - } catch (final Exception e) { - Log.w("There is no location provider " + listener.locationProvider, e); - } + try { + geoManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, gpsListener); + } catch (final Exception e) { + Log.w("No GPS provider available", e); + } + try { + geoManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, networkListener); + } catch (final Exception e) { + Log.w("No network provider available", e); } } @@ -76,50 +67,16 @@ public class GeoDataProvider extends LooperCallbacks<IGeoData> { geoManager.removeUpdates(gpsListener); } - private IGeoData findInitialLocation() { - final Location initialLocation = new Location(GeoData.INITIAL_PROVIDER); - try { - // Try to find a sensible initial location from the last locations known to Android. - final Location lastGpsLocation = geoManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); - final Location lastNetworkLocation = geoManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); - - // If both providers are non-null, take the most recent one - if (lastGpsLocation != null && lastNetworkLocation != null) { - if (lastGpsLocation.getTime() >= lastNetworkLocation.getTime()) { - copyCoords(initialLocation, lastGpsLocation); - } else { - copyCoords(initialLocation, lastNetworkLocation); - } - } else if (lastGpsLocation != null) { - copyCoords(initialLocation, lastGpsLocation); - } else if (lastNetworkLocation != null) { - copyCoords(initialLocation, lastNetworkLocation); - } else { - Log.i("GeoDataProvider: no last known location available"); - return GeoData.dummyLocation(); - } - } catch (final Exception e) { - // This error is non-fatal as its only consequence is that we will start with a dummy location - // instead of a previously known one. - Log.e("GeoDataProvider: error when retrieving last known location", e); - } - // Start with an historical GeoData just in case someone queries it before we get - // a chance to get any information. - return new GeoData(initialLocation); - } - - private static void copyCoords(final Location target, final Location source) { - target.setLatitude(source.getLatitude()); - target.setLongitude(source.getLongitude()); - } - private class Listener implements LocationListener { - private final String locationProvider; - private final LocationData locationData; - Listener(final String locationProvider, final LocationData locationData) { - this.locationProvider = locationProvider; - this.locationData = locationData; + @Override + public void onLocationChanged(final Location location) { + if (StringUtils.equals(location.getProvider(), LocationManager.GPS_PROVIDER)) { + latestGPSLocation = location; + assign(latestGPSLocation); + } else { + assign(GeoData.best(latestGPSLocation, location)); + } } @Override @@ -136,35 +93,11 @@ public class GeoDataProvider extends LooperCallbacks<IGeoData> { public void onProviderEnabled(final String provider) { // nothing } - - @Override - public void onLocationChanged(final Location location) { - locationData.update(location); - selectBest(); - } - } - - private LocationData best() { - if (gpsLocation.isRecent() || !netLocation.isValid()) { - return gpsLocation.isValid() ? gpsLocation : null; - } - if (!gpsLocation.isValid()) { - return netLocation; - } - return gpsLocation.timestamp > netLocation.timestamp ? gpsLocation : netLocation; } - private void selectBest() { - assign(best()); - } - - private void assign(final LocationData locationData) { - if (locationData == null) { - return; - } - + private void assign(final Location location) { // We do not necessarily get signalled when satellites go to 0/0. - final IGeoData current = new GeoData(locationData.location); + final IGeoData current = new GeoData(location); subscriber.onNext(current); } |
