From 1ca40780aafeb4060657701d9e5c0c381e9f76c9 Mon Sep 17 00:00:00 2001 From: gladiuscode Date: Sat, 19 Jul 2025 18:44:40 +0200 Subject: [PATCH 1/9] fix: remove interface computation from device orientation, use display instead --- .../OrientationDirectorModuleImpl.kt | 44 ++++--------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt index 1e4dc3b..040f985 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,45 +169,17 @@ 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) { - 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) { - val rotation = mUtils.getInterfaceRotation() - 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) { + if (isLocked) { return } + val rotation = mUtils.getInterfaceRotation() + val newInterfaceOrientation = mUtils.convertToOrientationFromScreenRotation(rotation) if (newInterfaceOrientation == lastInterfaceOrientation) { return } From 705474929b74492f95715b59f2c149a145ed1b92 Mon Sep 17 00:00:00 2001 From: gladiuscode Date: Sat, 19 Jul 2025 19:02:09 +0200 Subject: [PATCH 2/9] fix: convertToOrientationFromScreenRotation method --- .../main/java/com/orientationdirector/implementation/Utils.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/Utils.kt b/android/src/main/java/com/orientationdirector/implementation/Utils.kt index 0d919ce..14cbdff 100644 --- a/android/src/main/java/com/orientationdirector/implementation/Utils.kt +++ b/android/src/main/java/com/orientationdirector/implementation/Utils.kt @@ -77,8 +77,8 @@ class Utils(private val context: ReactContext) { fun convertToOrientationFromScreenRotation(screenRotation: Int): Orientation { return when (screenRotation) { - Surface.ROTATION_270 -> Orientation.LANDSCAPE_RIGHT - Surface.ROTATION_90 -> Orientation.LANDSCAPE_LEFT + Surface.ROTATION_270 -> Orientation.LANDSCAPE_LEFT + Surface.ROTATION_90 -> Orientation.LANDSCAPE_RIGHT Surface.ROTATION_180 -> Orientation.PORTRAIT_UPSIDE_DOWN else -> Orientation.PORTRAIT } From d605a338547da9634894818ab2270ce77a07ac8e Mon Sep 17 00:00:00 2001 From: gladiuscode Date: Sat, 19 Jul 2025 19:14:23 +0200 Subject: [PATCH 3/9] revert: "fix: convertToOrientationFromScreenRotation method" This reverts commit 705474929b74492f95715b59f2c149a145ed1b92. --- .../main/java/com/orientationdirector/implementation/Utils.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/Utils.kt b/android/src/main/java/com/orientationdirector/implementation/Utils.kt index 14cbdff..0d919ce 100644 --- a/android/src/main/java/com/orientationdirector/implementation/Utils.kt +++ b/android/src/main/java/com/orientationdirector/implementation/Utils.kt @@ -77,8 +77,8 @@ class Utils(private val context: ReactContext) { fun convertToOrientationFromScreenRotation(screenRotation: Int): Orientation { return when (screenRotation) { - Surface.ROTATION_270 -> Orientation.LANDSCAPE_LEFT - Surface.ROTATION_90 -> Orientation.LANDSCAPE_RIGHT + Surface.ROTATION_270 -> Orientation.LANDSCAPE_RIGHT + Surface.ROTATION_90 -> Orientation.LANDSCAPE_LEFT Surface.ROTATION_180 -> Orientation.PORTRAIT_UPSIDE_DOWN else -> Orientation.PORTRAIT } From c0dfaf3160ce70562db0d4aac1fbafbd9d3abc73 Mon Sep 17 00:00:00 2001 From: gladiuscode Date: Sat, 19 Jul 2025 19:15:07 +0200 Subject: [PATCH 4/9] fix: explicitly invert interface landscape orientation to match iOS --- .../OrientationDirectorModuleImpl.kt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt index 040f985..8c3a9e3 100644 --- a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt +++ b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt @@ -184,7 +184,21 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re return } - updateLastInterfaceOrientationTo(newInterfaceOrientation) + if (newInterfaceOrientation != Orientation.LANDSCAPE_LEFT && newInterfaceOrientation != Orientation.LANDSCAPE_RIGHT) { + updateLastInterfaceOrientationTo(newInterfaceOrientation) + return; + } + + /** + * The reason we invert the interface orientation is to match iOS behavior with + * UIInterfaceOrientation + */ + val invertedLandscapeOrientation = if (newInterfaceOrientation == Orientation.LANDSCAPE_RIGHT) { + Orientation.LANDSCAPE_LEFT + } else { + Orientation.LANDSCAPE_RIGHT + } + updateLastInterfaceOrientationTo(invertedLandscapeOrientation) } private fun updateIsLockedTo(value: Boolean) { From 4951d29f27681fb5ceb2027a7e7871bf24b6ea66 Mon Sep 17 00:00:00 2001 From: gladiuscode Date: Sat, 19 Jul 2025 19:30:34 +0200 Subject: [PATCH 5/9] fix: simplify convertToDeviceOrientation from logic --- .../implementation/Utils.kt | 87 +++++++------------ 1 file changed, 29 insertions(+), 58 deletions(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/Utils.kt b/android/src/main/java/com/orientationdirector/implementation/Utils.kt index 0d919ce..d45749f 100644 --- a/android/src/main/java/com/orientationdirector/implementation/Utils.kt +++ b/android/src/main/java/com/orientationdirector/implementation/Utils.kt @@ -20,38 +20,37 @@ 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) + 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, 45f) -> Orientation.FACE_UP + + // Face down: device is lying flat with screen down + isValueCloseTo(pitch, 0f, faceUpDownPitchTolerance) && isValueCloseTo(roll, 180f, 45f) -> Orientation.FACE_DOWN + + // Portrait: upright + 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 // fallback } } @@ -93,32 +92,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 - } } From fb5ceaf87f7fb210afa31a004fa4d72f20f36ab6 Mon Sep 17 00:00:00 2001 From: gladiuscode Date: Sat, 19 Jul 2025 19:35:36 +0200 Subject: [PATCH 6/9] fix: missing orientationAngles validation --- .../main/java/com/orientationdirector/implementation/Utils.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/src/main/java/com/orientationdirector/implementation/Utils.kt b/android/src/main/java/com/orientationdirector/implementation/Utils.kt index d45749f..3b7a26f 100644 --- a/android/src/main/java/com/orientationdirector/implementation/Utils.kt +++ b/android/src/main/java/com/orientationdirector/implementation/Utils.kt @@ -20,6 +20,10 @@ class Utils(private val context: ReactContext) { } fun convertToDeviceOrientationFrom(orientationAngles: FloatArray): Orientation { + if (orientationAngles.size < 3) { + return Orientation.PORTRAIT + } + val (_, pitchRadians, rollRadians) = orientationAngles val pitch = Math.toDegrees(pitchRadians.toDouble()).toFloat() From 86a470628d876ae4e48bd9dcf84428c3e29dfa66 Mon Sep 17 00:00:00 2001 From: gladiuscode Date: Sat, 19 Jul 2025 19:41:26 +0200 Subject: [PATCH 7/9] fix: face up and face down detection --- .../com/orientationdirector/implementation/Utils.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/Utils.kt b/android/src/main/java/com/orientationdirector/implementation/Utils.kt index 3b7a26f..2a7bc48 100644 --- a/android/src/main/java/com/orientationdirector/implementation/Utils.kt +++ b/android/src/main/java/com/orientationdirector/implementation/Utils.kt @@ -37,12 +37,14 @@ class Utils(private val context: ReactContext) { return when { // Face up: device is lying flat with screen up - isValueCloseTo(pitch, 0f, faceUpDownPitchTolerance) && isValueCloseTo(roll, 0f, 45f) -> Orientation.FACE_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, 45f) -> Orientation.FACE_DOWN + isValueCloseTo(pitch, 0f, faceUpDownPitchTolerance) && + (isValueCloseTo(roll, 180f, faceUpDownPitchTolerance) || isValueCloseTo(roll, -180f, faceUpDownPitchTolerance)) -> Orientation.FACE_DOWN - // Portrait: upright + // Portrait isValueCloseTo(pitch, -90f, 45f) -> Orientation.PORTRAIT // Portrait upside down @@ -54,7 +56,7 @@ class Utils(private val context: ReactContext) { // Landscape right isValueCloseTo(roll, 90f, 45f) -> Orientation.LANDSCAPE_RIGHT - else -> Orientation.PORTRAIT // fallback + else -> Orientation.PORTRAIT } } From 7cfd1ec2ca57645f305a4fdfddc9261746a48081 Mon Sep 17 00:00:00 2001 From: gladiuscode Date: Sat, 19 Jul 2025 19:41:37 +0200 Subject: [PATCH 8/9] fix(lint): kotlin code --- .../java/com/orientationdirector/implementation/Utils.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/Utils.kt b/android/src/main/java/com/orientationdirector/implementation/Utils.kt index 2a7bc48..ff592a5 100644 --- a/android/src/main/java/com/orientationdirector/implementation/Utils.kt +++ b/android/src/main/java/com/orientationdirector/implementation/Utils.kt @@ -42,7 +42,11 @@ class Utils(private val context: ReactContext) { // Face down: device is lying flat with screen down isValueCloseTo(pitch, 0f, faceUpDownPitchTolerance) && - (isValueCloseTo(roll, 180f, faceUpDownPitchTolerance) || isValueCloseTo(roll, -180f, faceUpDownPitchTolerance)) -> Orientation.FACE_DOWN + (isValueCloseTo(roll, 180f, faceUpDownPitchTolerance) || isValueCloseTo( + roll, + -180f, + faceUpDownPitchTolerance + )) -> Orientation.FACE_DOWN // Portrait isValueCloseTo(pitch, -90f, 45f) -> Orientation.PORTRAIT From 2e4868a7a81da71a40bbb2d7758fcdc5b8bb8c15 Mon Sep 17 00:00:00 2001 From: gladiuscode Date: Sun, 27 Jul 2025 17:11:06 +0200 Subject: [PATCH 9/9] feat: fix invert interface orientation only based on device one --- .../OrientationDirectorModuleImpl.kt | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt index 8c3a9e3..a85e6fb 100644 --- a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt +++ b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt @@ -178,27 +178,26 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re return } - val rotation = mUtils.getInterfaceRotation() - val newInterfaceOrientation = mUtils.convertToOrientationFromScreenRotation(rotation) - if (newInterfaceOrientation == lastInterfaceOrientation) { - return - } + if (lastDeviceOrientation != Orientation.LANDSCAPE_RIGHT && lastDeviceOrientation != Orientation.LANDSCAPE_LEFT) { + val rotation = mUtils.getInterfaceRotation() + val newInterfaceOrientation = mUtils.convertToOrientationFromScreenRotation(rotation) - if (newInterfaceOrientation != Orientation.LANDSCAPE_LEFT && newInterfaceOrientation != Orientation.LANDSCAPE_RIGHT) { updateLastInterfaceOrientationTo(newInterfaceOrientation) - return; + return } /** * The reason we invert the interface orientation is to match iOS behavior with - * UIInterfaceOrientation + * UIInterfaceOrientation when device is in landscape mode */ - val invertedLandscapeOrientation = if (newInterfaceOrientation == Orientation.LANDSCAPE_RIGHT) { - Orientation.LANDSCAPE_LEFT - } else { - Orientation.LANDSCAPE_RIGHT - } - updateLastInterfaceOrientationTo(invertedLandscapeOrientation) + val interfaceOrientationBasedOnDeviceOne = + if (lastDeviceOrientation == Orientation.LANDSCAPE_RIGHT) { + Orientation.LANDSCAPE_LEFT + } else { + Orientation.LANDSCAPE_RIGHT + } + + updateLastInterfaceOrientationTo(interfaceOrientationBasedOnDeviceOne) } private fun updateIsLockedTo(value: Boolean) { @@ -207,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) }