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

Commit 765f6a74 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Fixes alternate bouncer unlock.

In Flexiglass, SceneContainerStartable takes care of hiding the
alternate bouncer when it's visible when the device becomes unlocked or
when the device goes to sleep.

This CL makes two changes:
1. Disables a bit of logic in legacy code that was hiding the alternate bouncer too early after
a successful unlock causing the logic that depended on the alternate
bouncer being visible to not trigger, leaving the user on top of the
Lockscreen scene after a successful alternate bouncer auth from the
shade (see attached bug).

2. Updates the scene backstack to make sure that there's no Lockscreen
   scene on it when the device is unlocked via alternate bouncer.

Fix: 375191368
Test: manually verified. Created notifications with inline reply. Locked
the device. Clicked on "Reply" on a notification. Authenticated via
alternate bouncer / fingerprint (used SFPS). Collapsed the shade.
Verified that I'm seeing the Gone scene and not the dismissible
Lockscreen scene.
Flag: com.android.systemui.scene_container

Change-Id: Ifc5d565d0f9c27254f6e04cc723a28e2235e3ba5
parent 5508c034
Loading
Loading
Loading
Loading
+55 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import com.android.systemui.biometrics.data.repository.fingerprintPropertyReposi
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
import com.android.systemui.classifier.FalsingCollector
@@ -54,6 +55,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.flags.EnableSceneContainer
@@ -114,6 +116,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -2356,6 +2359,58 @@ class SceneContainerStartableTest : SysuiTestCase() {
            assertThat(isLockscreenEnabled).isTrue()
        }

    @Test
    fun replacesLockscreenSceneOnBackStack_whenUnlockdViaAlternateBouncer_fromShade() =
        testScope.runTest {
            val transitionState =
                prepareState(
                    isDeviceUnlocked = false,
                    initialSceneKey = Scenes.Lockscreen,
                    authenticationMethod = AuthenticationMethodModel.Pin,
                )
            underTest.start()

            val isUnlocked by
                collectLastValue(
                    kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }
                )
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            val backStack by collectLastValue(sceneBackInteractor.backStack)
            val isAlternateBouncerVisible by
                collectLastValue(kosmos.alternateBouncerInteractor.isVisible)
            assertThat(isUnlocked).isFalse()
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            assertThat(isAlternateBouncerVisible).isFalse()

            // Change to shade.
            sceneInteractor.changeScene(Scenes.Shade, "")
            transitionState.value = ObservableTransitionState.Idle(Scenes.Shade)
            runCurrent()
            assertThat(isUnlocked).isFalse()
            assertThat(currentScene).isEqualTo(Scenes.Shade)
            assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
            assertThat(isAlternateBouncerVisible).isFalse()

            // Show the alternate bouncer.
            kosmos.alternateBouncerInteractor.forceShow()
            kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open
            runCurrent()
            assertThat(isUnlocked).isFalse()
            assertThat(currentScene).isEqualTo(Scenes.Shade)
            assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
            assertThat(isAlternateBouncerVisible).isTrue()

            // Trigger a fingerprint unlock.
            kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                SuccessFingerprintAuthenticationStatus(0, true)
            )
            runCurrent()
            assertThat(isUnlocked).isTrue()
            assertThat(currentScene).isEqualTo(Scenes.Shade)
            assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Gone)
            assertThat(isAlternateBouncerVisible).isFalse()
        }

    private fun TestScope.emulateSceneTransition(
        transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
        toScene: SceneKey,
+16 −10
Original line number Diff line number Diff line
@@ -373,6 +373,7 @@ constructor(
                                    "device was unlocked with alternate bouncer showing" +
                                        " and shade didn't need to be left open"
                            } else {
                                replaceLockscreenSceneOnBackStack()
                                null
                            }
                        }
@@ -391,16 +392,7 @@ constructor(
                                val prevScene = previousScene.value
                                val targetScene = prevScene ?: Scenes.Gone
                                if (targetScene != Scenes.Gone) {
                                    sceneBackInteractor.updateBackStack { stack ->
                                        val list = stack.asIterable().toMutableList()
                                        check(list.last() == Scenes.Lockscreen) {
                                            "The bottommost/last SceneKey of the back stack isn't" +
                                                " the Lockscreen scene like expected. The back" +
                                                " stack is $stack."
                                        }
                                        list[list.size - 1] = Scenes.Gone
                                        sceneStackOf(*list.toTypedArray())
                                    }
                                    replaceLockscreenSceneOnBackStack()
                                }
                                targetScene to
                                    "device was unlocked with primary bouncer showing," +
@@ -435,6 +427,20 @@ constructor(
        }
    }

    /** If the [Scenes.Lockscreen] is on the backstack, replaces it with [Scenes.Gone]. */
    private fun replaceLockscreenSceneOnBackStack() {
        sceneBackInteractor.updateBackStack { stack ->
            val list = stack.asIterable().toMutableList()
            check(list.last() == Scenes.Lockscreen) {
                "The bottommost/last SceneKey of the back stack isn't" +
                    " the Lockscreen scene like expected. The back" +
                    " stack is $stack."
            }
            list[list.size - 1] = Scenes.Gone
            sceneStackOf(*list.toTypedArray())
        }
    }

    private fun handlePowerState() {
        applicationScope.launch {
            powerInteractor.detailedWakefulness.collect { wakefulness ->
+7 −0
Original line number Diff line number Diff line
@@ -520,6 +520,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
            mListenForCanShowAlternateBouncer.cancel(null);
        }
        mListenForCanShowAlternateBouncer = null;

        // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot.
        mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
                mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
@@ -568,6 +569,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
    }

    private void consumeCanShowAlternateBouncer(boolean canShow) {
        if (SceneContainerFlag.isEnabled()) {
            // When the scene framework is enabled, the alternative bouncer is hidden from the scene
            // framework logic so there's no need for this logic here.
            return;
        }

        // Hack: this is required to fix issues where
        // KeyguardBouncerRepository#alternateBouncerVisible state is incorrectly set and then never
        // reset. This is caused by usages of show()/forceShow() that only read this flow to set the