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

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

[flexiglass] Don't clear pending keyguard dismiss action when...

...when transitioning from alternate bouncer to primary bouncer.

There are several parts to this work:
1. Eliminate the logic that was clearing the dismiss action based on a
   flow of events
2. Clear the dismiss action explicitly when hiding the alternate bouncer
   from everywhere except when knowingly transfering to the primary
   bouncer
3. Clear the dismiss action explicitly when leaving the primary bouncer

Fix: 379848880
Test: manually verified the verification scenarios in comment#7 of the
attached bug
Test: unit test coverage added
Flag: com.android.systemui.scene_container

Change-Id: Ib3800d5956dd074aaa06e91662f23e81c759036c
parent 07e9d8ae
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
@@ -71,9 +72,7 @@ constructor(
    }

    @Composable
    override fun SceneScope.Content(
        modifier: Modifier,
    ) =
    override fun SceneScope.Content(modifier: Modifier) =
        BouncerScene(
            viewModel = rememberViewModel("BouncerScene") { contentViewModelFactory.create() },
            dialogFactory = dialogFactory,
@@ -89,6 +88,8 @@ private fun SceneScope.BouncerScene(
) {
    val backgroundColor = MaterialTheme.colorScheme.surface

    DisposableEffect(Unit) { onDispose { viewModel.onUiDestroyed() } }

    Box(modifier) {
        Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
            drawRect(color = backgroundColor)
@@ -101,7 +102,7 @@ private fun SceneScope.BouncerScene(
            dialogFactory,
            Modifier.element(Bouncer.Elements.Content)
                .sysuiResTag(Bouncer.TestTags.Root)
                .fillMaxSize()
                .fillMaxSize(),
        )
    }
}
+24 −0
Original line number Diff line number Diff line
@@ -34,6 +34,11 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
@@ -212,6 +217,25 @@ class BouncerSceneContentViewModelTest : SysuiTestCase() {
            assertThat(isFoldSplitRequired).isTrue()
        }

    @Test
    fun onUiDestroyed_clearsPendingDismissAction() =
        kosmos.runTest {
            val dismissAction by collectLastValue(fakeKeyguardRepository.dismissAction)
            fakeKeyguardRepository.setDismissAction(
                DismissAction.RunImmediately(
                    onDismissAction = { KeyguardDone.IMMEDIATE },
                    onCancelAction = {},
                    message = "",
                    willAnimateOnLockscreen = true,
                )
            )
            assertThat(dismissAction).isNotEqualTo(DismissAction.None)

            underTest.onUiDestroyed()

            assertThat(dismissAction).isEqualTo(DismissAction.None)
        }

    private fun authMethodsToTest(): List<AuthenticationMethodModel> {
        return listOf(None, Pin, Password, Pattern, Sim)
    }
+21 −56
Original line number Diff line number Diff line
@@ -35,10 +35,9 @@ 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.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.setSceneTransition
@@ -221,28 +220,6 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
            assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
        }

    @Test
    fun resetDismissAction() =
        testScope.runTest {
            kosmos.setSceneTransition(Idle(Scenes.Bouncer))
            var wasOnCancelInvoked = false
            startInteractor()
            keyguardRepository.setDismissAction(
                DismissAction.RunAfterKeyguardGone(
                    dismissAction = {},
                    onCancelAction = { wasOnCancelInvoked = true },
                    message = "message",
                    willAnimateOnLockscreen = true,
                )
            )
            assertThat(wasOnCancelInvoked).isFalse()
            kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
            runCurrent()

            assertThat(wasOnCancelInvoked).isTrue()
            assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
        }

    @Test
    fun doNotResetDismissActionOnUnlockedShade() =
        testScope.runTest {
@@ -271,37 +248,6 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
            assertThat(keyguardRepository.dismissAction.value).isEqualTo(dismissAction)
        }

    @Test
    fun resetDismissAction_onBouncer_OnAsleep() =
        testScope.runTest {
            kosmos.setSceneTransition(Idle(Scenes.Bouncer))
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.None
            )
            var wasOnCancelInvoked = false
            startInteractor()

            keyguardRepository.setDismissAction(
                DismissAction.RunAfterKeyguardGone(
                    dismissAction = {},
                    onCancelAction = { wasOnCancelInvoked = true },
                    message = "message",
                    willAnimateOnLockscreen = true,
                )
            )
            assertThat(wasOnCancelInvoked).isFalse()
            kosmos.fakePowerRepository.updateWakefulness(
                rawState = WakefulnessState.ASLEEP,
                lastWakeReason = WakeSleepReason.POWER_BUTTON,
                lastSleepReason = WakeSleepReason.TIMEOUT,
                powerButtonLaunchGestureTriggered = false,
            )
            runCurrent()

            assertThat(wasOnCancelInvoked).isTrue()
            assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
        }

    @Test
    fun setDismissAction_callsCancelRunnableOnPreviousDismissAction() =
        testScope.runTest {
@@ -410,6 +356,25 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
            assertThat(wasCancelActionInvoked).isFalse()
        }

    @Test
    fun clearDismissAction() =
        kosmos.runTest {
            val dismissAction by collectLastValue(fakeKeyguardRepository.dismissAction)
            fakeKeyguardRepository.setDismissAction(
                DismissAction.RunImmediately(
                    onDismissAction = { KeyguardDone.IMMEDIATE },
                    onCancelAction = {},
                    message = "",
                    willAnimateOnLockscreen = true,
                )
            )
            assertThat(dismissAction).isNotEqualTo(DismissAction.None)

            underTest.clearDismissAction()

            assertThat(dismissAction).isEqualTo(DismissAction.None)
        }

    private fun TestScope.startInteractor() {
        testScope.backgroundScope.launchTraced(
            "KeyguardDismissActionInteractorTest#startInteractor"
+32 −1
Original line number Diff line number Diff line
@@ -190,6 +190,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
    private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor;
    @Captor
    private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
    @Mock
    private KeyguardDismissActionInteractor mKeyguardDismissActionInteractor;

    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -236,7 +238,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
                        mKeyguardTransitionInteractor,
                        mock(KeyguardDismissTransitionInteractor.class),
                        StandardTestDispatcher(null, null),
                        () -> mock(KeyguardDismissActionInteractor.class),
                        () -> mKeyguardDismissActionInteractor,
                        mSelectedUserInteractor,
                        mock(JavaAdapter.class),
                        () -> mSceneInteractor,
@@ -968,4 +970,33 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
        );
        verify(mAlternateBouncerInteractor).hide();
    }

    @Test
    public void hideAlternateBouncer_clearsDismissActionByDefault() {
        clearInvocations(mKeyguardDismissActionInteractor);

        mStatusBarKeyguardViewManager.hideAlternateBouncer(/* updateScrim= */ true);

        verify(mKeyguardDismissActionInteractor).clearDismissAction();
    }

    @Test
    public void hideAlternateBouncer_clearsDismissActionExplicitly() {
        clearInvocations(mKeyguardDismissActionInteractor);

        mStatusBarKeyguardViewManager.hideAlternateBouncer(
                /* updateScrim= */ true, /* clearDismissAction= */ true);

        verify(mKeyguardDismissActionInteractor).clearDismissAction();
    }

    @Test
    public void hideAlternateBouncer_doNotClearDismissActionExplicitly() {
        clearInvocations(mKeyguardDismissActionInteractor);

        mStatusBarKeyguardViewManager.hideAlternateBouncer(
                /* updateScrim= */ true, /* clearDismissAction= */ false);

        verify(mKeyguardDismissActionInteractor, never()).clearDismissAction();
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -174,9 +174,20 @@ public interface KeyguardViewController {

    /**
     * Stop showing the alternate bouncer, if showing.
     *
     * <p>Should be like calling {@link #hideAlternateBouncer(boolean, boolean)} with a {@code true}
     * {@code clearDismissAction} parameter.
     */
    void hideAlternateBouncer(boolean updateScrim);

    /**
     * Stop showing the alternate bouncer, if showing.
     *
     * @param updateScrim Whether to update the scrim
     * @param clearDismissAction Whether the pending dismiss action should be cleared
     */
    void hideAlternateBouncer(boolean updateScrim, boolean clearDismissAction);

    // TODO: Deprecate registerStatusBar in KeyguardViewController interface. It is currently
    //  only used for testing purposes in StatusBarKeyguardViewManager, and it prevents us from
    //  achieving complete abstraction away from where the Keyguard View is mounted.
Loading