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

Commit d66f0827 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Lockscreen scene returns to portrait mode.

Adds hydration of the 3 minimally required states in
NotificationShadeWindowController in order to get it to calculate the
correct screenOrientation value in adjustScreenOrientation.

This makes the lockscreen scene return to portrait mode, even if system
UI has been put in landscape mode while unlocked.

Not yet ready to treat the bug as fixed because the orientation change
button is showing and it's still possible to change the orientation to
landcape while on the lockscreen scene.

Bug: 335830658
Test: unit tests added
Test: manually verified that hydration still works the same for the
three added states (used printf debugging and compared calls of
setKeyguardShowing, setBouncerShowing, and setKeyguardOccluded with
flexiglass on or off)
Test: manually verified that returning to the lockscreen scene after
setting the device to landscape while unlocked, returns the device to
portrair mode
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT

Change-Id: Iff145fcc8b57b6273a8b2ceb0caa602e93e3e721
parent 991d4df1
Loading
Loading
Loading
Loading
+2 −43
Original line number Diff line number Diff line
@@ -36,53 +36,40 @@ import com.android.systemui.authentication.domain.interactor.authenticationInter
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.classifier.falsingCollector
import com.android.systemui.classifier.falsingManager
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.model.SysUiState
import com.android.systemui.model.sceneContainerPlugin
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.qs.footerActionsController
import com.android.systemui.qs.footerActionsViewModelFactory
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneContainerStartable
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.testKosmos
import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
@@ -155,8 +142,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
            .apply { setTransitionState(transitionState) }
    }

    private val bouncerInteractor by lazy { kosmos.bouncerInteractor }

    private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
    private lateinit var bouncerActionButtonInteractor: BouncerActionButtonInteractor
    private lateinit var bouncerViewModel: BouncerViewModel
@@ -177,7 +162,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {

    private lateinit var shadeSceneViewModel: ShadeSceneViewModel

    private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
    private val powerInteractor by lazy { kosmos.powerInteractor }

    private var bouncerSceneJob: Job? = null
@@ -233,32 +217,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
                unfoldTransitionInteractor = kosmos.unfoldTransitionInteractor,
            )

        val displayTracker = FakeDisplayTracker(context)
        val sysUiState = SysUiState(displayTracker, kosmos.sceneContainerPlugin)
        val startable =
            SceneContainerStartable(
                applicationScope = testScope.backgroundScope,
                sceneInteractor = sceneInteractor,
                deviceEntryInteractor = deviceEntryInteractor,
                deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor,
                bouncerInteractor = bouncerInteractor,
                keyguardInteractor = keyguardInteractor,
                sysUiState = sysUiState,
                displayId = displayTracker.defaultDisplayId,
                sceneLogger = mock(),
                falsingCollector = kosmos.falsingCollector,
                falsingManager = kosmos.falsingManager,
                powerInteractor = powerInteractor,
                simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
                authenticationInteractor = dagger.Lazy { kosmos.authenticationInteractor },
                windowController = mock(),
                deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
                centralSurfaces = mock(),
                headsUpInteractor = kosmos.headsUpNotificationInteractor,
                occlusionInteractor = kosmos.sceneContainerOcclusionInteractor,
                faceUnlockInteractor = kosmos.deviceEntryFaceAuthInteractor,
                shadeInteractor = kosmos.shadeInteractor,
            )
        val startable = kosmos.sceneContainerStartable
        startable.start()

        assertWithMessage("Initial scene key mismatch!")
+95 −48
Original line number Diff line number Diff line
@@ -27,47 +27,40 @@ import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.falsingCollector
import com.android.systemui.classifier.falsingManager
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneContainerStartable
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.notificationShadeWindowController
import com.android.systemui.statusbar.phone.centralSurfaces
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -82,10 +75,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -95,21 +86,17 @@ import org.mockito.MockitoAnnotations
@EnableSceneContainer
class SceneContainerStartableTest : SysuiTestCase() {

    @Mock private lateinit var windowController: NotificationShadeWindowController
    @Mock private lateinit var centralSurfaces: CentralSurfaces

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val sceneInteractor by lazy { kosmos.sceneInteractor }
    private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
    private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
    private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
    private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
    private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
    private val sysUiState = spy(kosmos.sysUiState)
    private val falsingCollector: FalsingCollector = mock()
    private val powerInteractor = PowerInteractorFactory.create().powerInteractor
    private val sysUiState = kosmos.sysUiState
    private val falsingCollector = mock<FalsingCollector>().also { kosmos.falsingCollector = it }
    private val fakeSceneDataSource = kosmos.fakeSceneDataSource
    private val windowController = kosmos.notificationShadeWindowController
    private val centralSurfaces = kosmos.centralSurfaces
    private val powerInteractor = kosmos.powerInteractor

    private lateinit var underTest: SceneContainerStartable

@@ -117,30 +104,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        underTest =
            SceneContainerStartable(
                applicationScope = testScope.backgroundScope,
                sceneInteractor = sceneInteractor,
                deviceEntryInteractor = deviceEntryInteractor,
                deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor,
                bouncerInteractor = bouncerInteractor,
                keyguardInteractor = keyguardInteractor,
                sysUiState = sysUiState,
                displayId = Display.DEFAULT_DISPLAY,
                sceneLogger = mock(),
                falsingCollector = falsingCollector,
                falsingManager = kosmos.falsingManager,
                powerInteractor = powerInteractor,
                simBouncerInteractor = { kosmos.simBouncerInteractor },
                authenticationInteractor = { authenticationInteractor },
                windowController = windowController,
                deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
                centralSurfaces = centralSurfaces,
                headsUpInteractor = kosmos.headsUpNotificationInteractor,
                occlusionInteractor = kosmos.sceneContainerOcclusionInteractor,
                faceUnlockInteractor = kosmos.deviceEntryFaceAuthInteractor,
                shadeInteractor = kosmos.shadeInteractor,
            )
        underTest = kosmos.sceneContainerStartable
    }

    @Test
@@ -879,7 +843,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun hydrateWindowFocus() =
    fun hydrateWindowController_setNotificationShadeFocusable() =
        testScope.runTest {
            val currentDesiredSceneKey by collectLastValue(sceneInteractor.currentScene)
            val transitionStateFlow =
@@ -932,6 +896,89 @@ class SceneContainerStartableTest : SysuiTestCase() {
            verify(windowController, times(2)).setNotificationShadeFocusable(false)
        }

    @Test
    fun hydrateWindowController_setKeyguardShowing() =
        testScope.runTest {
            underTest.start()
            val notificationShadeWindowController = kosmos.notificationShadeWindowController
            val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen)
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            verify(notificationShadeWindowController).setKeyguardShowing(true)

            emulateSceneTransition(transitionStateFlow, Scenes.Bouncer)
            verify(notificationShadeWindowController, times(1)).setKeyguardShowing(true)

            emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
            verify(notificationShadeWindowController, times(1)).setKeyguardShowing(true)

            emulateSceneTransition(transitionStateFlow, Scenes.Shade)
            verify(notificationShadeWindowController, times(1)).setKeyguardShowing(true)

            emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
            verify(notificationShadeWindowController, times(1)).setKeyguardShowing(true)
        }

    @Test
    fun hydrateWindowController_setBouncerShowing() =
        testScope.runTest {
            underTest.start()
            val notificationShadeWindowController = kosmos.notificationShadeWindowController
            val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen)
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            verify(notificationShadeWindowController, never()).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(false)

            emulateSceneTransition(transitionStateFlow, Scenes.Bouncer)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(false)

            emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)

            kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                SuccessFingerprintAuthenticationStatus(0, true)
            )
            assertThat(currentScene).isEqualTo(Scenes.Gone)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)

            emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)

            emulateSceneTransition(transitionStateFlow, Scenes.Bouncer)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
        }

    @Test
    fun hydrateWindowController_setKeyguardOccluded() =
        testScope.runTest {
            underTest.start()
            val notificationShadeWindowController = kosmos.notificationShadeWindowController
            prepareState(initialSceneKey = Scenes.Lockscreen)
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            verify(notificationShadeWindowController, never()).setKeyguardOccluded(true)
            verify(notificationShadeWindowController, times(1)).setKeyguardOccluded(false)

            kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
                true,
                mock()
            )
            runCurrent()
            verify(notificationShadeWindowController, times(1)).setKeyguardOccluded(true)
            verify(notificationShadeWindowController, times(1)).setKeyguardOccluded(false)

            kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(false)
            runCurrent()
            verify(notificationShadeWindowController, times(1)).setKeyguardOccluded(true)
            verify(notificationShadeWindowController, times(2)).setKeyguardOccluded(false)
        }

    @Test
    fun hydrateInteractionState_whileLocked() =
        testScope.runTest {
+35 −15
Original line number Diff line number Diff line
@@ -117,9 +117,9 @@ constructor(
            hydrateSystemUiState()
            collectFalsingSignals()
            respondToFalsingDetections()
            hydrateWindowFocus()
            hydrateInteractionState()
            handleBouncerOverscroll()
            hydrateWindowController()
        } else {
            sceneLogger.logFrameworkEnabled(
                isEnabled = false,
@@ -403,6 +403,40 @@ constructor(
        }
    }

    private fun hydrateWindowController() {
        applicationScope.launch {
            sceneInteractor.transitionState
                .mapNotNull { transitionState ->
                    (transitionState as? ObservableTransitionState.Idle)?.scene
                }
                .distinctUntilChanged()
                .collect { sceneKey ->
                    windowController.setNotificationShadeFocusable(sceneKey != Scenes.Gone)
                }
        }

        applicationScope.launch {
            deviceEntryInteractor.isDeviceEntered.collect { isDeviceEntered ->
                windowController.setKeyguardShowing(!isDeviceEntered)
            }
        }

        applicationScope.launch {
            sceneInteractor.currentScene
                .map { it == Scenes.Bouncer }
                .distinctUntilChanged()
                .collect { isBouncerShowing ->
                    windowController.setBouncerShowing(isBouncerShowing)
                }
        }

        applicationScope.launch {
            occlusionInteractor.invisibleDueToOcclusion.collect { invisibleDueToOcclusion ->
                windowController.setKeyguardOccluded(invisibleDueToOcclusion)
            }
        }
    }

    /** Collects and reports signals into the falsing system. */
    private fun collectFalsingSignals() {
        applicationScope.launch {
@@ -464,20 +498,6 @@ constructor(
        }
    }

    /** Keeps the focus state of the window view up-to-date. */
    private fun hydrateWindowFocus() {
        applicationScope.launch {
            sceneInteractor.transitionState
                .mapNotNull { transitionState ->
                    (transitionState as? ObservableTransitionState.Idle)?.scene
                }
                .distinctUntilChanged()
                .collect { sceneKey ->
                    windowController.setNotificationShadeFocusable(sceneKey != Scenes.Gone)
                }
        }
    }

    /** Keeps the interaction state of [CentralSurfaces] up-to-date. */
    private fun hydrateInteractionState() {
        applicationScope.launch {
+1 −1
Original line number Diff line number Diff line
@@ -18,4 +18,4 @@ package com.android.systemui.classifier

import com.android.systemui.kosmos.Kosmos

val Kosmos.falsingCollector by Kosmos.Fixture { FalsingCollectorFake() }
var Kosmos.falsingCollector by Kosmos.Fixture<FalsingCollector> { FalsingCollectorFake() }
+6 −3
Original line number Diff line number Diff line
@@ -19,10 +19,13 @@ package com.android.systemui.model
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.settings.displayTracker
import org.mockito.Mockito.spy

val Kosmos.sysUiState by Fixture {
    spy(
        SysUiState(
            displayTracker,
            sceneContainerPlugin,
        )
    )
}
Loading