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

Commit 6f28e08a authored by Luca Zuccarini's avatar Luca Zuccarini
Browse files

Ensure that device entry does not always dismiss the current scene.

If a back stack exists, device entry might mean "remove the
lockscreen but keep the shade open". By updating the back stack
instead of simply moving to `Scenes.Gone`, we enable this behavior.
Any `OnDismissAction` triggered after device entry can still decide
whether the shade should be dismissed or not in the specific
instance.

Note that this does not perfectly match the pre-scene container
behavior yet, but that's because of a different issue with the scene
background. What's happening underneath is correct.

Fix: 436602773
Flag: com.android.systemui.scene_container
Test: atest DeviceEntryInteractorTest SceneBackInteractorTest SceneContainerStartableTest + manual (see video in the bug)
Change-Id: I27f0baa80ec434ff040cd64099c38792c48a6e38
parent 9a368d9b
Loading
Loading
Loading
Loading
+63 −0
Original line number Diff line number Diff line
@@ -51,6 +51,8 @@ import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useStandardTestDispatcher
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.scene.data.model.asIterable
import com.android.systemui.scene.domain.interactor.sceneBackInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Overlays
@@ -291,6 +293,28 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
            assertThat(currentScene).isEqualTo(Scenes.Gone)
        }

    @Test
    fun showOrUnlockDevice_notLocked_replacesLockscreenWithGoneInTheBackStack() =
        kosmos.runTest {
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            val backStack by collectLastValue(sceneBackInteractor.backStack)
            switchToScene(Scenes.Lockscreen)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            switchToScene(Scenes.QuickSettings)
            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
            assertThat(backStack!!.asIterable().toList()).isEqualTo(listOf(Scenes.Lockscreen))

            fakeAuthenticationRepository.setAuthenticationMethod(Pin)
            fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                SuccessFingerprintAuthenticationStatus(0, true)
            )

            underTest.attemptDeviceEntry("test")

            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
            assertThat(backStack!!.asIterable().toList()).isEqualTo(listOf(Scenes.Gone))
        }

    @Test
    fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() =
        kosmos.runTest {
@@ -305,6 +329,25 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
            assertThat(currentScene).isEqualTo(Scenes.Gone)
        }

    @Test
    fun showOrUnlockDevice_authMethodNotSecure_replacesLockscreenWithGoneInTheBackStack() =
        kosmos.runTest {
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            val backStack by collectLastValue(sceneBackInteractor.backStack)
            switchToScene(Scenes.Lockscreen)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            switchToScene(Scenes.QuickSettings)
            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
            assertThat(backStack!!.asIterable().toList()).isEqualTo(listOf(Scenes.Lockscreen))

            fakeAuthenticationRepository.setAuthenticationMethod(None)

            underTest.attemptDeviceEntry("test")

            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
            assertThat(backStack!!.asIterable().toList()).isEqualTo(listOf(Scenes.Gone))
        }

    @Test
    fun showOrUnlockDevice_authMethodSwipe_switchesToGoneScene() =
        kosmos.runTest {
@@ -320,6 +363,26 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
            assertThat(currentScene).isEqualTo(Scenes.Gone)
        }

    @Test
    fun showOrUnlockDevice_authMethodSwipe_replacesLockscreenWithGoneInTheBackStack() =
        kosmos.runTest {
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            val backStack by collectLastValue(sceneBackInteractor.backStack)
            switchToScene(Scenes.Lockscreen)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            switchToScene(Scenes.QuickSettings)
            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
            assertThat(backStack!!.asIterable().toList()).isEqualTo(listOf(Scenes.Lockscreen))

            fakeDeviceEntryRepository.setLockscreenEnabled(true)
            fakeAuthenticationRepository.setAuthenticationMethod(None)

            underTest.attemptDeviceEntry("test")

            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
            assertThat(backStack!!.asIterable().toList()).isEqualTo(listOf(Scenes.Gone))
        }

    @Test
    fun showOrUnlockDevice_noAlternateBouncer_switchesToBouncerScene() =
        kosmos.runTest {
+31 −8
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableSceneContainer
class SceneBackInteractorTest : SysuiTestCase() {

    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
@@ -48,7 +49,6 @@ class SceneBackInteractorTest : SysuiTestCase() {
    private val underTest by lazy { kosmos.sceneBackInteractor }

    @Test
    @EnableSceneContainer
    fun navigateToQs_thenBack_whileLocked() =
        kosmos.runTest {
            enableSingleShade()
@@ -64,7 +64,6 @@ class SceneBackInteractorTest : SysuiTestCase() {
        }

    @Test
    @EnableSceneContainer
    fun navigateToQs_thenUnlock() =
        kosmos.runTest {
            enableSingleShade()
@@ -79,7 +78,6 @@ class SceneBackInteractorTest : SysuiTestCase() {
        }

    @Test
    @EnableSceneContainer
    fun navigateToQs_skippingShade_thenBack_whileLocked() =
        kosmos.runTest {
            enableSingleShade()
@@ -93,7 +91,6 @@ class SceneBackInteractorTest : SysuiTestCase() {
        }

    @Test
    @EnableSceneContainer
    fun navigateToQs_skippingShade_thenBack_thenShade_whileLocked() =
        kosmos.runTest {
            enableSingleShade()
@@ -108,7 +105,6 @@ class SceneBackInteractorTest : SysuiTestCase() {
        }

    @Test
    @EnableSceneContainer
    fun navigateToQs_thenBack_whileUnlocked() =
        kosmos.runTest {
            enableSingleShade()
@@ -125,7 +121,6 @@ class SceneBackInteractorTest : SysuiTestCase() {
        }

    @Test
    @EnableSceneContainer
    fun navigateToQs_skippingShade_thenBack_whileUnlocked() =
        kosmos.runTest {
            enableSingleShade()
@@ -140,7 +135,6 @@ class SceneBackInteractorTest : SysuiTestCase() {
        }

    @Test
    @EnableSceneContainer
    fun navigateToQs_skippingShade_thenBack_thenShade_whileUnlocked() =
        kosmos.runTest {
            enableSingleShade()
@@ -156,7 +150,6 @@ class SceneBackInteractorTest : SysuiTestCase() {
        }

    @Test
    @EnableSceneContainer
    fun updateBackStack() =
        kosmos.runTest {
            enableSingleShade()
@@ -174,6 +167,36 @@ class SceneBackInteractorTest : SysuiTestCase() {
                .isEqualTo(listOf(Scenes.Lockscreen, Scenes.Shade))
        }

    @Test
    fun replaceLockscreenSceneOnBackStack_replacesLockscreenWithGone() =
        kosmos.runTest {
            enableSingleShade()
            underTest.onSceneChange(from = Scenes.Lockscreen, to = Scenes.Shade)
            underTest.onSceneChange(from = Scenes.Shade, to = Scenes.QuickSettings)
            assertThat(underTest.backStack.value.asIterable().toList())
                .isEqualTo(listOf(Scenes.Shade, Scenes.Lockscreen))

            underTest.replaceLockscreenSceneOnBackStack()

            assertThat(underTest.backStack.value.asIterable().toList())
                .isEqualTo(listOf(Scenes.Shade, Scenes.Gone))
        }

    @Test
    fun replaceLockscreenSceneOnBackStack_nothingToDo() =
        kosmos.runTest {
            enableSingleShade()
            underTest.onSceneChange(from = Scenes.Gone, to = Scenes.Shade)
            underTest.onSceneChange(from = Scenes.Shade, to = Scenes.QuickSettings)
            assertThat(underTest.backStack.value.asIterable().toList())
                .isEqualTo(listOf(Scenes.Shade, Scenes.Gone))

            underTest.replaceLockscreenSceneOnBackStack()

            assertThat(underTest.backStack.value.asIterable().toList())
                .isEqualTo(listOf(Scenes.Shade, Scenes.Gone))
        }

    private suspend fun Kosmos.assertRoute(vararg route: RouteNode) {
        val currentScene by collectLastValue(sceneInteractor.currentScene)
        val backScene by collectLastValue(underTest.backScene)
+16 −9
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.scene.data.model.asIterable
import com.android.systemui.scene.data.model.peek
import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -66,7 +67,7 @@ constructor(
    private val deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>,
    private val alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>,
    private val dismissCallbackRegistry: Lazy<DismissCallbackRegistry>,
    sceneBackInteractor: Lazy<SceneBackInteractor>,
    private val sceneBackInteractor: Lazy<SceneBackInteractor>,
    @SceneFrameworkTableLog private val tableLogBuffer: Lazy<TableLogBuffer>,
) {
    /**
@@ -250,18 +251,24 @@ constructor(
                                    " required, original reason for request: $loggingReason",
                        )
                }
            } else {
                if (sceneBackInteractor.get().backStack.value.peek() != null) {
                    // If there is a back stack, replacing the Lockscreen scene at the bottom of the
                    // stack triggers device entry without necessarily dismissing the current scene.
                    sceneBackInteractor.get().replaceLockscreenSceneOnBackStack()
                } else {
                    sceneInteractor
                        .get()
                        .changeScene(
                            toScene = Scenes.Gone,
                            loggingReason =
                            "request to unlock device while authentication isn't required," +
                                " original reason for request: $loggingReason",
                                "request to unlock device while authentication isn't " +
                                    "required, original reason for request: $loggingReason",
                        )
                }
            }
        }
    }

    /**
     * Returns `true` if the device currently requires authentication before entry is granted;
+1 −5
Original line number Diff line number Diff line
@@ -182,11 +182,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
            if (!mKeyguard.isShowing()) {
                showDialog(expandable);
            } else {
                mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
                    // Dismissing the keyguard will collapse the shade, so we don't animate from the
                    // view here as it would not look good.
                    showDialog(null /* view */);
                });
                mActivityStarter.postQSRunnableDismissingKeyguard(() -> showDialog(expandable));
            }
        });
    }
+17 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import com.android.systemui.scene.data.model.sceneStackOf
import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -92,6 +93,22 @@ constructor(
        )
    }

    /**
     * If the [Scenes.Lockscreen] is on the bottom of the navigation backstack, replaces it with
     * [Scenes.Gone].
     */
    fun replaceLockscreenSceneOnBackStack() {
        updateBackStack { stack ->
            val list = stack.asIterable().toMutableList()
            if (list.lastOrNull() == Scenes.Lockscreen) {
                list[list.size - 1] = Scenes.Gone
                sceneStackOf(*list.toTypedArray())
            } else {
                stack
            }
        }
    }

    private fun stackOperation(from: SceneKey, to: SceneKey, stack: SceneStack): StackOperation? {
        val fromDistance =
            checkNotNull(sceneContainerConfig.navigationDistances[from]) {
Loading