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

Commit cab6bef4 authored by Beverly's avatar Beverly
Browse files

[flexiglass] Handle dismiss actions from shade => primary bouncer => gone/shade

SceneContainerStartable:
Check whether leaveOpenOnDismissKeyguard is true to determine
whether to go to the GONE or SHADE scene. "Leave open" is referring
to leaving the shade open.

KeyguardDismissActionInteractor:
When the device becomes unlocked on the SHADE, run any pending
dismiss actions.

Test: setup pin/pattern/password; pull down the shade on the
lockscreen; tap on a notification; successfully authenticate;
observe device transitions to the notification intent
Test: setup pin/pattern/password; pull down the shade on the
lockscreen; tap on the "edit qs" affordance; successfully authenticate;
observe user is back on QS with the "edit QS" UI
Test: setup pin/pattern/password; longpress on a QS tile;
successfully authenticate; observe longpress action occurs
Test: atest SceneContainerStartableTest KeyguardDismissActionInteractorTest
Bug: 308819693
Flag: com.android.systemui.scene_container

Change-Id: Iefe6fae0167407013f406093dd773b1039a5f691
parent 0d4eb64a
Loading
Loading
Loading
Loading
+36 −1
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ 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.sysuiStatusBarStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -274,9 +275,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
        }

    @Test
    fun switchFromBouncerToQuickSettingsWhenDeviceUnlocked() =
    fun switchFromBouncerToQuickSettingsWhenDeviceUnlocked_whenLeaveOpenShade() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
            kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open

            val transitionState =
                prepareState(
@@ -305,6 +307,39 @@ class SceneContainerStartableTest : SysuiTestCase() {
            assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
        }

    @Test
    fun switchFromBouncerToGoneWhenDeviceUnlocked_whenDoNotLeaveOpenShade() =
        testScope.runTest {
            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
            kosmos.sysuiStatusBarStateController.leaveOpen = false // don't leave shade open

            val transitionState =
                prepareState(
                    authenticationMethod = AuthenticationMethodModel.Pin,
                    isDeviceUnlocked = false,
                    initialSceneKey = Scenes.Lockscreen,
                )
            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
            underTest.start()
            runCurrent()

            sceneInteractor.changeScene(Scenes.QuickSettings, "switching to qs for test")
            transitionState.value = ObservableTransitionState.Idle(Scenes.QuickSettings)
            runCurrent()
            assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)

            sceneInteractor.changeScene(Scenes.Bouncer, "switching to bouncer for test")
            transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer)
            runCurrent()
            assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)

            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                SuccessFingerprintAuthenticationStatus(0, true)
            )

            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
        }

    @Test
    fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn() =
        testScope.runTest {
+31 −3
Original line number Diff line number Diff line
@@ -19,12 +19,15 @@ package com.android.systemui.keyguard.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
import com.android.systemui.util.kotlin.sample
@@ -35,6 +38,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.map
@@ -51,6 +55,8 @@ constructor(
    transitionInteractor: KeyguardTransitionInteractor,
    val dismissInteractor: KeyguardDismissInteractor,
    @Application private val applicationScope: CoroutineScope,
    sceneInteractor: SceneInteractor,
    deviceEntryInteractor: DeviceEntryInteractor,
) {
    val dismissAction: Flow<DismissAction> = repository.dismissAction

@@ -80,9 +86,26 @@ constructor(
            .filter { it }
            .map {}

    /**
     * True if the any variation of the notification shade or quick settings is showing AND the
     * device is unlocked. Else, false.
     */
    private val isOnShadeWhileUnlocked: Flow<Boolean> =
        combine(
                sceneInteractor.currentScene,
                deviceEntryInteractor.isUnlocked,
            ) { scene, isUnlocked ->
                isUnlocked &&
                    (scene == Scenes.Shade ||
                        scene == Scenes.NotificationsShade ||
                        scene == Scenes.QuickSettings ||
                        scene == Scenes.QuickSettingsShade)
            }
            .distinctUntilChanged()
    val executeDismissAction: Flow<() -> KeyguardDone> =
        merge(
                finishedTransitionToGone,
                isOnShadeWhileUnlocked.filter { it }.map {},
                dismissInteractor.dismissKeyguardRequestWithImmediateDismissAction
            )
            .sample(dismissAction)
@@ -99,9 +122,10 @@ constructor(
                    scene = Scenes.Bouncer,
                    stateWithoutSceneContainer = PRIMARY_BOUNCER
                ),
                transitionInteractor.isFinishedIn(state = ALTERNATE_BOUNCER)
            ) { isOnGone, isOnBouncer, isOnAltBouncer ->
                !isOnGone && !isOnBouncer && !isOnAltBouncer
                transitionInteractor.isFinishedIn(state = ALTERNATE_BOUNCER),
                isOnShadeWhileUnlocked,
            ) { isOnGone, isOnBouncer, isOnAltBouncer, isOnShadeWhileUnlocked ->
                !isOnGone && !isOnBouncer && !isOnAltBouncer && !isOnShadeWhileUnlocked
            }
            .filter { it }
            .sampleFilter(dismissAction) { it !is DismissAction.None }
@@ -112,6 +136,7 @@ constructor(
    }

    fun runAfterKeyguardGone(runnable: Runnable) {
        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
        setDismissAction(
            DismissAction.RunAfterKeyguardGone(
                dismissAction = { runnable.run() },
@@ -123,15 +148,18 @@ constructor(
    }

    fun setDismissAction(dismissAction: DismissAction) {
        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
        repository.dismissAction.value.onCancelAction.run()
        repository.setDismissAction(dismissAction)
    }

    fun handleDismissAction() {
        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
        repository.setDismissAction(DismissAction.None)
    }

    suspend fun setKeyguardDone(keyguardDoneTiming: KeyguardDone) {
        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
        dismissInteractor.setKeyguardDone(keyguardDoneTiming)
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInte
import com.android.systemui.log.core.LogLevel
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.sample
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,7 +35,7 @@ import kotlinx.coroutines.launch
class KeyguardDismissActionBinder
@Inject
constructor(
    private val interactor: KeyguardDismissActionInteractor,
    private val interactorLazy: Lazy<KeyguardDismissActionInteractor>,
    @Application private val scope: CoroutineScope,
    private val keyguardLogger: KeyguardLogger,
) : CoreStartable {
@@ -44,6 +45,7 @@ constructor(
            return
        }

        val interactor = interactorLazy.get()
        scope.launch {
            interactor.executeDismissAction.collect {
                log("executeDismissAction")
+9 −2
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
@@ -130,6 +131,7 @@ constructor(
    private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor,
    private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
    private val dismissCallbackRegistry: DismissCallbackRegistry,
    private val statusBarStateController: SysuiStatusBarStateController,
) : CoreStartable {
    private val centralSurfaces: CentralSurfaces?
        get() = centralSurfacesOptLazy.get().getOrNull()
@@ -356,8 +358,13 @@ constructor(
                        isOnBouncer ->
                            // When the device becomes unlocked in Bouncer, go to previous scene,
                            // or Gone.
                            if (previousScene.value == Scenes.Lockscreen) {
                                Scenes.Gone to "device was unlocked in Bouncer scene"
                            if (
                                previousScene.value == Scenes.Lockscreen ||
                                    !statusBarStateController.leaveOpenOnKeyguardHide()
                            ) {
                                Scenes.Gone to
                                    "device was unlocked in Bouncer scene and shade" +
                                        " didn't need to be left open"
                            } else {
                                val prevScene = previousScene.value
                                (prevScene ?: Scenes.Gone) to
+32 −31
Original line number Diff line number Diff line
@@ -20,21 +20,24 @@ package com.android.systemui.keyguard.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.setSceneTransition
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -44,13 +47,12 @@ import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@EnableSceneContainer
@RunWith(AndroidJUnit4::class)
class KeyguardDismissActionInteractorTest : SysuiTestCase() {
    val kosmos = testKosmos()

    private val keyguardRepository = kosmos.fakeKeyguardRepository
    private val transitionRepository = kosmos.fakeKeyguardTransitionRepository

    private val testScope = kosmos.testScope

    private lateinit var dismissInteractorWithDependencies:
@@ -74,6 +76,8 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
                transitionInteractor = kosmos.keyguardTransitionInteractor,
                dismissInteractor = dismissInteractorWithDependencies.interactor,
                applicationScope = testScope.backgroundScope,
                sceneInteractor = kosmos.sceneInteractor,
                deviceEntryInteractor = kosmos.deviceEntryInteractor,
            )
    }

@@ -158,7 +162,6 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
        }

    @Test
    @DisableSceneContainer
    fun executeDismissAction_dismissKeyguardRequestWithoutImmediateDismissAction() =
        testScope.runTest {
            val executeDismissAction by collectLastValue(underTest.executeDismissAction)
@@ -175,43 +178,37 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
            )
            assertThat(executeDismissAction).isNull()

            // WHEN the keyguard is GONE
            transitionRepository.sendTransitionSteps(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.GONE,
                testScope,
            )
            kosmos.setSceneTransition(Idle(Scenes.Gone))

            assertThat(executeDismissAction).isNotNull()
        }

    @Test
    @EnableSceneContainer
    fun executeDismissAction_dismissKeyguardRequestWithoutImmediateDismissAction_scene_container() =
    fun resetDismissAction() =
        testScope.runTest {
            val executeDismissAction by collectLastValue(underTest.executeDismissAction)

            // WHEN a keyguard action will run after the keyguard is gone
            val onDismissAction = {}
            kosmos.setSceneTransition(Idle(Scenes.Bouncer))
            val resetDismissAction by collectLastValue(underTest.resetDismissAction)
            keyguardRepository.setDismissAction(
                DismissAction.RunAfterKeyguardGone(
                    dismissAction = onDismissAction,
                    dismissAction = {},
                    onCancelAction = {},
                    message = "message",
                    willAnimateOnLockscreen = true,
                )
            )
            assertThat(executeDismissAction).isNull()

            kosmos.setSceneTransition(Idle(Scenes.Gone))

            assertThat(executeDismissAction).isNotNull()
            assertThat(resetDismissAction).isNull()
            kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
            assertThat(resetDismissAction).isEqualTo(Unit)
        }

    @Test
    fun resetDismissAction() =
    fun doNotResetDismissActionOnUnlockedShade() =
        testScope.runTest {
            kosmos.setSceneTransition(Idle(Scenes.Bouncer))
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.None
            )
            val resetDismissAction by collectLastValue(underTest.resetDismissAction)

            keyguardRepository.setDismissAction(
                DismissAction.RunAfterKeyguardGone(
                    dismissAction = {},
@@ -220,12 +217,16 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
                    willAnimateOnLockscreen = true,
                )
            )
            transitionRepository.sendTransitionSteps(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.AOD,
                testScope
            assertThat(resetDismissAction).isNull()

            kosmos.setSceneTransition(
                Transition(
                    from = Scenes.Bouncer,
                    to = Scenes.NotificationsShade,
                    progress = flowOf(1f),
                )
            assertThat(resetDismissAction).isEqualTo(Unit)
            )
            assertThat(resetDismissAction).isNull()
        }

    @Test
Loading