Skip to content

Commit ba58945

Browse files
authored
feat(android): rewrite sensor logic to improve reliability (#103)
1 parent 32e6a84 commit ba58945

File tree

3 files changed

+82
-153
lines changed

3 files changed

+82
-153
lines changed

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

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re
2626
private var didComputeInitialDeviceOrientation = false;
2727

2828
init {
29-
mOrientationSensorsEventListener.setOnOrientationAnglesChangedCallback { orientation ->
30-
onOrientationAnglesChanged(orientation)
29+
mOrientationSensorsEventListener.setOnDeviceOrientationChangedCallback { orientation ->
30+
onDeviceOrientationChanged(orientation)
3131
}
3232

3333
mAutoRotationObserver.enable()
@@ -151,15 +151,10 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re
151151
).contains(activity.requestedOrientation)
152152
}
153153

154-
private fun onOrientationAnglesChanged(orientationAngles: FloatArray) {
155-
val deviceOrientation = mUtils.convertToDeviceOrientationFrom(orientationAngles)
156-
if (deviceOrientation == Orientation.UNKNOWN) {
157-
return
158-
}
154+
private fun onDeviceOrientationChanged(deviceOrientation: Orientation) {
155+
if (deviceOrientation == Orientation.UNKNOWN) return
159156

160-
if (lastDeviceOrientation == deviceOrientation) {
161-
return
162-
}
157+
if (lastDeviceOrientation == deviceOrientation) return
163158

164159
mEventManager.sendDeviceOrientationDidChange(deviceOrientation.ordinal)
165160
lastDeviceOrientation = deviceOrientation
@@ -168,9 +163,8 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re
168163

169164
// NOTE(2.init): This is needed to disable sensors if they were needed just for the initial
170165
// device computation.
171-
if (didComputeInitialDeviceOrientation) {
172-
return
173-
}
166+
if (didComputeInitialDeviceOrientation) return
167+
174168
didComputeInitialDeviceOrientation = true
175169

176170
if (!areOrientationSensorsEnabled) {
@@ -179,13 +173,9 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re
179173
}
180174

181175
private fun checkInterfaceOrientation(skipIfAutoRotationIsDisabled: Boolean = true) {
182-
if (skipIfAutoRotationIsDisabled && !mAutoRotationObserver.getLastAutoRotationStatus()) {
183-
return
184-
}
176+
if (skipIfAutoRotationIsDisabled && !mAutoRotationObserver.getLastAutoRotationStatus()) return
185177

186-
if (isLocked) {
187-
return
188-
}
178+
if (isLocked) return
189179

190180
if (lastDeviceOrientation != Orientation.LANDSCAPE_RIGHT && lastDeviceOrientation != Orientation.LANDSCAPE_LEFT) {
191181
val rotation = mUtils.getInterfaceRotation()

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

Lines changed: 73 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,125 +5,118 @@ import android.hardware.Sensor
55
import android.hardware.SensorEvent
66
import android.hardware.SensorEventListener
77
import android.hardware.SensorManager
8+
import android.view.OrientationEventListener
89
import com.facebook.react.bridge.ReactApplicationContext
910

1011
class OrientationSensorsEventListener(
1112
context: ReactApplicationContext,
12-
) : SensorEventListener {
13+
) : SensorEventListener, OrientationEventListener(context, SensorManager.SENSOR_DELAY_UI) {
14+
private val rotationMatrix = FloatArray(9)
15+
1316
private var mSensorManager: SensorManager =
1417
context.getSystemService(SENSOR_SERVICE) as SensorManager
15-
1618
private var mRotationSensor: Sensor? =
1719
mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
18-
private var mAccelerometerSensor: Sensor? =
19-
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
20-
private var mMagneticFieldSensor: Sensor? =
21-
mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
22-
2320
private var hasRotationSensor: Boolean =
24-
mRotationSensor != null
25-
private var hasAccelerometerAndMagneticFieldSensors: Boolean =
26-
mAccelerometerSensor != null && mMagneticFieldSensor != null
21+
mRotationSensor != null
2722

28-
private val accelerometerReading = FloatArray(3)
29-
private val magnetometerReading = FloatArray(3)
30-
31-
private var lastComputedOrientationAngles = FloatArray(3)
32-
private var onOrientationAnglesChangedCallback: ((orientationAngles: FloatArray) -> Unit)? = null
33-
34-
fun setOnOrientationAnglesChangedCallback(callback: (orientation: FloatArray) -> Unit) {
35-
onOrientationAnglesChangedCallback = callback
36-
}
37-
38-
override fun onSensorChanged(event: SensorEvent?) {
39-
if (event == null) {
40-
return
41-
}
42-
43-
if (event.sensor.type == Sensor.TYPE_ROTATION_VECTOR) {
44-
computeOrientationFromRotationSensor(event.values);
45-
return
46-
}
23+
private var lastComputedDeviceOrientation = Orientation.UNKNOWN
24+
private var lastComputedFaceOrientation = Orientation.UNKNOWN
4725

48-
computeOrientationFromOtherSensors(event)
26+
private var onDeviceOrientationChangedCallback: ((deviceOrientation: Orientation) -> Unit)? = null
27+
fun setOnDeviceOrientationChangedCallback(callback: (deviceOrientation: Orientation) -> Unit) {
28+
onDeviceOrientationChangedCallback = callback
4929
}
5030

51-
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
31+
override fun enable() {
32+
super.enable()
5233

53-
fun enable() {
5434
if (hasRotationSensor) {
5535
mSensorManager.registerListener(
5636
this,
5737
mRotationSensor,
5838
SensorManager.SENSOR_DELAY_NORMAL,
5939
SensorManager.SENSOR_DELAY_UI
6040
)
61-
return
6241
}
6342

64-
if (hasAccelerometerAndMagneticFieldSensors) {
65-
mSensorManager.registerListener(
66-
this,
67-
mAccelerometerSensor,
68-
SensorManager.SENSOR_DELAY_NORMAL,
69-
SensorManager.SENSOR_DELAY_UI
70-
)
71-
mSensorManager.registerListener(
72-
this,
73-
mMagneticFieldSensor,
74-
SensorManager.SENSOR_DELAY_NORMAL,
75-
SensorManager.SENSOR_DELAY_UI
76-
)
77-
return
78-
}
43+
lastComputedDeviceOrientation = Orientation.UNKNOWN
44+
lastComputedFaceOrientation = Orientation.UNKNOWN
7945
}
8046

81-
fun disable() {
82-
mSensorManager.unregisterListener(this)
83-
}
84-
85-
private fun computeOrientationFromRotationSensor(values: FloatArray) {
86-
val rotationMatrix = FloatArray(9)
87-
SensorManager.getRotationMatrixFromVector(rotationMatrix, values)
47+
override fun disable() {
48+
super.disable()
8849

89-
val orientationAngles = FloatArray(3)
90-
SensorManager.getOrientation(rotationMatrix, orientationAngles)
50+
if (hasRotationSensor) {
51+
mSensorManager.unregisterListener(this)
52+
}
9153

92-
notifyOrientationAnglesChanged(orientationAngles)
54+
lastComputedDeviceOrientation = Orientation.UNKNOWN
55+
lastComputedFaceOrientation = Orientation.UNKNOWN
9356
}
9457

95-
private fun computeOrientationFromOtherSensors(event: SensorEvent) {
96-
if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
97-
System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)
58+
override fun onOrientationChanged(angleDegrees: Int) {
59+
if (angleDegrees == ORIENTATION_UNKNOWN) {
60+
lastComputedDeviceOrientation = Orientation.UNKNOWN
61+
return
9862
}
9963

100-
if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {
101-
System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)
64+
val currentDeviceOrientation = when (angleDegrees) {
65+
in LANDSCAPE_RIGHT_START .. LANDSCAPE_RIGHT_END -> Orientation.LANDSCAPE_RIGHT
66+
in PORTRAIT_UPSIDE_DOWN_START..PORTRAIT_UPSIDE_DOWN_END -> Orientation.PORTRAIT_UPSIDE_DOWN
67+
in LANDSCAPE_LEFT_START..LANDSCAPE_LEFT_END -> Orientation.LANDSCAPE_LEFT
68+
else -> Orientation.PORTRAIT
10269
}
10370

104-
val rotationMatrix = FloatArray(9)
105-
val didComputeMatrix = SensorManager.getRotationMatrix(
106-
rotationMatrix,
107-
null,
108-
accelerometerReading,
109-
magnetometerReading
110-
)
111-
if (!didComputeMatrix) {
71+
if (currentDeviceOrientation == lastComputedDeviceOrientation) return
72+
73+
notifyDeviceOrientationChanged(currentDeviceOrientation)
74+
}
75+
76+
override fun onSensorChanged(event: SensorEvent) {
77+
if (event.sensor.type != Sensor.TYPE_ROTATION_VECTOR) return
78+
79+
if (lastComputedDeviceOrientation != Orientation.UNKNOWN) {
80+
lastComputedFaceOrientation = Orientation.UNKNOWN
11281
return
11382
}
11483

115-
val orientationAngles = FloatArray(3)
116-
SensorManager.getOrientation(rotationMatrix, orientationAngles)
84+
SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values)
85+
86+
val zUp = rotationMatrix[8]
87+
val currentFaceOrientation = when {
88+
zUp > FACE_UP_Z_THRESHOLD -> Orientation.FACE_UP
89+
zUp < -FACE_DOWN_Z_THRESHOLD -> Orientation.FACE_DOWN
90+
else -> null
91+
}
92+
93+
if (currentFaceOrientation == null) return
94+
95+
if (currentFaceOrientation == lastComputedFaceOrientation) return
11796

118-
notifyOrientationAnglesChanged(orientationAngles)
97+
notifyFaceOrientationChanged(currentFaceOrientation)
11998
}
12099

121-
private fun notifyOrientationAnglesChanged(orientationAngles: FloatArray) {
122-
if (lastComputedOrientationAngles.contentEquals(orientationAngles)) {
123-
return
124-
}
100+
private fun notifyDeviceOrientationChanged(deviceOrientation: Orientation) {
101+
lastComputedDeviceOrientation = deviceOrientation
102+
onDeviceOrientationChangedCallback?.invoke(deviceOrientation)
103+
}
104+
105+
private fun notifyFaceOrientationChanged(faceOrientation: Orientation) {
106+
lastComputedFaceOrientation = faceOrientation
107+
onDeviceOrientationChangedCallback?.invoke(faceOrientation)
108+
}
125109

126-
onOrientationAnglesChangedCallback?.invoke(orientationAngles)
127-
lastComputedOrientationAngles = orientationAngles
110+
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit
111+
112+
companion object {
113+
private const val LANDSCAPE_RIGHT_START = 45
114+
private const val LANDSCAPE_RIGHT_END = 134
115+
private const val PORTRAIT_UPSIDE_DOWN_START = 135
116+
private const val PORTRAIT_UPSIDE_DOWN_END = 224
117+
private const val LANDSCAPE_LEFT_START = 225
118+
private const val LANDSCAPE_LEFT_END = 314
119+
private const val FACE_UP_Z_THRESHOLD = 0.906f
120+
private const val FACE_DOWN_Z_THRESHOLD = 0.906f
128121
}
129122
}

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

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,51 +19,6 @@ class Utils(private val context: ReactContext) {
1919
}
2020
}
2121

22-
fun convertToDeviceOrientationFrom(orientationAngles: FloatArray): Orientation {
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-
}
37-
38-
return when {
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.UNKNOWN
64-
}
65-
}
66-
6722
fun convertToActivityOrientationFrom(orientation: Orientation): Int {
6823
return when (orientation) {
6924
Orientation.LANDSCAPE_RIGHT -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
@@ -93,13 +48,4 @@ class Utils(private val context: ReactContext) {
9348
}
9449
}
9550

96-
fun convertToInterfaceOrientationFrom(deviceOrientation: Orientation): Orientation {
97-
return when (deviceOrientation) {
98-
Orientation.PORTRAIT -> Orientation.PORTRAIT
99-
Orientation.LANDSCAPE_RIGHT -> Orientation.LANDSCAPE_LEFT
100-
Orientation.PORTRAIT_UPSIDE_DOWN -> Orientation.PORTRAIT_UPSIDE_DOWN
101-
Orientation.LANDSCAPE_LEFT -> Orientation.LANDSCAPE_RIGHT
102-
else -> Orientation.UNKNOWN
103-
}
104-
}
10551
}

0 commit comments

Comments
 (0)