aboutsummaryrefslogtreecommitdiffstats
path: root/main/src/cgeo/geocaching/sensors/GeoDataProvider.java
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/cgeo/geocaching/sensors/GeoDataProvider.java')
-rw-r--r--main/src/cgeo/geocaching/sensors/GeoDataProvider.java255
1 files changed, 38 insertions, 217 deletions
diff --git a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java
index a4799cb..dab05d0 100644
--- a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java
+++ b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java
@@ -1,25 +1,13 @@
package cgeo.geocaching.sensors;
import cgeo.geocaching.utils.Log;
-import cgeo.geocaching.utils.StartableHandlerThread;
+import cgeo.geocaching.utils.RxUtils.LooperCallbacks;
import org.apache.commons.lang3.StringUtils;
import rx.Observable;
-import rx.Observable.OnSubscribe;
-import rx.Subscriber;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.functions.Action0;
-import rx.functions.Action1;
-import rx.observables.ConnectableObservable;
-import rx.subjects.BehaviorSubject;
-import rx.subscriptions.CompositeSubscription;
-import rx.subscriptions.Subscriptions;
import android.content.Context;
-import android.location.GpsSatellite;
-import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -27,40 +15,13 @@ import android.os.Bundle;
import java.util.concurrent.TimeUnit;
-public class GeoDataProvider implements OnSubscribe<IGeoData> {
+public class GeoDataProvider extends LooperCallbacks<GeoData> {
- private static final String LAST_LOCATION_PSEUDO_PROVIDER = "last";
+ private final Context context;
private final LocationManager geoManager;
- private final LocationData gpsLocation = new LocationData();
- private final LocationData netLocation = new LocationData();
- private final BehaviorSubject<IGeoData> subject;
- private static final StartableHandlerThread handlerThread =
- new StartableHandlerThread("GeoDataProvider thread", android.os.Process.THREAD_PRIORITY_BACKGROUND);
- static {
- handlerThread.start();
- }
-
- public boolean gpsEnabled = false;
- public int satellitesVisible = 0;
- public int satellitesFixed = 0;
-
- 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.
@@ -71,117 +32,51 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> {
* @param context the context used to retrieve the system services
*/
protected GeoDataProvider(final Context context) {
- geoManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
- subject = BehaviorSubject.create(findInitialLocation());
+ super(2500, TimeUnit.MILLISECONDS);
+ this.context = context.getApplicationContext();
+ geoManager = (LocationManager) this.context.getSystemService(Context.LOCATION_SERVICE);
}
- public static Observable<IGeoData> create(final Context context) {
- final GeoDataProvider provider = new GeoDataProvider(context);
- return provider.worker.refCount();
+ public static Observable<GeoData> create(final Context context) {
+ return Observable.create(new GeoDataProvider(context)).onBackpressureDrop();
}
@Override
- public void call(final Subscriber<? super IGeoData> subscriber) {
- subject.subscribe(subscriber);
- }
-
- final ConnectableObservable<IGeoData> worker = new ConnectableObservable<IGeoData>(this) {
- private int debugSessionCounter = 0;
-
- private final Object lock = new Object();
- private int count = 0;
-
- final private GpsStatus.Listener gpsStatusListener = new GpsStatusListener();
- final private Listener networkListener = new Listener(LocationManager.NETWORK_PROVIDER, netLocation);
- final private Listener gpsListener = new Listener(LocationManager.GPS_PROVIDER, gpsLocation);
-
- @Override
- public void connect(Action1<? super Subscription> connection) {
- final CompositeSubscription subscription = new CompositeSubscription();
- AndroidSchedulers.handlerThread(handlerThread.getHandler()).createWorker().schedule(new Action0() {
- @Override
- public void call() {
- synchronized(lock) {
- if (count++ == 0) {
- Log.d("GeoDataProvider: starting the GPS and network listeners" + " (" + ++debugSessionCounter + ")");
- geoManager.addGpsStatusListener(gpsStatusListener);
- 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);
- }
- }
- }
- }
-
- subscription.add(Subscriptions.create(new Action0() {
- @Override
- public void call() {
- AndroidSchedulers.handlerThread(handlerThread.getHandler()).createWorker().schedule(new Action0() {
- @Override
- public void call() {
- synchronized (lock) {
- if (--count == 0) {
- Log.d("GeoDataProvider: stopping the GPS and network listeners" + " (" + debugSessionCounter + ")");
- geoManager.removeUpdates(networkListener);
- geoManager.removeUpdates(gpsListener);
- geoManager.removeGpsStatusListener(gpsStatusListener);
- }
- }
- }
- }, 2500, TimeUnit.MILLISECONDS);
- }
- }));
- }
- });
- connection.call(subscription);
+ public void onStart() {
+ final GeoData initialLocation = GeoData.getInitialLocation(context);
+ if (initialLocation != null) {
+ subject.onNext(initialLocation);
}
- };
-
- private IGeoData findInitialLocation() {
- final Location initialLocation = new Location(LAST_LOCATION_PSEUDO_PROVIDER);
+ Log.d("GeoDataProvider: starting the GPS and network listeners");
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");
- }
+ geoManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, gpsListener);
+ } catch (final Exception e) {
+ Log.w("Unable to create GPS location provider: " + e.getMessage());
+ }
+ try {
+ geoManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, networkListener);
} 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);
+ Log.w("Unable to create network location provider: " + e.getMessage());
}
- // Start with an historical GeoData just in case someone queries it before we get
- // a chance to get any information.
- return new GeoData(initialLocation, false, 0, 0, true);
}
- private static void copyCoords(final Location target, final Location source) {
- target.setLatitude(source.getLatitude());
- target.setLongitude(source.getLongitude());
+ @Override
+ protected void onStop() {
+ Log.d("GeoDataProvider: stopping the GPS and network listeners");
+ geoManager.removeUpdates(networkListener);
+ geoManager.removeUpdates(gpsListener);
}
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
@@ -198,85 +93,11 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> {
public void onProviderEnabled(final String provider) {
// nothing
}
-
- @Override
- public void onLocationChanged(final Location location) {
- locationData.update(location);
- selectBest();
- }
- }
-
- private final class GpsStatusListener implements GpsStatus.Listener {
-
- @Override
- public void onGpsStatusChanged(final int event) {
- boolean changed = false;
- switch (event) {
- case GpsStatus.GPS_EVENT_FIRST_FIX:
- case GpsStatus.GPS_EVENT_SATELLITE_STATUS: {
- final GpsStatus status = geoManager.getGpsStatus(null);
- int visible = 0;
- int fixed = 0;
- for (final GpsSatellite satellite : status.getSatellites()) {
- if (satellite.usedInFix()) {
- fixed++;
- }
- visible++;
- }
- if (visible != satellitesVisible || fixed != satellitesFixed) {
- satellitesVisible = visible;
- satellitesFixed = fixed;
- changed = true;
- }
- break;
- }
- case GpsStatus.GPS_EVENT_STARTED:
- if (!gpsEnabled) {
- gpsEnabled = true;
- changed = true;
- }
- break;
- case GpsStatus.GPS_EVENT_STOPPED:
- if (gpsEnabled) {
- gpsEnabled = false;
- satellitesFixed = 0;
- satellitesVisible = 0;
- changed = true;
- }
- break;
- default:
- throw new IllegalStateException();
- }
-
- if (changed) {
- 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 int visible = gpsLocation.isRecent() ? satellitesVisible : 0;
- final boolean pseudoLocation = StringUtils.equals(locationData.location.getProvider(), LAST_LOCATION_PSEUDO_PROVIDER);
- final IGeoData current = new GeoData(locationData.location, gpsEnabled, visible, satellitesFixed, pseudoLocation);
+ final GeoData current = new GeoData(location);
subject.onNext(current);
}