Custom Rotation observer which works like ActivityInfo.SCREEN_ORIENTATION_SENSOR (android:screenOrientation="sensor") in Activities

46 views Asked by At

I need to get phone rotation values similar to which we can get in Activities with Sensor orientation (ActivityInfo.SCREEN_ORIENTATION_SENSOR / android:screenOrientation="sensor").

But I need to get it in background (a foreground service or app process) when app may not be visible or screen turned off, that's why Display.rotation or Resources.configuration.orientation won't work in this case.

In this case OrientationEventListener can help and it works in background with screen turned off. But how to covert orientation value to rotation in the similar logic like it's done in Activities by default with Sensor orientation? So it would look the same and natural like in activities - config changes on orientation.

I implemented the following method but still it doesn't really the same and not that good as orientation changes in Activity.

// Orientation hysteresis amount used in rounding, in degrees
private const val ORIENTATION_HYSTERESIS = 5

class RotationLiveData(context: Context) : LiveData<Int>(
    // initial rotation
    context.getSystemService<DisplayManager>()!!.getDisplay(Display.DEFAULT_DISPLAY).rotation
) {

    private val listener = object : OrientationEventListener(
        context.applicationContext,
    ) {
        private var orientationHistory = ORIENTATION_UNKNOWN

        override fun onOrientationChanged(orientation: Int) {
            if (orientation == ORIENTATION_UNKNOWN) return

            val changeOrientation = if (orientationHistory == ORIENTATION_UNKNOWN) {
                true
            } else {
                var dist = abs(orientation - orientationHistory)
                dist = dist.coerceAtMost(360 - dist)
                dist >= 45 + ORIENTATION_HYSTERESIS
            }
            if (changeOrientation) {
                orientationHistory = (orientation + 45) / 90 * 90 % 360

                val rotation = when (orientationHistory) {
                    0 -> Surface.ROTATION_0
                    90 -> Surface.ROTATION_270
                    180 -> return // Surface.ROTATION_180 ignore (activities also don't have it)
                    else -> Surface.ROTATION_90
                }

                if (rotation != value) {
                    value = rotation
                }
            }
        }
    }

    override fun onActive() {
        super.onActive()
        listener.enable()
    }

    override fun onInactive() {
        super.onInactive()
        listener.disable()
    }
}

How can I improve it? Is there any source how is it done in Activity to repeat the same logic?

One of the problem with this custom method that when you hold the phone normally in portrait orientation and put it on the desk (flat positioned) it may return landscape rotation caused by a little angle, when you expect to have it in the same portrait rotation. Sensor Screen Orientation for Activity doesn't have such issues. That's why I'm trying to repeat the same logic and get the same rotation values in such situations.

0

There are 0 answers