diff --git a/app/src/main/java/de/westnordost/streetcomplete/tangram/CompassComponent.java b/app/src/main/java/de/westnordost/streetcomplete/tangram/CompassComponent.java index 1f33f8aaba..0f559ae7e6 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/tangram/CompassComponent.java +++ b/app/src/main/java/de/westnordost/streetcomplete/tangram/CompassComponent.java @@ -6,6 +6,8 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.location.Location; +import android.os.Handler; +import android.os.HandlerThread; import android.support.annotation.AnyThread; import android.view.Display; import android.view.Surface; @@ -16,7 +18,9 @@ */ public class CompassComponent implements SensorEventListener { - private static float SMOOTHEN_FACTOR = 0.1f; + private static final int MAX_DISPATCH_FPS = 30; + private static final float SMOOTHEN_FACTOR = 0.1f; + private static final float MIN_DIFFERENCE = 0.005f; private SensorManager sensorManager; private Display display; @@ -25,10 +29,13 @@ public class CompassComponent implements SensorEventListener private float declination; private float rotation, tilt; - private boolean isRotationSet; private Listener listener; + private Handler sensorHandler; + private HandlerThread sensorThread; + private Thread dispatcherThread; + public interface Listener { @AnyThread void onRotationChanged(float rotation, float tilt); @@ -45,36 +52,42 @@ public void onCreate(SensorManager sensorManager, Display display) this.sensorManager = sensorManager; accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); magnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); + sensorThread = new HandlerThread("Compass Sensor Thread"); + sensorThread.start(); + sensorHandler = new Handler(sensorThread.getLooper()); + dispatcherThread = new Thread(this::dispatchLoop, "Compass Dispatcher Thread"); + dispatcherThread.start(); } - private float getDisplayTilt(float pitch, float roll) + private float[] remapToDisplayRotation(float[] inR) { - switch (display.getRotation()) - { - case Surface.ROTATION_0: return pitch; - case Surface.ROTATION_90: return roll; - case Surface.ROTATION_180: return -pitch; - case Surface.ROTATION_270: return -roll; - } - return 0; - } - - private int getDisplayRotation() - { - switch (display.getRotation()) - { - case Surface.ROTATION_0: return 0; - case Surface.ROTATION_90: return 90; - case Surface.ROTATION_180: return 180; - case Surface.ROTATION_270: return 270; + int h, v; + float[] outR = new float[9]; + switch (display.getRotation()) { + case Surface.ROTATION_90: + h = SensorManager.AXIS_Y; + v = SensorManager.AXIS_MINUS_X; + break; + case Surface.ROTATION_180: + h = SensorManager.AXIS_MINUS_X; + v = SensorManager.AXIS_MINUS_Y; + break; + case Surface.ROTATION_270: + h = SensorManager.AXIS_MINUS_Y; + v = SensorManager.AXIS_X; + break; + case Surface.ROTATION_0: + default: + return inR; } - return 0; + SensorManager.remapCoordinateSystem(inR, h, v, outR); + return outR; } public void onResume() { - sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI); - sensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI); + sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI, sensorHandler); + sensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI, sensorHandler); } public void onPause() @@ -82,6 +95,13 @@ public void onPause() sensorManager.unregisterListener(this); } + public void onDestroy() + { + listener = null; + sensorThread.quit(); + dispatcherThread.interrupt(); + } + @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { @@ -93,11 +113,11 @@ public void onPause() if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { - geomagnetic = event.values; + geomagnetic = event.values.clone(); } else if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { - gravity = event.values; + gravity = event.values.clone(); } if (gravity != null && geomagnetic != null) @@ -106,38 +126,44 @@ else if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) float I[] = new float[9]; boolean success = SensorManager.getRotationMatrix(R, I, gravity, geomagnetic); if (success) { + R = remapToDisplayRotation(R); float orientation[] = new float[3]; SensorManager.getOrientation(R, orientation); - float azimut = orientation[0]; + float azimut = orientation[0] - declination; float pitch = orientation[1]; float roll = orientation[2]; - float displayRotation = (float) (Math.PI * getDisplayRotation() / 180); - float displayTilt = getDisplayTilt(pitch, roll); - - if(!isRotationSet) - { - rotation = azimut; - tilt = displayTilt; - isRotationSet = true; - } - else - { - rotation = smoothenAngle(azimut, rotation, SMOOTHEN_FACTOR); - tilt = smoothenAngle(displayTilt, tilt, SMOOTHEN_FACTOR); - } - onRotationChanged(rotation + displayRotation - declination, tilt); + rotation = azimut; + tilt = pitch; } } } - private float lastTilt, lastRotation; - private void onRotationChanged(float r, float t) + private void dispatchLoop() { - if(Math.abs(this.lastTilt - t) < 0.005 && Math.abs(this.lastRotation - r) < 0.005) return; - listener.onRotationChanged(r,t); - this.lastTilt = t; - this.lastRotation = r; + boolean first = true; + float lastTilt = 0, lastRotation = 0, t = 0, r = 0; + while(!Thread.interrupted()) { + try { Thread.sleep(1000 / MAX_DISPATCH_FPS); } catch (InterruptedException e) { return; } + + if(first && (rotation != 0 || tilt != 0)) + { + r = rotation; + t = tilt; + first = false; + } else + { + r = smoothenAngle(rotation, r, SMOOTHEN_FACTOR); + t = smoothenAngle(tilt, t, SMOOTHEN_FACTOR); + } + + if(Math.abs(lastTilt - t) > MIN_DIFFERENCE || Math.abs(lastRotation - r) > MIN_DIFFERENCE) + { + listener.onRotationChanged(r, t); + lastTilt = t; + lastRotation = r; + } + } } private static float smoothenAngle(float newValue, float oldValue, float factor) diff --git a/app/src/main/java/de/westnordost/streetcomplete/tangram/MapFragment.java b/app/src/main/java/de/westnordost/streetcomplete/tangram/MapFragment.java index b42cfa8905..fdd63e5bfc 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/tangram/MapFragment.java +++ b/app/src/main/java/de/westnordost/streetcomplete/tangram/MapFragment.java @@ -475,7 +475,7 @@ private void updateAccuracy() if(directionMarker != null) { - directionMarker.setVisible(true); + if(!directionMarker.isVisible()) directionMarker.setVisible(true); double r = rotation * 180 / Math.PI; directionMarker.setStylingFromString( "{ style: 'points', color: '#cc536dfe', size: [" + @@ -486,13 +486,9 @@ private void updateAccuracy() if (isCompassMode) { float mapRotation = -rotation; - - // though the rotation and tilt are already smoothened by the CompassComponent, when it - // involves rotating the whole view, it feels better for the user if this is smoothened - // even further if (controller.getRotation() != mapRotation) { - controller.setRotationEased(mapRotation, 50); + controller.setRotation(mapRotation); } onMapOrientation(mapRotation, controller.getTilt()); } @@ -641,7 +637,7 @@ private void saveMapState() @Override public void onDestroy() { super.onDestroy(); - compass.setListener(null); + compass.onDestroy(); if(mapView != null) mapView.onDestroy(); controller = null; directionMarker = null;