diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/OrientationHelperTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/OrientationHelperTest.java index 011176cb..b015c739 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/OrientationHelperTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/OrientationHelperTest.java @@ -41,23 +41,19 @@ public class OrientationHelperTest extends BaseTest { @Test public void testEnable() { - assertNotNull(helper.mListener); - assertEquals(helper.getDisplayOffset(), -1); - assertEquals(helper.getDeviceOrientation(), -1); + assertEquals(helper.getLastDisplayOffset(), -1); + assertEquals(helper.getLastDeviceOrientation(), -1); - helper.enable(getContext()); - assertNotNull(helper.mListener); - assertNotEquals(helper.getDisplayOffset(), -1); // Don't know about device orientation. + helper.enable(); + assertNotEquals(helper.getLastDisplayOffset(), -1); // Don't know about device orientation. // Ensure nothing bad if called twice. - helper.enable(getContext()); - assertNotNull(helper.mListener); - assertNotEquals(helper.getDisplayOffset(), -1); + helper.enable(); + assertNotEquals(helper.getLastDisplayOffset(), -1); helper.disable(); - assertNotNull(helper.mListener); - assertEquals(helper.getDisplayOffset(), -1); - assertEquals(helper.getDeviceOrientation(), -1); + assertEquals(helper.getLastDisplayOffset(), -1); + assertEquals(helper.getLastDeviceOrientation(), -1); } @Test @@ -65,37 +61,37 @@ public class OrientationHelperTest extends BaseTest { // Sometimes (on some APIs) the helper will trigger an update to 0 // right after enabling. But that's fine for us, times(1) will be OK either way. - helper.enable(getContext()); - helper.mListener.onOrientationChanged(OrientationEventListener.ORIENTATION_UNKNOWN); - assertEquals(helper.getDeviceOrientation(), 0); - helper.mListener.onOrientationChanged(10); - assertEquals(helper.getDeviceOrientation(), 0); - helper.mListener.onOrientationChanged(-10); - assertEquals(helper.getDeviceOrientation(), 0); - helper.mListener.onOrientationChanged(44); - assertEquals(helper.getDeviceOrientation(), 0); - helper.mListener.onOrientationChanged(360); - assertEquals(helper.getDeviceOrientation(), 0); + helper.enable(); + helper.mDeviceOrientationListener.onOrientationChanged(OrientationEventListener.ORIENTATION_UNKNOWN); + assertEquals(helper.getLastDeviceOrientation(), 0); + helper.mDeviceOrientationListener.onOrientationChanged(10); + assertEquals(helper.getLastDeviceOrientation(), 0); + helper.mDeviceOrientationListener.onOrientationChanged(-10); + assertEquals(helper.getLastDeviceOrientation(), 0); + helper.mDeviceOrientationListener.onOrientationChanged(44); + assertEquals(helper.getLastDeviceOrientation(), 0); + helper.mDeviceOrientationListener.onOrientationChanged(360); + assertEquals(helper.getLastDeviceOrientation(), 0); // Callback called just once. verify(callback, times(1)).onDeviceOrientationChanged(0); - helper.mListener.onOrientationChanged(90); - helper.mListener.onOrientationChanged(91); - assertEquals(helper.getDeviceOrientation(), 90); + helper.mDeviceOrientationListener.onOrientationChanged(90); + helper.mDeviceOrientationListener.onOrientationChanged(91); + assertEquals(helper.getLastDeviceOrientation(), 90); verify(callback, times(1)).onDeviceOrientationChanged(90); - helper.mListener.onOrientationChanged(180); - assertEquals(helper.getDeviceOrientation(), 180); + helper.mDeviceOrientationListener.onOrientationChanged(180); + assertEquals(helper.getLastDeviceOrientation(), 180); verify(callback, times(1)).onDeviceOrientationChanged(180); - helper.mListener.onOrientationChanged(270); - assertEquals(helper.getDeviceOrientation(), 270); + helper.mDeviceOrientationListener.onOrientationChanged(270); + assertEquals(helper.getLastDeviceOrientation(), 270); verify(callback, times(1)).onDeviceOrientationChanged(270); // It is still 270 after ORIENTATION_UNKNOWN - helper.mListener.onOrientationChanged(OrientationEventListener.ORIENTATION_UNKNOWN); - assertEquals(helper.getDeviceOrientation(), 270); + helper.mDeviceOrientationListener.onOrientationChanged(OrientationEventListener.ORIENTATION_UNKNOWN); + assertEquals(helper.getLastDeviceOrientation(), 270); verify(callback, times(1)).onDeviceOrientationChanged(270); } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index ccbf7428..f55e9289 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -356,7 +356,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { // attached. That's why we instantiate the preview here. doInstantiatePreview(); } - mOrientationHelper.enable(getContext()); + mOrientationHelper.enable(); } @Override @@ -722,8 +722,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver { if (mCameraPreview != null) mCameraPreview.onResume(); if (checkPermissions(getAudio())) { // Update display orientation for current CameraEngine - mOrientationHelper.enable(getContext()); - mCameraEngine.getAngles().setDisplayOffset(mOrientationHelper.getDisplayOffset()); + mOrientationHelper.enable(); + mCameraEngine.getAngles().setDisplayOffset(mOrientationHelper.getLastDisplayOffset()); mCameraEngine.start(); } } @@ -2073,7 +2073,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { @Override public void onDeviceOrientationChanged(int deviceOrientation) { mLogger.i("onDeviceOrientationChanged", deviceOrientation); - int displayOffset = mOrientationHelper.getDisplayOffset(); + int displayOffset = mOrientationHelper.getLastDisplayOffset(); if (!mUseDeviceOrientation) { // To fool the engine to return outputs in the VIEW reference system, // The device orientation should be set to -displayOffset. @@ -2093,6 +2093,19 @@ public class CameraView extends FrameLayout implements LifecycleObserver { }); } + @Override + public void onDisplayOffsetChanged(int displayOffset, boolean willRecreate) { + mLogger.i("onDisplayOffsetChanged", displayOffset, "recreate:", willRecreate); + if (isOpened() && !willRecreate) { + // Display offset changes when the device rotation lock is off and the activity + // is free to rotate. However, some changes will NOT recreate the activity, namely + // 180 degrees flips. In this case, we must restart the camera manually. + mLogger.w("onDisplayOffsetChanged", "restarting the camera."); + close(); + open(); + } + } + @Override public void dispatchOnZoomChanged(final float newValue, @Nullable final PointF[] fingers) { mLogger.i("dispatchOnZoomChanged", newValue); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/LogAction.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/LogAction.java index d51567a2..8c20a83f 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/LogAction.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/LogAction.java @@ -36,7 +36,7 @@ class LogAction extends BaseAction { " afState: " + afState + " afTriggerState: " + afTriggerState; if (!log.equals(lastLog)) { lastLog = log; - LOG.i(log); + LOG.v(log); } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/OrientationHelper.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/OrientationHelper.java index a72a626e..36c5f1d5 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/OrientationHelper.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/OrientationHelper.java @@ -5,6 +5,8 @@ import android.hardware.SensorManager; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import android.hardware.display.DisplayManager; +import android.os.Build; import android.view.Display; import android.view.OrientationEventListener; import android.view.Surface; @@ -12,21 +14,28 @@ import android.view.WindowManager; /** * Helps with keeping track of both device orientation (which changes when device is rotated) - * and the display offset (which depends on the activity orientation - * wrt the device default orientation). + * and the display offset (which depends on the activity orientation wrt the device default + * orientation). */ public class OrientationHelper { /** - * Receives callback about the device orientation changes. + * Receives callback about the orientation changes. */ public interface Callback { void onDeviceOrientationChanged(int deviceOrientation); + void onDisplayOffsetChanged(int displayOffset, boolean willRecreate); } - @VisibleForTesting final OrientationEventListener mListener; + private final Context mContext; private final Callback mCallback; + + @VisibleForTesting + final OrientationEventListener mDeviceOrientationListener; private int mDeviceOrientation = -1; + + @VisibleForTesting + final DisplayManager.DisplayListener mDisplayOffsetListener; private int mDisplayOffset = -1; /** @@ -35,57 +44,78 @@ public class OrientationHelper { * @param callback a {@link Callback} */ public OrientationHelper(@NonNull Context context, @NonNull Callback callback) { + mContext = context; mCallback = callback; - mListener = new OrientationEventListener(context.getApplicationContext(), + mDeviceOrientationListener = new OrientationEventListener(context.getApplicationContext(), SensorManager.SENSOR_DELAY_NORMAL) { @SuppressWarnings("ConstantConditions") @Override public void onOrientationChanged(int orientation) { - int or = 0; + int deviceOrientation = 0; if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) { - or = mDeviceOrientation != -1 ? mDeviceOrientation : 0; + deviceOrientation = mDeviceOrientation != -1 ? mDeviceOrientation : 0; } else if (orientation >= 315 || orientation < 45) { - or = 0; + deviceOrientation = 0; } else if (orientation >= 45 && orientation < 135) { - or = 90; + deviceOrientation = 90; } else if (orientation >= 135 && orientation < 225) { - or = 180; + deviceOrientation = 180; } else if (orientation >= 225 && orientation < 315) { - or = 270; + deviceOrientation = 270; } - if (or != mDeviceOrientation) { - mDeviceOrientation = or; + if (deviceOrientation != mDeviceOrientation) { + mDeviceOrientation = deviceOrientation; mCallback.onDeviceOrientationChanged(mDeviceOrientation); } } }; + if (Build.VERSION.SDK_INT >= 17) { + mDisplayOffsetListener = new DisplayManager.DisplayListener() { + public void onDisplayAdded(int displayId) { } + public void onDisplayRemoved(int displayId) { } + + @Override + public void onDisplayChanged(int displayId) { + int oldDisplayOffset = mDisplayOffset; + int newDisplayOffset = findDisplayOffset(); + if (newDisplayOffset != oldDisplayOffset) { + mDisplayOffset = newDisplayOffset; + // With 180 degrees flips, the activity is not recreated. + boolean willRecreate = Math.abs(newDisplayOffset - oldDisplayOffset) != 180; + mCallback.onDisplayOffsetChanged(newDisplayOffset, willRecreate); + } + } + }; + } else { + mDisplayOffsetListener = null; + } } /** * Enables this listener. - * @param context a context */ - public void enable(@NonNull Context context) { - Display display = ((WindowManager) context - .getSystemService(Context.WINDOW_SERVICE)) - .getDefaultDisplay(); - switch (display.getRotation()) { - case Surface.ROTATION_0: mDisplayOffset = 0; break; - case Surface.ROTATION_90: mDisplayOffset = 90; break; - case Surface.ROTATION_180: mDisplayOffset = 180; break; - case Surface.ROTATION_270: mDisplayOffset = 270; break; - default: mDisplayOffset = 0; break; + public void enable() { + mDisplayOffset = findDisplayOffset(); + if (Build.VERSION.SDK_INT >= 17) { + DisplayManager manager = (DisplayManager) + mContext.getSystemService(Context.DISPLAY_SERVICE); + manager.registerDisplayListener(mDisplayOffsetListener, null); } - mListener.enable(); + mDeviceOrientationListener.enable(); } /** * Disables this listener. */ public void disable() { - mListener.disable(); + mDeviceOrientationListener.disable(); + if (Build.VERSION.SDK_INT >= 17) { + DisplayManager manager = (DisplayManager) + mContext.getSystemService(Context.DISPLAY_SERVICE); + manager.unregisterDisplayListener(mDisplayOffsetListener); + } mDisplayOffset = -1; mDeviceOrientation = -1; } @@ -94,7 +124,8 @@ public class OrientationHelper { * Returns the current device orientation. * @return device orientation */ - public int getDeviceOrientation() { + @SuppressWarnings("WeakerAccess") + public int getLastDeviceOrientation() { return mDeviceOrientation; } @@ -102,7 +133,20 @@ public class OrientationHelper { * Returns the current display offset. * @return display offset */ - public int getDisplayOffset() { + public int getLastDisplayOffset() { return mDisplayOffset; } + + private int findDisplayOffset() { + Display display = ((WindowManager) mContext + .getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay(); + 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; + default: return 0; + } + } }