diff --git a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt index 1e4dc3b..a85e6fb 100644 --- a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt +++ b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt @@ -33,7 +33,7 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re mAutoRotationObserver.enable() mBroadcastReceiver.setOnReceiveCallback { - adaptInterfaceTo(lastDeviceOrientation, false) + checkInterfaceOrientation(false) } context.addLifecycleEventListener(mLifecycleListener) @@ -113,7 +113,7 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re context.currentActivity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED updateIsLockedTo(false) - adaptInterfaceTo(lastDeviceOrientation) + checkInterfaceOrientation() } fun resetSupportedInterfaceOrientations() { @@ -161,7 +161,7 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re mEventManager.sendDeviceOrientationDidChange(deviceOrientation.ordinal) lastDeviceOrientation = deviceOrientation - adaptInterfaceTo(deviceOrientation) + checkInterfaceOrientation() if (!didComputeInitialDeviceOrientation) { didComputeInitialDeviceOrientation = true @@ -169,50 +169,35 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re } } - private fun adaptInterfaceTo(deviceOrientation: Orientation, checkLastAutoRotationStatus: Boolean = true) { - if (checkLastAutoRotationStatus && !mAutoRotationObserver.getLastAutoRotationStatus()) { + private fun checkInterfaceOrientation(skipIfAutoRotationIsDisabled: Boolean = true) { + if (skipIfAutoRotationIsDisabled && !mAutoRotationObserver.getLastAutoRotationStatus()) { return } - val supportsLandscape = - mUtils.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; - if (isLocked && !supportsLandscape) { + if (isLocked) { return } - var newInterfaceOrientation = mUtils.convertToInterfaceOrientationFrom(deviceOrientation); - - /** - * When the device orientation is either face up or face down, - * we can't match it to an interface orientation, because - * it could be either portrait or any landscape. - * So we read it from the system itself. - */ - if (newInterfaceOrientation == Orientation.UNKNOWN) { + if (lastDeviceOrientation != Orientation.LANDSCAPE_RIGHT && lastDeviceOrientation != Orientation.LANDSCAPE_LEFT) { val rotation = mUtils.getInterfaceRotation() - newInterfaceOrientation = mUtils.convertToOrientationFromScreenRotation(rotation) - } + val newInterfaceOrientation = mUtils.convertToOrientationFromScreenRotation(rotation) - /** - * This differs from iOS because we can't read the actual orientation of the interface, - * we read its rotation. - * This means that even if the requestedOrientation of the currentActivity is locked to landscape - * it reads every possible orientation and this is not what we want. - * Instead, we check that its value is either LANDSCAPE_RIGHT or LANDSCAPE_LEFT, otherwise we - * exit - */ - val newInterfaceOrientationIsNotLandscape = - newInterfaceOrientation != Orientation.LANDSCAPE_RIGHT - && newInterfaceOrientation != Orientation.LANDSCAPE_LEFT; - if (supportsLandscape && newInterfaceOrientationIsNotLandscape) { + updateLastInterfaceOrientationTo(newInterfaceOrientation) return } - if (newInterfaceOrientation == lastInterfaceOrientation) { - return - } + /** + * The reason we invert the interface orientation is to match iOS behavior with + * UIInterfaceOrientation when device is in landscape mode + */ + val interfaceOrientationBasedOnDeviceOne = + if (lastDeviceOrientation == Orientation.LANDSCAPE_RIGHT) { + Orientation.LANDSCAPE_LEFT + } else { + Orientation.LANDSCAPE_RIGHT + } - updateLastInterfaceOrientationTo(newInterfaceOrientation) + updateLastInterfaceOrientationTo(interfaceOrientationBasedOnDeviceOne) } private fun updateIsLockedTo(value: Boolean) { @@ -221,6 +206,10 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re } private fun updateLastInterfaceOrientationTo(value: Orientation) { + if (value == lastInterfaceOrientation) { + return + } + lastInterfaceOrientation = value mEventManager.sendInterfaceOrientationDidChange(value.ordinal) } diff --git a/android/src/main/java/com/orientationdirector/implementation/Utils.kt b/android/src/main/java/com/orientationdirector/implementation/Utils.kt index 0d919ce..ff592a5 100644 --- a/android/src/main/java/com/orientationdirector/implementation/Utils.kt +++ b/android/src/main/java/com/orientationdirector/implementation/Utils.kt @@ -20,38 +20,47 @@ class Utils(private val context: ReactContext) { } fun convertToDeviceOrientationFrom(orientationAngles: FloatArray): Orientation { - val (_, pitchRadians, rollRadians) = orientationAngles; - - val pitchDegrees = Math.toDegrees(pitchRadians.toDouble()).toFloat() - val rollDegrees = Math.toDegrees(rollRadians.toDouble()).toFloat() - - // This is needed to account for inaccuracy due to subtle movements such as tilting - val pitchToleranceDefault = 5f - val rollTolerance = 0f - val toleranceForFaceUpOrDown = 5f; - - ////////////////////////////////////// - // These limits are set based on SensorManager.getOrientation reference - // https://developer.android.com/develop/sensors-and-location/sensors/sensors_position#sensors-pos-orient - // - val portraitLimit = -90f - val landscapeRightLimit = 180f - val landscapeLeftLimit = -180f - // - ////////////////////////////////////// - - val isPitchInLandscapeModeRange = - checkIfValueIsBetweenTolerance(pitchDegrees, pitchToleranceDefault) - val isPitchCloseToFaceUpOrDown = - checkIfValueIsBetweenTolerance(pitchDegrees, toleranceForFaceUpOrDown) + if (orientationAngles.size < 3) { + return Orientation.PORTRAIT + } + + val (_, pitchRadians, rollRadians) = orientationAngles + + val pitch = Math.toDegrees(pitchRadians.toDouble()).toFloat() + val roll = Math.toDegrees(rollRadians.toDouble()).toFloat() + + val faceUpDownPitchTolerance = 30f + + fun isValueCloseTo(value: Float, target: Float, tolerance: Float): Boolean { + return value in (target - tolerance)..(target + tolerance) + } return when { - checkIfRollIsCloseToFaceUp(rollDegrees) && isPitchCloseToFaceUpOrDown -> Orientation.FACE_UP - checkIfRollIsCloseToFaceDown(rollDegrees) && isPitchCloseToFaceUpOrDown -> Orientation.FACE_DOWN - rollDegrees in rollTolerance..landscapeRightLimit - rollTolerance && isPitchInLandscapeModeRange -> Orientation.LANDSCAPE_RIGHT - rollDegrees in landscapeLeftLimit + rollTolerance..-rollTolerance && isPitchInLandscapeModeRange -> Orientation.LANDSCAPE_LEFT - pitchDegrees in portraitLimit..pitchToleranceDefault -> Orientation.PORTRAIT - else -> Orientation.PORTRAIT_UPSIDE_DOWN + // Face up: device is lying flat with screen up + isValueCloseTo(pitch, 0f, faceUpDownPitchTolerance) && + isValueCloseTo(roll, 0f, faceUpDownPitchTolerance) -> Orientation.FACE_UP + + // Face down: device is lying flat with screen down + isValueCloseTo(pitch, 0f, faceUpDownPitchTolerance) && + (isValueCloseTo(roll, 180f, faceUpDownPitchTolerance) || isValueCloseTo( + roll, + -180f, + faceUpDownPitchTolerance + )) -> Orientation.FACE_DOWN + + // Portrait + isValueCloseTo(pitch, -90f, 45f) -> Orientation.PORTRAIT + + // Portrait upside down + isValueCloseTo(pitch, 90f, 45f) -> Orientation.PORTRAIT_UPSIDE_DOWN + + // Landscape left + isValueCloseTo(roll, -90f, 45f) -> Orientation.LANDSCAPE_LEFT + + // Landscape right + isValueCloseTo(roll, 90f, 45f) -> Orientation.LANDSCAPE_RIGHT + + else -> Orientation.PORTRAIT } } @@ -93,32 +102,4 @@ class Utils(private val context: ReactContext) { else -> Orientation.UNKNOWN } } - - fun getRequestedOrientation(): Int { - if (context.currentActivity?.requestedOrientation == null) { - return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - } - - return context.currentActivity!!.requestedOrientation; - } - - private fun checkIfValueIsBetweenTolerance(value: Float, tolerance: Float): Boolean { - return value > -tolerance && value < tolerance - } - - private fun checkIfRollIsCloseToFaceDown(value: Float): Boolean { - val landscapeLimit = 180f - val faceDownLimit = 170f - - return value in faceDownLimit..landscapeLimit || - value in -landscapeLimit..-faceDownLimit; - } - - private fun checkIfRollIsCloseToFaceUp(value: Float): Boolean { - val landscapeLimit = 0f - val faceUpLimit = 10f - - return value in landscapeLimit..faceUpLimit || - value in -faceUpLimit..-landscapeLimit - } }