expo-rotation-module 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -11,6 +11,14 @@ import android.util.Log
|
|
|
11
11
|
import android.content.pm.ActivityInfo
|
|
12
12
|
import expo.modules.kotlin.modules.Module
|
|
13
13
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
14
|
+
import android.hardware.Sensor
|
|
15
|
+
import android.hardware.SensorEvent
|
|
16
|
+
import android.hardware.SensorEventListener
|
|
17
|
+
import android.hardware.SensorManager
|
|
18
|
+
import kotlin.math.atan2
|
|
19
|
+
import kotlin.math.sqrt
|
|
20
|
+
import kotlin.math.abs
|
|
21
|
+
|
|
14
22
|
|
|
15
23
|
class ExpoRotationModule : Module() {
|
|
16
24
|
companion object {
|
|
@@ -22,6 +30,19 @@ class ExpoRotationModule : Module() {
|
|
|
22
30
|
private var desiredAxis: String? = null
|
|
23
31
|
private var lastWrittenRotation: Int = -1
|
|
24
32
|
|
|
33
|
+
// Sensor-related fields for tilt detection and debouncing
|
|
34
|
+
private var sensorManager: SensorManager? = null
|
|
35
|
+
private var accelSensor: Sensor? = null
|
|
36
|
+
private var accelListener: SensorEventListener? = null
|
|
37
|
+
// tilt threshold (degrees) below which device is considered "flat"
|
|
38
|
+
private val tiltThresholdDegrees = 20f
|
|
39
|
+
// debounce: must see same rot candidate this many times before writing
|
|
40
|
+
private var stableCount = 0
|
|
41
|
+
private val stableRequired = 3
|
|
42
|
+
private var lastRotCandidate = -1
|
|
43
|
+
// last measured inclination in degrees (0 = flat, 90 = upright)
|
|
44
|
+
private var lastInclinationDeg = 90f
|
|
45
|
+
|
|
25
46
|
override fun definition() = ModuleDefinition {
|
|
26
47
|
Name(NAME)
|
|
27
48
|
|
|
@@ -51,6 +72,22 @@ class ExpoRotationModule : Module() {
|
|
|
51
72
|
orientationListener?.disable()
|
|
52
73
|
orientationListener = null
|
|
53
74
|
desiredAxis = null
|
|
75
|
+
lastWrittenRotation = -1
|
|
76
|
+
// unregister accelerometer listener if registered
|
|
77
|
+
try {
|
|
78
|
+
if (sensorManager != null && accelListener != null) {
|
|
79
|
+
sensorManager?.unregisterListener(accelListener)
|
|
80
|
+
}
|
|
81
|
+
} catch (e: Exception) {
|
|
82
|
+
Log.e(TAG, "stopOrientationListener sensor unregister error", e)
|
|
83
|
+
}
|
|
84
|
+
accelListener = null
|
|
85
|
+
accelSensor = null
|
|
86
|
+
sensorManager = null
|
|
87
|
+
// reset debounce
|
|
88
|
+
stableCount = 0
|
|
89
|
+
lastRotCandidate = -1
|
|
90
|
+
lastInclinationDeg = 90f
|
|
54
91
|
return@Function null
|
|
55
92
|
}
|
|
56
93
|
|
|
@@ -165,6 +202,32 @@ class ExpoRotationModule : Module() {
|
|
|
165
202
|
// If listener already exists, do nothing
|
|
166
203
|
if (orientationListener != null) return
|
|
167
204
|
|
|
205
|
+
// set up accelerometer to measure inclination
|
|
206
|
+
sensorManager = ctx.getSystemService(Context.SENSOR_SERVICE) as? SensorManager
|
|
207
|
+
accelSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
|
|
208
|
+
accelListener = object : SensorEventListener {
|
|
209
|
+
override fun onSensorChanged(event: SensorEvent) {
|
|
210
|
+
val x = event.values[0].toDouble()
|
|
211
|
+
val y = event.values[1].toDouble()
|
|
212
|
+
val z = event.values[2].toDouble()
|
|
213
|
+
val g = sqrt(x * x + y * y + z * z)
|
|
214
|
+
if (g > 0.0) {
|
|
215
|
+
var inclRad = atan2(z, sqrt(x * x + y * y))
|
|
216
|
+
var inclDeg = Math.toDegrees(inclRad).toFloat()
|
|
217
|
+
inclDeg = abs(inclDeg)
|
|
218
|
+
// clamp
|
|
219
|
+
if (inclDeg > 90f) inclDeg = 90f
|
|
220
|
+
lastInclinationDeg = inclDeg
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
|
|
225
|
+
}
|
|
226
|
+
if (accelSensor != null) {
|
|
227
|
+
// Use UI delay for reasonable responsiveness
|
|
228
|
+
sensorManager?.registerListener(accelListener, accelSensor, SensorManager.SENSOR_DELAY_UI)
|
|
229
|
+
}
|
|
230
|
+
|
|
168
231
|
orientationListener = object : android.view.OrientationEventListener(ctx) {
|
|
169
232
|
override fun onOrientationChanged(orientation: Int) {
|
|
170
233
|
// orientation: 0..359 degrees, or ORIENTATION_UNKNOWN (-1)
|
|
@@ -179,6 +242,23 @@ class ExpoRotationModule : Module() {
|
|
|
179
242
|
|
|
180
243
|
try {
|
|
181
244
|
val resolver: ContentResolver = ctx.contentResolver
|
|
245
|
+
|
|
246
|
+
// If device is nearly flat, skip writing to avoid noise
|
|
247
|
+
if (lastInclinationDeg < tiltThresholdDegrees) {
|
|
248
|
+
lastRotCandidate = -1
|
|
249
|
+
stableCount = 0
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Debounce: require several consistent buckets
|
|
254
|
+
if (rot == lastRotCandidate) {
|
|
255
|
+
stableCount += 1
|
|
256
|
+
} else {
|
|
257
|
+
lastRotCandidate = rot
|
|
258
|
+
stableCount = 1
|
|
259
|
+
}
|
|
260
|
+
if (stableCount < stableRequired) return
|
|
261
|
+
|
|
182
262
|
// desiredAxis controls whether we want portrait or landscape axis.
|
|
183
263
|
when (desiredAxis) {
|
|
184
264
|
"PORTRAIT" -> {
|