Loading packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +14 −13 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor; import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate; import com.android.systemui.biometrics.SideFpsController; import com.android.systemui.biometrics.SideFpsUiRequestSource; Loading @@ -80,8 +81,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteracto import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.model.SceneKey; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; Loading Loading @@ -394,7 +393,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } }; private final UserInteractor mUserInteractor; private final Provider<SceneInteractor> mSceneInteractor; private final Provider<AuthenticationInteractor> mAuthenticationInteractor; private final Provider<JavaAdapter> mJavaAdapter; @Nullable private Job mSceneTransitionCollectionJob; Loading Loading @@ -425,7 +424,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard Provider<JavaAdapter> javaAdapter, UserInteractor userInteractor, FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate, Provider<SceneInteractor> sceneInteractor Provider<AuthenticationInteractor> authenticationInteractor ) { super(view); view.setAccessibilityDelegate(faceAuthAccessibilityDelegate); Loading Loading @@ -454,7 +453,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; mBouncerMessageInteractor = bouncerMessageInteractor; mUserInteractor = userInteractor; mSceneInteractor = sceneInteractor; mAuthenticationInteractor = authenticationInteractor; mJavaAdapter = javaAdapter; } Loading @@ -480,19 +479,21 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard showPrimarySecurityScreen(false); if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) { // When the scene framework transitions from bouncer to gone, we dismiss the keyguard. // When the scene framework says that the lockscreen has been dismissed, dismiss the // keyguard here, revealing the underlying app or launcher: mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow( mSceneInteractor.get().finishedSceneTransitions( /* from= */ SceneKey.Bouncer.INSTANCE, /* to= */ SceneKey.Gone.INSTANCE), unused -> { mAuthenticationInteractor.get().isLockscreenDismissed(), isLockscreenDismissed -> { if (isLockscreenDismissed) { final int selectedUserId = mUserInteractor.getSelectedUserId(); showNextSecurityScreenOrFinish( /* authenticated= */ true, selectedUserId, /* bypassSecondaryLockScreen= */ true, mSecurityModel.getSecurityMode(selectedUserId)); }); } } ); } } Loading packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +6 −2 Original line number Diff line number Diff line Loading @@ -114,14 +114,18 @@ constructor( * - `true` doesn't mean the lockscreen is invisible (since this state changes before the * transition occurs). */ private val isLockscreenDismissed = val isLockscreenDismissed: StateFlow<Boolean> = sceneInteractor.desiredScene .map { it.key } .filter { currentScene -> currentScene == SceneKey.Gone || currentScene == SceneKey.Lockscreen } .map { it == SceneKey.Gone } .distinctUntilChanged() .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = false, ) /** * Whether it's currently possible to swipe up to dismiss the lockscreen without requiring Loading packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +21 −18 Original line number Diff line number Diff line Loading @@ -17,21 +17,22 @@ package com.android.systemui.scene.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.shared.logger.SceneLogger import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.RemoteUserInput import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn /** * Generic business logic and app state accessors for the scene framework. Loading @@ -44,6 +45,7 @@ import kotlinx.coroutines.flow.mapNotNull class SceneInteractor @Inject constructor( @Application applicationScope: CoroutineScope, private val repository: SceneContainerRepository, private val logger: SceneLogger, ) { Loading Loading @@ -88,6 +90,22 @@ constructor( */ val transitionState: StateFlow<ObservableTransitionState> = repository.transitionState /** * The key of the scene that the UI is currently transitioning to or `null` if there is no * active transition at the moment. * * This is a convenience wrapper around [transitionState], meant for flow-challenged consumers * like Java code. */ val transitioningTo: StateFlow<SceneKey?> = transitionState .map { state -> (state as? ObservableTransitionState.Transition)?.toScene } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = null, ) /** Whether the scene container is visible. */ val isVisible: StateFlow<Boolean> = repository.isVisible Loading Loading @@ -142,21 +160,6 @@ constructor( repository.setTransitionState(transitionState) } /** * Returns a stream of events that emits one [Unit] every time the framework transitions from * [from] to [to]. */ fun finishedSceneTransitions(from: SceneKey, to: SceneKey): Flow<Unit> { return transitionState .mapNotNull { it as? ObservableTransitionState.Idle } .map { idleState -> idleState.scene } .distinctUntilChanged() .pairwise() .mapNotNull { (previousSceneKey, currentSceneKey) -> Unit.takeIf { previousSceneKey == from && currentSceneKey == to } } } /** Handles a remote user input. */ fun onRemoteUserInput(input: RemoteUserInput) { _remoteUserInput.value = input Loading packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +51 −7 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserS import com.android.keyguard.KeyguardSecurityModel.SecurityMode import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate import com.android.systemui.biometrics.SideFpsController import com.android.systemui.biometrics.SideFpsUiRequestSource Loading Loading @@ -144,6 +145,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { private lateinit var testableResources: TestableResources private lateinit var sceneTestUtils: SceneTestUtils private lateinit var sceneInteractor: SceneInteractor private lateinit var authenticationInteractor: AuthenticationInteractor private lateinit var sceneTransitionStateFlow: MutableStateFlow<ObservableTransitionState> private lateinit var underTest: KeyguardSecurityContainerController Loading Loading @@ -207,6 +209,11 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { sceneTransitionStateFlow = MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen)) sceneInteractor.setTransitionState(sceneTransitionStateFlow) authenticationInteractor = sceneTestUtils.authenticationInteractor( repository = sceneTestUtils.authenticationRepository(), sceneInteractor = sceneInteractor ) underTest = KeyguardSecurityContainerController( Loading Loading @@ -237,7 +244,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { userInteractor, faceAuthAccessibilityDelegate, ) { sceneInteractor authenticationInteractor } } Loading Loading @@ -753,7 +760,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { } @Test fun dismissesKeyguard_whenSceneChangesFromBouncerToGone() = fun dismissesKeyguard_whenSceneChangesToGone() = sceneTestUtils.testScope.runTest { featureFlags.set(Flags.SCENE_CONTAINER, true) Loading Loading @@ -790,12 +797,32 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { runCurrent() verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt()) // While listening, moving back to the lockscreen scene does not dismiss the keyguard // again. clearInvocations(viewMediatorCallback) sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( SceneKey.Gone, SceneKey.Lockscreen, flowOf(.5f) ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Lockscreen) runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) // While listening, moving back to the bouncer scene does not dismiss the keyguard // again. clearInvocations(viewMediatorCallback) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition(SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f)) ObservableTransitionState.Transition( SceneKey.Lockscreen, SceneKey.Bouncer, flowOf(.5f) ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer) Loading @@ -815,7 +842,21 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) // While not listening, moving back to the bouncer does not dismiss the keyguard. // While not listening, moving to the lockscreen does not dismiss the keyguard. sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( SceneKey.Gone, SceneKey.Lockscreen, flowOf(.5f) ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Lockscreen) runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) // While not listening, moving to the bouncer does not dismiss the keyguard. sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition(SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f)) Loading @@ -826,12 +867,15 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) // Reattaching the view starts listening again so moving from the bouncer scene to the // gone // scene now does dismiss the keyguard again. // gone scene now does dismiss the keyguard again, this time from lockscreen. underTest.onViewAttached() sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f)) ObservableTransitionState.Transition( SceneKey.Lockscreen, SceneKey.Gone, flowOf(.5f) ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone) Loading packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +64 −2 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest Loading Loading @@ -605,7 +606,68 @@ class AuthenticationInteractorTest : SysuiTestCase() { assertThat(hintedPinLength).isNull() } private fun switchToScene(sceneKey: SceneKey) { sceneInteractor.changeScene(SceneModel(sceneKey), "reason") @Test fun isLockscreenDismissed() = testScope.runTest { val isLockscreenDismissed by collectLastValue(underTest.isLockscreenDismissed) // Start on lockscreen. switchToScene(SceneKey.Lockscreen) assertThat(isLockscreenDismissed).isFalse() // The user swipes down to reveal shade. switchToScene(SceneKey.Shade) assertThat(isLockscreenDismissed).isFalse() // The user swipes down to reveal quick settings. switchToScene(SceneKey.QuickSettings) assertThat(isLockscreenDismissed).isFalse() // The user swipes up to go back to shade. switchToScene(SceneKey.Shade) assertThat(isLockscreenDismissed).isFalse() // The user swipes up to reveal bouncer. switchToScene(SceneKey.Bouncer) assertThat(isLockscreenDismissed).isFalse() // The user hits back to return to lockscreen. switchToScene(SceneKey.Lockscreen) assertThat(isLockscreenDismissed).isFalse() // The user swipes up to reveal bouncer. switchToScene(SceneKey.Bouncer) assertThat(isLockscreenDismissed).isFalse() // The user enters correct credentials and goes to gone. switchToScene(SceneKey.Gone) assertThat(isLockscreenDismissed).isTrue() // The user swipes down to reveal shade. switchToScene(SceneKey.Shade) assertThat(isLockscreenDismissed).isTrue() // The user swipes down to reveal quick settings. switchToScene(SceneKey.QuickSettings) assertThat(isLockscreenDismissed).isTrue() // The user swipes up to go back to shade. switchToScene(SceneKey.Shade) assertThat(isLockscreenDismissed).isTrue() // The user swipes up to go back to gone. switchToScene(SceneKey.Gone) assertThat(isLockscreenDismissed).isTrue() // The device goes to sleep, returning to the lockscreen. switchToScene(SceneKey.Lockscreen) assertThat(isLockscreenDismissed).isFalse() } private fun TestScope.switchToScene(sceneKey: SceneKey) { val model = SceneModel(sceneKey) val loggingReason = "reason" sceneInteractor.changeScene(model, loggingReason) sceneInteractor.onSceneChanged(model, loggingReason) runCurrent() } } Loading
packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +14 −13 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor; import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate; import com.android.systemui.biometrics.SideFpsController; import com.android.systemui.biometrics.SideFpsUiRequestSource; Loading @@ -80,8 +81,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteracto import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.model.SceneKey; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; Loading Loading @@ -394,7 +393,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } }; private final UserInteractor mUserInteractor; private final Provider<SceneInteractor> mSceneInteractor; private final Provider<AuthenticationInteractor> mAuthenticationInteractor; private final Provider<JavaAdapter> mJavaAdapter; @Nullable private Job mSceneTransitionCollectionJob; Loading Loading @@ -425,7 +424,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard Provider<JavaAdapter> javaAdapter, UserInteractor userInteractor, FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate, Provider<SceneInteractor> sceneInteractor Provider<AuthenticationInteractor> authenticationInteractor ) { super(view); view.setAccessibilityDelegate(faceAuthAccessibilityDelegate); Loading Loading @@ -454,7 +453,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; mBouncerMessageInteractor = bouncerMessageInteractor; mUserInteractor = userInteractor; mSceneInteractor = sceneInteractor; mAuthenticationInteractor = authenticationInteractor; mJavaAdapter = javaAdapter; } Loading @@ -480,19 +479,21 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard showPrimarySecurityScreen(false); if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) { // When the scene framework transitions from bouncer to gone, we dismiss the keyguard. // When the scene framework says that the lockscreen has been dismissed, dismiss the // keyguard here, revealing the underlying app or launcher: mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow( mSceneInteractor.get().finishedSceneTransitions( /* from= */ SceneKey.Bouncer.INSTANCE, /* to= */ SceneKey.Gone.INSTANCE), unused -> { mAuthenticationInteractor.get().isLockscreenDismissed(), isLockscreenDismissed -> { if (isLockscreenDismissed) { final int selectedUserId = mUserInteractor.getSelectedUserId(); showNextSecurityScreenOrFinish( /* authenticated= */ true, selectedUserId, /* bypassSecondaryLockScreen= */ true, mSecurityModel.getSecurityMode(selectedUserId)); }); } } ); } } Loading
packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +6 −2 Original line number Diff line number Diff line Loading @@ -114,14 +114,18 @@ constructor( * - `true` doesn't mean the lockscreen is invisible (since this state changes before the * transition occurs). */ private val isLockscreenDismissed = val isLockscreenDismissed: StateFlow<Boolean> = sceneInteractor.desiredScene .map { it.key } .filter { currentScene -> currentScene == SceneKey.Gone || currentScene == SceneKey.Lockscreen } .map { it == SceneKey.Gone } .distinctUntilChanged() .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = false, ) /** * Whether it's currently possible to swipe up to dismiss the lockscreen without requiring Loading
packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +21 −18 Original line number Diff line number Diff line Loading @@ -17,21 +17,22 @@ package com.android.systemui.scene.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.shared.logger.SceneLogger import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.RemoteUserInput import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn /** * Generic business logic and app state accessors for the scene framework. Loading @@ -44,6 +45,7 @@ import kotlinx.coroutines.flow.mapNotNull class SceneInteractor @Inject constructor( @Application applicationScope: CoroutineScope, private val repository: SceneContainerRepository, private val logger: SceneLogger, ) { Loading Loading @@ -88,6 +90,22 @@ constructor( */ val transitionState: StateFlow<ObservableTransitionState> = repository.transitionState /** * The key of the scene that the UI is currently transitioning to or `null` if there is no * active transition at the moment. * * This is a convenience wrapper around [transitionState], meant for flow-challenged consumers * like Java code. */ val transitioningTo: StateFlow<SceneKey?> = transitionState .map { state -> (state as? ObservableTransitionState.Transition)?.toScene } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = null, ) /** Whether the scene container is visible. */ val isVisible: StateFlow<Boolean> = repository.isVisible Loading Loading @@ -142,21 +160,6 @@ constructor( repository.setTransitionState(transitionState) } /** * Returns a stream of events that emits one [Unit] every time the framework transitions from * [from] to [to]. */ fun finishedSceneTransitions(from: SceneKey, to: SceneKey): Flow<Unit> { return transitionState .mapNotNull { it as? ObservableTransitionState.Idle } .map { idleState -> idleState.scene } .distinctUntilChanged() .pairwise() .mapNotNull { (previousSceneKey, currentSceneKey) -> Unit.takeIf { previousSceneKey == from && currentSceneKey == to } } } /** Handles a remote user input. */ fun onRemoteUserInput(input: RemoteUserInput) { _remoteUserInput.value = input Loading
packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +51 −7 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserS import com.android.keyguard.KeyguardSecurityModel.SecurityMode import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate import com.android.systemui.biometrics.SideFpsController import com.android.systemui.biometrics.SideFpsUiRequestSource Loading Loading @@ -144,6 +145,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { private lateinit var testableResources: TestableResources private lateinit var sceneTestUtils: SceneTestUtils private lateinit var sceneInteractor: SceneInteractor private lateinit var authenticationInteractor: AuthenticationInteractor private lateinit var sceneTransitionStateFlow: MutableStateFlow<ObservableTransitionState> private lateinit var underTest: KeyguardSecurityContainerController Loading Loading @@ -207,6 +209,11 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { sceneTransitionStateFlow = MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen)) sceneInteractor.setTransitionState(sceneTransitionStateFlow) authenticationInteractor = sceneTestUtils.authenticationInteractor( repository = sceneTestUtils.authenticationRepository(), sceneInteractor = sceneInteractor ) underTest = KeyguardSecurityContainerController( Loading Loading @@ -237,7 +244,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { userInteractor, faceAuthAccessibilityDelegate, ) { sceneInteractor authenticationInteractor } } Loading Loading @@ -753,7 +760,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { } @Test fun dismissesKeyguard_whenSceneChangesFromBouncerToGone() = fun dismissesKeyguard_whenSceneChangesToGone() = sceneTestUtils.testScope.runTest { featureFlags.set(Flags.SCENE_CONTAINER, true) Loading Loading @@ -790,12 +797,32 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { runCurrent() verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt()) // While listening, moving back to the lockscreen scene does not dismiss the keyguard // again. clearInvocations(viewMediatorCallback) sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( SceneKey.Gone, SceneKey.Lockscreen, flowOf(.5f) ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Lockscreen) runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) // While listening, moving back to the bouncer scene does not dismiss the keyguard // again. clearInvocations(viewMediatorCallback) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition(SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f)) ObservableTransitionState.Transition( SceneKey.Lockscreen, SceneKey.Bouncer, flowOf(.5f) ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer) Loading @@ -815,7 +842,21 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) // While not listening, moving back to the bouncer does not dismiss the keyguard. // While not listening, moving to the lockscreen does not dismiss the keyguard. sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition( SceneKey.Gone, SceneKey.Lockscreen, flowOf(.5f) ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Lockscreen) runCurrent() verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) // While not listening, moving to the bouncer does not dismiss the keyguard. sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition(SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f)) Loading @@ -826,12 +867,15 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) // Reattaching the view starts listening again so moving from the bouncer scene to the // gone // scene now does dismiss the keyguard again. // gone scene now does dismiss the keyguard again, this time from lockscreen. underTest.onViewAttached() sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f)) ObservableTransitionState.Transition( SceneKey.Lockscreen, SceneKey.Gone, flowOf(.5f) ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone) Loading
packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +64 −2 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest Loading Loading @@ -605,7 +606,68 @@ class AuthenticationInteractorTest : SysuiTestCase() { assertThat(hintedPinLength).isNull() } private fun switchToScene(sceneKey: SceneKey) { sceneInteractor.changeScene(SceneModel(sceneKey), "reason") @Test fun isLockscreenDismissed() = testScope.runTest { val isLockscreenDismissed by collectLastValue(underTest.isLockscreenDismissed) // Start on lockscreen. switchToScene(SceneKey.Lockscreen) assertThat(isLockscreenDismissed).isFalse() // The user swipes down to reveal shade. switchToScene(SceneKey.Shade) assertThat(isLockscreenDismissed).isFalse() // The user swipes down to reveal quick settings. switchToScene(SceneKey.QuickSettings) assertThat(isLockscreenDismissed).isFalse() // The user swipes up to go back to shade. switchToScene(SceneKey.Shade) assertThat(isLockscreenDismissed).isFalse() // The user swipes up to reveal bouncer. switchToScene(SceneKey.Bouncer) assertThat(isLockscreenDismissed).isFalse() // The user hits back to return to lockscreen. switchToScene(SceneKey.Lockscreen) assertThat(isLockscreenDismissed).isFalse() // The user swipes up to reveal bouncer. switchToScene(SceneKey.Bouncer) assertThat(isLockscreenDismissed).isFalse() // The user enters correct credentials and goes to gone. switchToScene(SceneKey.Gone) assertThat(isLockscreenDismissed).isTrue() // The user swipes down to reveal shade. switchToScene(SceneKey.Shade) assertThat(isLockscreenDismissed).isTrue() // The user swipes down to reveal quick settings. switchToScene(SceneKey.QuickSettings) assertThat(isLockscreenDismissed).isTrue() // The user swipes up to go back to shade. switchToScene(SceneKey.Shade) assertThat(isLockscreenDismissed).isTrue() // The user swipes up to go back to gone. switchToScene(SceneKey.Gone) assertThat(isLockscreenDismissed).isTrue() // The device goes to sleep, returning to the lockscreen. switchToScene(SceneKey.Lockscreen) assertThat(isLockscreenDismissed).isFalse() } private fun TestScope.switchToScene(sceneKey: SceneKey) { val model = SceneModel(sceneKey) val loggingReason = "reason" sceneInteractor.changeScene(model, loggingReason) sceneInteractor.onSceneChanged(model, loggingReason) runCurrent() } }