Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,125 +5,118 @@ 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,
mRotationSensor,
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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
}