Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 243ef567 authored by Nicolò Mazzucato's avatar Nicolò Mazzucato Committed by Android (Google) Code Review
Browse files

Merge "Reuse existing repositories for DisplayStateRepository" into main

parents 38996c14 3eb0a90e
Loading
Loading
Loading
Loading
+14 −80
Original line number Diff line number Diff line
@@ -17,30 +17,20 @@
package com.android.systemui.biometrics.data.repository

import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.hardware.display.DisplayManager
import android.hardware.display.DisplayManager.DisplayListener
import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
import android.os.Handler
import android.util.Size
import android.view.DisplayInfo
import com.android.app.tracing.traceSection
import com.android.internal.util.ArrayUtils
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.toDisplayRotation
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import java.util.concurrent.Executor
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.REAR_DISPLAY
import com.android.systemui.display.data.repository.DisplayRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

@@ -65,52 +55,23 @@ interface DisplayStateRepository {
    val currentDisplaySize: StateFlow<Size>
}

// TODO(b/296211844): This class could directly use DeviceStateRepository and DisplayRepository
// instead.
@SysUISingleton
class DisplayStateRepositoryImpl
@Inject
constructor(
    @Application applicationScope: CoroutineScope,
    @Background backgroundScope: CoroutineScope,
    @Application val context: Context,
    deviceStateManager: DeviceStateManager,
    displayManager: DisplayManager,
    @Background backgroundHandler: Handler,
    @Background backgroundExecutor: Executor,
    @Background backgroundDispatcher: CoroutineDispatcher,
    deviceStateRepository: DeviceStateRepository,
    displayRepository: DisplayRepository,
) : DisplayStateRepository {
    override val isReverseDefaultRotation =
        context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)

    override val isInRearDisplayMode: StateFlow<Boolean> =
        conflatedCallbackFlow {
                val sendRearDisplayStateUpdate = { state: Boolean ->
                    trySendWithFailureLogging(
                        state,
                        TAG,
                        "Error sending rear display state update to $state"
                    )
                }

                val callback =
                    DeviceStateManager.DeviceStateCallback { state ->
                        val isInRearDisplayMode =
                            ArrayUtils.contains(
                                context.resources.getIntArray(
                                    com.android.internal.R.array.config_rearDisplayDeviceStates
                                ),
                                state
                            )
                        sendRearDisplayStateUpdate(isInRearDisplayMode)
                    }

                sendRearDisplayStateUpdate(false)
                deviceStateManager.registerCallback(backgroundExecutor, callback)
                awaitClose { deviceStateManager.unregisterCallback(callback) }
            }
            .flowOn(backgroundDispatcher)
        deviceStateRepository.state
            .map { it == REAR_DISPLAY }
            .stateIn(
                applicationScope,
                backgroundScope,
                started = SharingStarted.Eagerly,
                initialValue = false,
            )
@@ -122,37 +83,10 @@ constructor(
    }

    private val currentDisplayInfo: StateFlow<DisplayInfo> =
        conflatedCallbackFlow {
                val callback =
                    object : DisplayListener {
                        override fun onDisplayRemoved(displayId: Int) {}

                        override fun onDisplayAdded(displayId: Int) {}

                        override fun onDisplayChanged(displayId: Int) {
                            traceSection(
                                "DisplayStateRepository" +
                                    ".currentRotationDisplayListener#onDisplayChanged"
                            ) {
                                val displayInfo = getDisplayInfo()
                                trySendWithFailureLogging(
                                    displayInfo,
                                    TAG,
                                    "Error sending displayInfo to $displayInfo"
                                )
                            }
                        }
                    }
                displayManager.registerDisplayListener(
                    callback,
                    backgroundHandler,
                    EVENT_FLAG_DISPLAY_CHANGED
                )
                awaitClose { displayManager.unregisterDisplayListener(callback) }
            }
            .flowOn(backgroundDispatcher)
        displayRepository.displayChangeEvent
            .map { getDisplayInfo() }
            .stateIn(
                applicationScope,
                backgroundScope,
                started = SharingStarted.Eagerly,
                initialValue = getDisplayInfo(),
            )
@@ -169,7 +103,7 @@ constructor(
        currentDisplayInfo
            .map { rotationToDisplayRotation(it.rotation) }
            .stateIn(
                applicationScope,
                backgroundScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = rotationToDisplayRotation(currentDisplayInfo.value.rotation)
            )
@@ -178,7 +112,7 @@ constructor(
        currentDisplayInfo
            .map { Size(it.naturalWidth, it.naturalHeight) }
            .stateIn(
                applicationScope,
                backgroundScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue =
                    Size(
+19 −67
Original line number Diff line number Diff line
@@ -16,11 +16,9 @@

package com.android.systemui.keyguard.data.repository

import android.hardware.devicestate.DeviceStateManager
import android.hardware.display.DisplayManager
import android.os.Handler
import android.util.Size
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.DisplayInfo
import android.view.Surface
import androidx.test.filters.SmallTest
@@ -29,61 +27,37 @@ import com.android.systemui.biometrics.data.repository.DisplayStateRepository
import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
import com.android.systemui.display.data.repository.FakeDeviceStateRepository
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.same
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule

private const val NORMAL_DISPLAY_MODE_DEVICE_STATE = 2
private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class DisplayStateRepositoryTest : SysuiTestCase() {
    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
    @Mock private lateinit var deviceStateManager: DeviceStateManager
    @Mock private lateinit var displayManager: DisplayManager
    @Mock private lateinit var handler: Handler
    @Mock private lateinit var display: Display
    private lateinit var underTest: DisplayStateRepository

    private val display = mock<Display>()
    private val testScope = TestScope(StandardTestDispatcher())
    private val fakeExecutor = FakeExecutor(FakeSystemClock())
    private val fakeDeviceStateRepository = FakeDeviceStateRepository()
    private val fakeDisplayRepository = FakeDisplayRepository()

    @Captor
    private lateinit var displayListenerCaptor: ArgumentCaptor<DisplayManager.DisplayListener>
    private lateinit var underTest: DisplayStateRepository

    @Before
    fun setUp() {
        val rearDisplayDeviceStates = intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE)
        mContext.orCreateTestableResources.addOverride(
            com.android.internal.R.array.config_rearDisplayDeviceStates,
            rearDisplayDeviceStates
        )

        mContext.orCreateTestableResources.addOverride(
            com.android.internal.R.bool.config_reverseDefaultRotation,
            false
@@ -96,11 +70,8 @@ class DisplayStateRepositoryTest : SysuiTestCase() {
            DisplayStateRepositoryImpl(
                testScope.backgroundScope,
                mContext,
                deviceStateManager,
                displayManager,
                handler,
                fakeExecutor,
                UnconfinedTestDispatcher(),
                fakeDeviceStateRepository,
                fakeDisplayRepository,
            )
    }

@@ -110,12 +81,10 @@ class DisplayStateRepositoryTest : SysuiTestCase() {
            val isInRearDisplayMode by collectLastValue(underTest.isInRearDisplayMode)
            runCurrent()

            val callback = deviceStateManager.captureCallback()

            callback.onStateChanged(NORMAL_DISPLAY_MODE_DEVICE_STATE)
            fakeDeviceStateRepository.emit(DeviceState.FOLDED)
            assertThat(isInRearDisplayMode).isFalse()

            callback.onStateChanged(REAR_DISPLAY_MODE_DEVICE_STATE)
            fakeDeviceStateRepository.emit(DeviceState.REAR_DISPLAY)
            assertThat(isInRearDisplayMode).isTrue()
        }

@@ -125,19 +94,13 @@ class DisplayStateRepositoryTest : SysuiTestCase() {
            val currentRotation by collectLastValue(underTest.currentRotation)
            runCurrent()

            verify(displayManager)
                .registerDisplayListener(
                    displayListenerCaptor.capture(),
                    same(handler),
                    eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
                )

            whenever(display.getDisplayInfo(any())).then {
                val info = it.getArgument<DisplayInfo>(0)
                info.rotation = Surface.ROTATION_90
                return@then true
            }
            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_90)

            fakeDisplayRepository.emitDisplayChangeEvent(DEFAULT_DISPLAY)
            assertThat(currentRotation).isEqualTo(DisplayRotation.ROTATION_90)

            whenever(display.getDisplayInfo(any())).then {
@@ -145,7 +108,8 @@ class DisplayStateRepositoryTest : SysuiTestCase() {
                info.rotation = Surface.ROTATION_180
                return@then true
            }
            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_180)

            fakeDisplayRepository.emitDisplayChangeEvent(DEFAULT_DISPLAY)
            assertThat(currentRotation).isEqualTo(DisplayRotation.ROTATION_180)
        }

@@ -155,13 +119,6 @@ class DisplayStateRepositoryTest : SysuiTestCase() {
            val currentSize by collectLastValue(underTest.currentDisplaySize)
            runCurrent()

            verify(displayManager)
                .registerDisplayListener(
                    displayListenerCaptor.capture(),
                    same(handler),
                    eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
                )

            whenever(display.getDisplayInfo(any())).then {
                val info = it.getArgument<DisplayInfo>(0)
                info.rotation = Surface.ROTATION_0
@@ -169,7 +126,7 @@ class DisplayStateRepositoryTest : SysuiTestCase() {
                info.logicalHeight = 200
                return@then true
            }
            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_0)
            fakeDisplayRepository.emitDisplayChangeEvent(DEFAULT_DISPLAY)
            assertThat(currentSize).isEqualTo(Size(100, 200))

            whenever(display.getDisplayInfo(any())).then {
@@ -179,12 +136,7 @@ class DisplayStateRepositoryTest : SysuiTestCase() {
                info.logicalHeight = 200
                return@then true
            }
            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_180)
            fakeDisplayRepository.emitDisplayChangeEvent(DEFAULT_DISPLAY)
            assertThat(currentSize).isEqualTo(Size(200, 100))
        }
}

private fun DeviceStateManager.captureCallback() =
    withArgCaptor<DeviceStateManager.DeviceStateCallback> {
        verify(this@captureCallback).registerCallback(any(), capture())
    }