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" -> {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-rotation-module",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Screen Orientation Native Module",
5
5
  "main": "index.js",
6
6
  "types": "src/index.d.ts",