Skip to content

Commit 26cf330

Browse files
authored
fix(android): broken interface computation (#89)
1 parent ef8a429 commit 26cf330

File tree

2 files changed

+63
-93
lines changed

2 files changed

+63
-93
lines changed

android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re
3333
mAutoRotationObserver.enable()
3434

3535
mBroadcastReceiver.setOnReceiveCallback {
36-
adaptInterfaceTo(lastDeviceOrientation, false)
36+
checkInterfaceOrientation(false)
3737
}
3838

3939
context.addLifecycleEventListener(mLifecycleListener)
@@ -113,7 +113,7 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re
113113
context.currentActivity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
114114

115115
updateIsLockedTo(false)
116-
adaptInterfaceTo(lastDeviceOrientation)
116+
checkInterfaceOrientation()
117117
}
118118

119119
fun resetSupportedInterfaceOrientations() {
@@ -161,58 +161,43 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re
161161
mEventManager.sendDeviceOrientationDidChange(deviceOrientation.ordinal)
162162
lastDeviceOrientation = deviceOrientation
163163

164-
adaptInterfaceTo(deviceOrientation)
164+
checkInterfaceOrientation()
165165

166166
if (!didComputeInitialDeviceOrientation) {
167167
didComputeInitialDeviceOrientation = true
168168
mOrientationSensorsEventListener.disable()
169169
}
170170
}
171171

172-
private fun adaptInterfaceTo(deviceOrientation: Orientation, checkLastAutoRotationStatus: Boolean = true) {
173-
if (checkLastAutoRotationStatus && !mAutoRotationObserver.getLastAutoRotationStatus()) {
172+
private fun checkInterfaceOrientation(skipIfAutoRotationIsDisabled: Boolean = true) {
173+
if (skipIfAutoRotationIsDisabled && !mAutoRotationObserver.getLastAutoRotationStatus()) {
174174
return
175175
}
176176

177-
val supportsLandscape =
178-
mUtils.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
179-
if (isLocked && !supportsLandscape) {
177+
if (isLocked) {
180178
return
181179
}
182180

183-
var newInterfaceOrientation = mUtils.convertToInterfaceOrientationFrom(deviceOrientation);
184-
185-
/**
186-
* When the device orientation is either face up or face down,
187-
* we can't match it to an interface orientation, because
188-
* it could be either portrait or any landscape.
189-
* So we read it from the system itself.
190-
*/
191-
if (newInterfaceOrientation == Orientation.UNKNOWN) {
181+
if (lastDeviceOrientation != Orientation.LANDSCAPE_RIGHT && lastDeviceOrientation != Orientation.LANDSCAPE_LEFT) {
192182
val rotation = mUtils.getInterfaceRotation()
193-
newInterfaceOrientation = mUtils.convertToOrientationFromScreenRotation(rotation)
194-
}
183+
val newInterfaceOrientation = mUtils.convertToOrientationFromScreenRotation(rotation)
195184

196-
/**
197-
* This differs from iOS because we can't read the actual orientation of the interface,
198-
* we read its rotation.
199-
* This means that even if the requestedOrientation of the currentActivity is locked to landscape
200-
* it reads every possible orientation and this is not what we want.
201-
* Instead, we check that its value is either LANDSCAPE_RIGHT or LANDSCAPE_LEFT, otherwise we
202-
* exit
203-
*/
204-
val newInterfaceOrientationIsNotLandscape =
205-
newInterfaceOrientation != Orientation.LANDSCAPE_RIGHT
206-
&& newInterfaceOrientation != Orientation.LANDSCAPE_LEFT;
207-
if (supportsLandscape && newInterfaceOrientationIsNotLandscape) {
185+
updateLastInterfaceOrientationTo(newInterfaceOrientation)
208186
return
209187
}
210188

211-
if (newInterfaceOrientation == lastInterfaceOrientation) {
212-
return
213-
}
189+
/**
190+
* The reason we invert the interface orientation is to match iOS behavior with
191+
* UIInterfaceOrientation when device is in landscape mode
192+
*/
193+
val interfaceOrientationBasedOnDeviceOne =
194+
if (lastDeviceOrientation == Orientation.LANDSCAPE_RIGHT) {
195+
Orientation.LANDSCAPE_LEFT
196+
} else {
197+
Orientation.LANDSCAPE_RIGHT
198+
}
214199

215-
updateLastInterfaceOrientationTo(newInterfaceOrientation)
200+
updateLastInterfaceOrientationTo(interfaceOrientationBasedOnDeviceOne)
216201
}
217202

218203
private fun updateIsLockedTo(value: Boolean) {
@@ -221,6 +206,10 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re
221206
}
222207

223208
private fun updateLastInterfaceOrientationTo(value: Orientation) {
209+
if (value == lastInterfaceOrientation) {
210+
return
211+
}
212+
224213
lastInterfaceOrientation = value
225214
mEventManager.sendInterfaceOrientationDidChange(value.ordinal)
226215
}

android/src/main/java/com/orientationdirector/implementation/Utils.kt

Lines changed: 39 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,47 @@ class Utils(private val context: ReactContext) {
2020
}
2121

2222
fun convertToDeviceOrientationFrom(orientationAngles: FloatArray): Orientation {
23-
val (_, pitchRadians, rollRadians) = orientationAngles;
24-
25-
val pitchDegrees = Math.toDegrees(pitchRadians.toDouble()).toFloat()
26-
val rollDegrees = Math.toDegrees(rollRadians.toDouble()).toFloat()
27-
28-
// This is needed to account for inaccuracy due to subtle movements such as tilting
29-
val pitchToleranceDefault = 5f
30-
val rollTolerance = 0f
31-
val toleranceForFaceUpOrDown = 5f;
32-
33-
//////////////////////////////////////
34-
// These limits are set based on SensorManager.getOrientation reference
35-
// https://developer.android.com/develop/sensors-and-location/sensors/sensors_position#sensors-pos-orient
36-
//
37-
val portraitLimit = -90f
38-
val landscapeRightLimit = 180f
39-
val landscapeLeftLimit = -180f
40-
//
41-
//////////////////////////////////////
42-
43-
val isPitchInLandscapeModeRange =
44-
checkIfValueIsBetweenTolerance(pitchDegrees, pitchToleranceDefault)
45-
val isPitchCloseToFaceUpOrDown =
46-
checkIfValueIsBetweenTolerance(pitchDegrees, toleranceForFaceUpOrDown)
23+
if (orientationAngles.size < 3) {
24+
return Orientation.PORTRAIT
25+
}
26+
27+
val (_, pitchRadians, rollRadians) = orientationAngles
28+
29+
val pitch = Math.toDegrees(pitchRadians.toDouble()).toFloat()
30+
val roll = Math.toDegrees(rollRadians.toDouble()).toFloat()
31+
32+
val faceUpDownPitchTolerance = 30f
33+
34+
fun isValueCloseTo(value: Float, target: Float, tolerance: Float): Boolean {
35+
return value in (target - tolerance)..(target + tolerance)
36+
}
4737

4838
return when {
49-
checkIfRollIsCloseToFaceUp(rollDegrees) && isPitchCloseToFaceUpOrDown -> Orientation.FACE_UP
50-
checkIfRollIsCloseToFaceDown(rollDegrees) && isPitchCloseToFaceUpOrDown -> Orientation.FACE_DOWN
51-
rollDegrees in rollTolerance..landscapeRightLimit - rollTolerance && isPitchInLandscapeModeRange -> Orientation.LANDSCAPE_RIGHT
52-
rollDegrees in landscapeLeftLimit + rollTolerance..-rollTolerance && isPitchInLandscapeModeRange -> Orientation.LANDSCAPE_LEFT
53-
pitchDegrees in portraitLimit..pitchToleranceDefault -> Orientation.PORTRAIT
54-
else -> Orientation.PORTRAIT_UPSIDE_DOWN
39+
// Face up: device is lying flat with screen up
40+
isValueCloseTo(pitch, 0f, faceUpDownPitchTolerance) &&
41+
isValueCloseTo(roll, 0f, faceUpDownPitchTolerance) -> Orientation.FACE_UP
42+
43+
// Face down: device is lying flat with screen down
44+
isValueCloseTo(pitch, 0f, faceUpDownPitchTolerance) &&
45+
(isValueCloseTo(roll, 180f, faceUpDownPitchTolerance) || isValueCloseTo(
46+
roll,
47+
-180f,
48+
faceUpDownPitchTolerance
49+
)) -> Orientation.FACE_DOWN
50+
51+
// Portrait
52+
isValueCloseTo(pitch, -90f, 45f) -> Orientation.PORTRAIT
53+
54+
// Portrait upside down
55+
isValueCloseTo(pitch, 90f, 45f) -> Orientation.PORTRAIT_UPSIDE_DOWN
56+
57+
// Landscape left
58+
isValueCloseTo(roll, -90f, 45f) -> Orientation.LANDSCAPE_LEFT
59+
60+
// Landscape right
61+
isValueCloseTo(roll, 90f, 45f) -> Orientation.LANDSCAPE_RIGHT
62+
63+
else -> Orientation.PORTRAIT
5564
}
5665
}
5766

@@ -93,32 +102,4 @@ class Utils(private val context: ReactContext) {
93102
else -> Orientation.UNKNOWN
94103
}
95104
}
96-
97-
fun getRequestedOrientation(): Int {
98-
if (context.currentActivity?.requestedOrientation == null) {
99-
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
100-
}
101-
102-
return context.currentActivity!!.requestedOrientation;
103-
}
104-
105-
private fun checkIfValueIsBetweenTolerance(value: Float, tolerance: Float): Boolean {
106-
return value > -tolerance && value < tolerance
107-
}
108-
109-
private fun checkIfRollIsCloseToFaceDown(value: Float): Boolean {
110-
val landscapeLimit = 180f
111-
val faceDownLimit = 170f
112-
113-
return value in faceDownLimit..landscapeLimit ||
114-
value in -landscapeLimit..-faceDownLimit;
115-
}
116-
117-
private fun checkIfRollIsCloseToFaceUp(value: Float): Boolean {
118-
val landscapeLimit = 0f
119-
val faceUpLimit = 10f
120-
121-
return value in landscapeLimit..faceUpLimit ||
122-
value in -faceUpLimit..-landscapeLimit
123-
}
124105
}

0 commit comments

Comments
 (0)