package cgeo.geocaching.sensors;

import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.RxUtils;

import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Subscriber;
import rx.functions.Action0;
import rx.subscriptions.Subscriptions;

import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

public class RotationProvider {

    private RotationProvider() {
        // Utility class, not to be instantiated
    }

    @TargetApi(19)
    public static Observable<Float> create(final Context context) {
        final SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        final Sensor rotationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
        if (rotationSensor == null) {
            Log.w("RotationProvider: no rotation sensor on this device");
            return Observable.error(new RuntimeException("no rotation sensor"));
        }
        Log.d("RotationProvider: sensor found");
        final Observable<Float> observable = Observable.create(new OnSubscribe<Float>() {
            private final float[] rotationMatrix = new float[16];
            private final float[] orientation = new float[4];
            private final float[] values = new float[4];

            @Override
            public void call(final Subscriber<? super Float> subscriber) {
                final SensorEventListener listener = new SensorEventListener() {
                    @Override
                    public void onSensorChanged(final SensorEvent event) {
                        // On some Samsung devices, SensorManager#getRotationMatrixFromVector throws an exception if the rotation
                        // vector has more than 4 elements. Since only the four first elements are used, we can truncate the vector
                        // without losing precision.
                        if (event.values.length > 4) {
                            System.arraycopy(event.values, 0, values, 0, 4);
                            SensorManager.getRotationMatrixFromVector(rotationMatrix, values);
                        } else {
                            SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values);
                        }
                        SensorManager.getOrientation(rotationMatrix, orientation);
                        subscriber.onNext((float) (orientation[0] * 180 / Math.PI));
                    }

                    @Override
                    public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
                    }

                };
                Log.d("RotationProvider: registering listener");
                sensorManager.registerListener(listener, rotationSensor, SensorManager.SENSOR_DELAY_NORMAL);
                subscriber.add(Subscriptions.create(new Action0() {
                    @Override
                    public void call() {
                        RxUtils.looperCallbacksWorker.schedule(new Action0() {
                            @Override
                            public void call() {
                                Log.d("RotationProvider: unregistering listener");
                                sensorManager.unregisterListener(listener, rotationSensor);
                            }
                        });
                    }
                }));
            }
        });
        return observable.subscribeOn(RxUtils.looperCallbacksScheduler).share().onBackpressureLatest();
    }

    public static boolean hasRotationSensor(final Context context) {
        return ((SensorManager) context.getSystemService(Context.SENSOR_SERVICE)).getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) != null;
    }

}

@@ -1226,24 +1226,25 @@
 %3CFloat%3E() %7B%0A
+%0A
             
@@ -1247,62 +1247,183 @@
     
-private final float%5B%5D rotationMatrix = new float%5B16%5D;%0A
+@Override%0A            public void call(final Subscriber%3C? super Float%3E subscriber) %7B%0A                final SensorEventListener listener = new SensorEventListener() %7B%0A%0A        
     
@@ -1452,27 +1452,30 @@
 float%5B%5D 
+r
 o
-rien
 tation
+Matrix
  = new f
@@ -1479,20 +1479,29 @@
 w float%5B
-4
+16
 %5D;%0A
+        
         
@@ -1526,22 +1526,27 @@
 float%5B%5D 
-values
+orientation
  = new f
@@ -1554,17 +1554,16 @@
 oat%5B4%5D;%0A
-%0A
         
@@ -1570,173 +1570,61 @@
     
-@Override%0A            public void call(final Subscriber%3C? super Float%3E subscriber) %7B%0A                final SensorEventListener listener = new SensorEventListener() %7B
+        private final float%5B%5D values = new float%5B4%5D;%0A
 %0A   

move fields down scope