diff --git a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt index cdf36bf..27cd29b 100644 --- a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt +++ b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt @@ -26,8 +26,8 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re private var didComputeInitialDeviceOrientation = false; init { - mOrientationSensorsEventListener.setOnOrientationAnglesChangedCallback { orientation -> - onOrientationAnglesChanged(orientation) + mOrientationSensorsEventListener.setOnDeviceOrientationChangedCallback { orientation -> + onDeviceOrientationChanged(orientation) } mAutoRotationObserver.enable() @@ -151,15 +151,10 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re ).contains(activity.requestedOrientation) } - private fun onOrientationAnglesChanged(orientationAngles: FloatArray) { - val deviceOrientation = mUtils.convertToDeviceOrientationFrom(orientationAngles) - if (deviceOrientation == Orientation.UNKNOWN) { - return - } + private fun onDeviceOrientationChanged(deviceOrientation: Orientation) { + if (deviceOrientation == Orientation.UNKNOWN) return - if (lastDeviceOrientation == deviceOrientation) { - return - } + if (lastDeviceOrientation == deviceOrientation) return mEventManager.sendDeviceOrientationDidChange(deviceOrientation.ordinal) lastDeviceOrientation = deviceOrientation @@ -168,9 +163,8 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re // NOTE(2.init): This is needed to disable sensors if they were needed just for the initial // device computation. - if (didComputeInitialDeviceOrientation) { - return - } + if (didComputeInitialDeviceOrientation) return + didComputeInitialDeviceOrientation = true if (!areOrientationSensorsEnabled) { @@ -179,13 +173,9 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re } private fun checkInterfaceOrientation(skipIfAutoRotationIsDisabled: Boolean = true) { - if (skipIfAutoRotationIsDisabled && !mAutoRotationObserver.getLastAutoRotationStatus()) { - return - } + if (skipIfAutoRotationIsDisabled && !mAutoRotationObserver.getLastAutoRotationStatus()) return - if (isLocked) { - return - } + if (isLocked) return if (lastDeviceOrientation != Orientation.LANDSCAPE_RIGHT && lastDeviceOrientation != Orientation.LANDSCAPE_LEFT) { val rotation = mUtils.getInterfaceRotation() diff --git a/android/src/main/java/com/orientationdirector/implementation/OrientationSensorsEventListener.kt b/android/src/main/java/com/orientationdirector/implementation/OrientationSensorsEventListener.kt index 4790e3f..0f388e3 100644 --- a/android/src/main/java/com/orientationdirector/implementation/OrientationSensorsEventListener.kt +++ b/android/src/main/java/com/orientationdirector/implementation/OrientationSensorsEventListener.kt @@ -5,52 +5,32 @@ import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager +import android.view.OrientationEventListener import com.facebook.react.bridge.ReactApplicationContext class OrientationSensorsEventListener( context: ReactApplicationContext, -) : SensorEventListener { +) : SensorEventListener, OrientationEventListener(context, SensorManager.SENSOR_DELAY_UI) { + private val rotationMatrix = FloatArray(9) + private var mSensorManager: SensorManager = context.getSystemService(SENSOR_SERVICE) as SensorManager - private var mRotationSensor: Sensor? = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) - private var mAccelerometerSensor: Sensor? = - mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) - private var mMagneticFieldSensor: Sensor? = - mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) - private var hasRotationSensor: Boolean = - mRotationSensor != null - private var hasAccelerometerAndMagneticFieldSensors: Boolean = - mAccelerometerSensor != null && mMagneticFieldSensor != null + mRotationSensor != null - private val accelerometerReading = FloatArray(3) - private val magnetometerReading = FloatArray(3) - - private var lastComputedOrientationAngles = FloatArray(3) - private var onOrientationAnglesChangedCallback: ((orientationAngles: FloatArray) -> Unit)? = null - - fun setOnOrientationAnglesChangedCallback(callback: (orientation: FloatArray) -> Unit) { - onOrientationAnglesChangedCallback = callback - } - - override fun onSensorChanged(event: SensorEvent?) { - if (event == null) { - return - } - - if (event.sensor.type == Sensor.TYPE_ROTATION_VECTOR) { - computeOrientationFromRotationSensor(event.values); - return - } + private var lastComputedDeviceOrientation = Orientation.UNKNOWN + private var lastComputedFaceOrientation = Orientation.UNKNOWN - computeOrientationFromOtherSensors(event) + private var onDeviceOrientationChangedCallback: ((deviceOrientation: Orientation) -> Unit)? = null + fun setOnDeviceOrientationChangedCallback(callback: (deviceOrientation: Orientation) -> Unit) { + onDeviceOrientationChangedCallback = callback } - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} + override fun enable() { + super.enable() - fun enable() { if (hasRotationSensor) { mSensorManager.registerListener( this, @@ -58,72 +38,85 @@ class OrientationSensorsEventListener( SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI ) - return } - if (hasAccelerometerAndMagneticFieldSensors) { - mSensorManager.registerListener( - this, - mAccelerometerSensor, - SensorManager.SENSOR_DELAY_NORMAL, - SensorManager.SENSOR_DELAY_UI - ) - mSensorManager.registerListener( - this, - mMagneticFieldSensor, - SensorManager.SENSOR_DELAY_NORMAL, - SensorManager.SENSOR_DELAY_UI - ) - return - } + lastComputedDeviceOrientation = Orientation.UNKNOWN + lastComputedFaceOrientation = Orientation.UNKNOWN } - fun disable() { - mSensorManager.unregisterListener(this) - } - - private fun computeOrientationFromRotationSensor(values: FloatArray) { - val rotationMatrix = FloatArray(9) - SensorManager.getRotationMatrixFromVector(rotationMatrix, values) + override fun disable() { + super.disable() - val orientationAngles = FloatArray(3) - SensorManager.getOrientation(rotationMatrix, orientationAngles) + if (hasRotationSensor) { + mSensorManager.unregisterListener(this) + } - notifyOrientationAnglesChanged(orientationAngles) + lastComputedDeviceOrientation = Orientation.UNKNOWN + lastComputedFaceOrientation = Orientation.UNKNOWN } - private fun computeOrientationFromOtherSensors(event: SensorEvent) { - if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) { - System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size) + override fun onOrientationChanged(angleDegrees: Int) { + if (angleDegrees == ORIENTATION_UNKNOWN) { + lastComputedDeviceOrientation = Orientation.UNKNOWN + return } - if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) { - System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size) + val currentDeviceOrientation = when (angleDegrees) { + in LANDSCAPE_RIGHT_START .. LANDSCAPE_RIGHT_END -> Orientation.LANDSCAPE_RIGHT + in PORTRAIT_UPSIDE_DOWN_START..PORTRAIT_UPSIDE_DOWN_END -> Orientation.PORTRAIT_UPSIDE_DOWN + in LANDSCAPE_LEFT_START..LANDSCAPE_LEFT_END -> Orientation.LANDSCAPE_LEFT + else -> Orientation.PORTRAIT } - val rotationMatrix = FloatArray(9) - val didComputeMatrix = SensorManager.getRotationMatrix( - rotationMatrix, - null, - accelerometerReading, - magnetometerReading - ) - if (!didComputeMatrix) { + if (currentDeviceOrientation == lastComputedDeviceOrientation) return + + notifyDeviceOrientationChanged(currentDeviceOrientation) + } + + override fun onSensorChanged(event: SensorEvent) { + if (event.sensor.type != Sensor.TYPE_ROTATION_VECTOR) return + + if (lastComputedDeviceOrientation != Orientation.UNKNOWN) { + lastComputedFaceOrientation = Orientation.UNKNOWN return } - val orientationAngles = FloatArray(3) - SensorManager.getOrientation(rotationMatrix, orientationAngles) + SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values) + + val zUp = rotationMatrix[8] + val currentFaceOrientation = when { + zUp > FACE_UP_Z_THRESHOLD -> Orientation.FACE_UP + zUp < -FACE_DOWN_Z_THRESHOLD -> Orientation.FACE_DOWN + else -> null + } + + if (currentFaceOrientation == null) return + + if (currentFaceOrientation == lastComputedFaceOrientation) return - notifyOrientationAnglesChanged(orientationAngles) + notifyFaceOrientationChanged(currentFaceOrientation) } - private fun notifyOrientationAnglesChanged(orientationAngles: FloatArray) { - if (lastComputedOrientationAngles.contentEquals(orientationAngles)) { - return - } + private fun notifyDeviceOrientationChanged(deviceOrientation: Orientation) { + lastComputedDeviceOrientation = deviceOrientation + onDeviceOrientationChangedCallback?.invoke(deviceOrientation) + } + + private fun notifyFaceOrientationChanged(faceOrientation: Orientation) { + lastComputedFaceOrientation = faceOrientation + onDeviceOrientationChangedCallback?.invoke(faceOrientation) + } - onOrientationAnglesChangedCallback?.invoke(orientationAngles) - lastComputedOrientationAngles = orientationAngles + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit + + companion object { + private const val LANDSCAPE_RIGHT_START = 45 + private const val LANDSCAPE_RIGHT_END = 134 + private const val PORTRAIT_UPSIDE_DOWN_START = 135 + private const val PORTRAIT_UPSIDE_DOWN_END = 224 + private const val LANDSCAPE_LEFT_START = 225 + private const val LANDSCAPE_LEFT_END = 314 + private const val FACE_UP_Z_THRESHOLD = 0.906f + private const val FACE_DOWN_Z_THRESHOLD = 0.906f } } diff --git a/android/src/main/java/com/orientationdirector/implementation/Utils.kt b/android/src/main/java/com/orientationdirector/implementation/Utils.kt index 0553307..915084a 100644 --- a/android/src/main/java/com/orientationdirector/implementation/Utils.kt +++ b/android/src/main/java/com/orientationdirector/implementation/Utils.kt @@ -19,51 +19,6 @@ 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() - 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 { - // Face up: device is lying flat with screen 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, faceUpDownPitchTolerance) || isValueCloseTo( - roll, - -180f, - faceUpDownPitchTolerance - )) -> Orientation.FACE_DOWN - - // Portrait - 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.UNKNOWN - } - } - fun convertToActivityOrientationFrom(orientation: Orientation): Int { return when (orientation) { Orientation.LANDSCAPE_RIGHT -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE @@ -93,13 +48,4 @@ class Utils(private val context: ReactContext) { } } - fun convertToInterfaceOrientationFrom(deviceOrientation: Orientation): Orientation { - return when (deviceOrientation) { - Orientation.PORTRAIT -> Orientation.PORTRAIT - Orientation.LANDSCAPE_RIGHT -> Orientation.LANDSCAPE_LEFT - Orientation.PORTRAIT_UPSIDE_DOWN -> Orientation.PORTRAIT_UPSIDE_DOWN - Orientation.LANDSCAPE_LEFT -> Orientation.LANDSCAPE_RIGHT - else -> Orientation.UNKNOWN - } - } }