diff options
5 files changed, 145 insertions, 49 deletions
diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java index ef224ff..21307d2 100644 --- a/main/src/cgeo/geocaching/CgeoApplication.java +++ b/main/src/cgeo/geocaching/CgeoApplication.java @@ -39,12 +39,15 @@ public class CgeoApplication extends Application { public synchronized Observable<ImmutablePair<IGeoData, Float>> geoDirObservable() { if (geoDir == null) { - geoDir = Observable.combineLatest(GeoDataProvider.create(this), DirectionProvider.create(this), new Func2<IGeoData, Float, ImmutablePair<IGeoData, Float>>() { + final Observable<IGeoData> geo = GeoDataProvider.create(this); + final Observable<Float> dir = DirectionProvider.create(this); + final Observable<ImmutablePair<IGeoData, Float>> combined = Observable.combineLatest(geo, dir, new Func2<IGeoData, Float, ImmutablePair<IGeoData, Float>>() { @Override public ImmutablePair<IGeoData, Float> call(final IGeoData geoData, final Float dir) { - return new ImmutablePair<IGeoData, Float>(geoData, dir); + return ImmutablePair.of(geoData, dir); } }); + geoDir = combined.publish().refCount(); } return geoDir; } diff --git a/main/src/cgeo/geocaching/sensors/DirectionProvider.java b/main/src/cgeo/geocaching/sensors/DirectionProvider.java index 162f14c..8efbc1f 100644 --- a/main/src/cgeo/geocaching/sensors/DirectionProvider.java +++ b/main/src/cgeo/geocaching/sensors/DirectionProvider.java @@ -1,15 +1,13 @@ package cgeo.geocaching.sensors; +import android.os.Process; import cgeo.geocaching.compatibility.Compatibility; +import cgeo.geocaching.utils.StartableHandlerThread; import rx.Observable; import rx.Observable.OnSubscribe; import rx.Subscriber; -import rx.Subscription; -import rx.observables.ConnectableObservable; import rx.subjects.BehaviorSubject; -import rx.subscriptions.Subscriptions; -import rx.functions.Action0; import android.app.Activity; import android.content.Context; @@ -17,39 +15,24 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.os.*; -public class DirectionProvider implements OnSubscribe<Float> { +public class DirectionProvider { - private final SensorManager sensorManager; - private final BehaviorSubject<Float> subject = BehaviorSubject.create(0.0f); + private static final BehaviorSubject<Float> subject = BehaviorSubject.create(0.0f); - static public Observable<Float> create(final Context context) { - return new DirectionProvider((SensorManager) context.getSystemService(Context.SENSOR_SERVICE)).worker.refCount(); - } + static class Listener implements SensorEventListener, StartableHandlerThread.Callback { - private DirectionProvider(final SensorManager sensorManager) { - this.sensorManager = sensorManager; - } + private int count = 0; + private SensorManager sensorManager; - @Override - public void call(final Subscriber<? super Float> subscriber) { - subject.distinctUntilChanged().subscribe(subscriber); - } + @Override + public void onSensorChanged(final SensorEvent event) { + subject.onNext(event.values[0]); + } - private final ConnectableObservable<Float> worker = new ConnectableObservable<Float>(this) { @Override - public Subscription connect() { - @SuppressWarnings("deprecation") - // This will be removed when using a new location service. Until then, it is okay to be used. - final Sensor defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); - final SensorEventListener listener = new SensorEventListener() { - @Override - public void onSensorChanged(final SensorEvent event) { - subject.onNext(event.values[0]); - } - - @Override - public void onAccuracyChanged(final Sensor sensor, final int accuracy) { + public void onAccuracyChanged(final Sensor sensor, final int accuracy) { /* * There is a bug in Android, which apparently causes this method to be called every * time the sensor _value_ changed, even if the _accuracy_ did not change. So logging @@ -60,19 +43,43 @@ public class DirectionProvider implements OnSubscribe<Float> { * See for example https://code.google.com/p/android/issues/detail?id=14792 */ - //Log.i(Settings.tag, "Compass' accuracy is low (" + accuracy + ")"); - } - }; - - sensorManager.registerListener(listener, defaultSensor, SensorManager.SENSOR_DELAY_NORMAL); - return Subscriptions.create(new Action0() { - @Override - public void call() { - sensorManager.unregisterListener(listener); - } - }); + //Log.i(Settings.tag, "Compass' accuracy is low (" + accuracy + ")"); } - }; + + // This will be removed when using a new location service. Until then, it is okay to be used. + @SuppressWarnings("deprecation") + @Override + public void start(final Context context, final Handler handler) { + if (++count == 1) { + sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL, handler); + } + } + + @Override + public void stop() { + if (--count == 0) { + sensorManager.unregisterListener(this); + } + } + + } + + private static final StartableHandlerThread handlerThread = + new StartableHandlerThread("DirectionProvider thread", Process.THREAD_PRIORITY_BACKGROUND, new Listener()); + static { + handlerThread.start(); + } + + static public Observable<Float> create(final Context context) { + return Observable.create(new OnSubscribe<Float>() { + @Override + public void call(final Subscriber<? super Float> subscriber) { + handlerThread.start(subscriber, context); + subject.subscribe(subscriber); + } + }); + } /** * Take the phone rotation (through a given activity) in account and adjust the direction. diff --git a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java index 160d98d..a77b477 100644 --- a/main/src/cgeo/geocaching/sensors/GeoDataProvider.java +++ b/main/src/cgeo/geocaching/sensors/GeoDataProvider.java @@ -1,7 +1,9 @@ package cgeo.geocaching.sensors; +import android.os.*; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.StartableHandlerThread; import org.apache.commons.lang3.StringUtils; import rx.Observable; import rx.Observable.OnSubscribe; @@ -22,7 +24,6 @@ import android.location.GpsStatus; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; -import android.os.Bundle; import java.util.concurrent.TimeUnit; @@ -33,6 +34,11 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> { 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; @@ -92,7 +98,7 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> { @Override public Subscription connect() { final CompositeSubscription subscription = new CompositeSubscription(); - AndroidSchedulers.mainThread().schedule(new Action1<Inner>() { + AndroidSchedulers.handlerThread(handlerThread.getHandler()).schedule(new Action1<Inner>() { @Override public void call(final Inner inner) { synchronized(lock) { @@ -112,7 +118,7 @@ public class GeoDataProvider implements OnSubscribe<IGeoData> { subscription.add(Subscriptions.create(new Action0() { @Override public void call() { - AndroidSchedulers.mainThread().schedule(new Action1<Inner>() { + AndroidSchedulers.handlerThread(handlerThread.getHandler()).schedule(new Action1<Inner>() { @Override public void call(final Inner inner) { synchronized (lock) { diff --git a/main/src/cgeo/geocaching/sensors/GeoDirHandler.java b/main/src/cgeo/geocaching/sensors/GeoDirHandler.java index 0af2cc8..588bd84 100644 --- a/main/src/cgeo/geocaching/sensors/GeoDirHandler.java +++ b/main/src/cgeo/geocaching/sensors/GeoDirHandler.java @@ -42,12 +42,12 @@ public abstract class GeoDirHandler { * preferences allow it). */ public Subscription start() { - return app.geoDirObservable().subscribe(new Action1<ImmutablePair<IGeoData, Float>>() { + return app.geoDirObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<ImmutablePair<IGeoData, Float>>() { @Override public void call(final ImmutablePair<IGeoData, Float> geoDir) { handleGeoDir(geoDir); } - }, AndroidSchedulers.mainThread()); + }); } } diff --git a/main/src/cgeo/geocaching/utils/StartableHandlerThread.java b/main/src/cgeo/geocaching/utils/StartableHandlerThread.java new file mode 100644 index 0000000..152badc --- /dev/null +++ b/main/src/cgeo/geocaching/utils/StartableHandlerThread.java @@ -0,0 +1,80 @@ +package cgeo.geocaching.utils; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import org.eclipse.jdt.annotation.NonNull; +import rx.Subscriber; +import rx.functions.Action0; +import rx.subscriptions.Subscriptions; + +/** + * Derivated class of {@link android.os.HandlerThread} with an exposed handler and a start/stop mechanism + * based on subscriptions. + */ + +public class StartableHandlerThread extends HandlerThread { + + private final static int START = 1; + private final static int STOP = 2; + + static public interface Callback { + public void start(final Context context, final Handler handler); + public void stop(); + } + + private class StartableHandler extends Handler { + public StartableHandler() { + super(StartableHandlerThread.this.getLooper()); + } + + @Override + public void handleMessage(final Message message) { + if (callback != null) { + switch (message.what) { + case START: + callback.start((Context) message.obj, this); + break; + case STOP: + callback.stop(); + break; + } + } + } + } + + private Handler handler; + private Callback callback; + + public StartableHandlerThread(@NonNull final String name, final int priority, final Callback callback) { + super(name, priority); + this.callback = callback; + } + + public StartableHandlerThread(@NonNull final String name, final int priority) { + this(name, priority, null); + } + + public Handler getHandler() { + if (handler == null) { + synchronized(this) { + if (handler == null) { + handler = new StartableHandler(); + } + } + } + return handler; + } + + public void start(final Subscriber<?> subscriber, final Context context) { + getHandler().obtainMessage(START, context).sendToTarget(); + subscriber.add(Subscriptions.create(new Action0() { + @Override + public void call() { + getHandler().sendEmptyMessage(STOP); + } + })); + } + +} |
