Loading packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt +86 −0 Original line number Diff line number Diff line Loading @@ -19,17 +19,22 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState 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.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository 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.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.domain.interactor.powerInteractor Loading @@ -38,11 +43,13 @@ 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 import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest Loading Loading @@ -80,6 +87,7 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { alternateBouncerInteractor = kosmos.alternateBouncerInteractor, shadeInteractor = { kosmos.shadeInteractor }, keyguardInteractor = { kosmos.keyguardInteractor }, sceneInteractor = { kosmos.sceneInteractor }, ) } Loading Loading @@ -178,7 +186,11 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { ) assertThat(executeDismissAction).isNull() kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) kosmos.setSceneTransition(Idle(Scenes.Gone)) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(executeDismissAction).isNotNull() } Loading Loading @@ -301,4 +313,78 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { underTest.setKeyguardDone(KeyguardDone.IMMEDIATE) assertThat(keyguardDoneTiming).isEqualTo(KeyguardDone.IMMEDIATE) } @Test @EnableSceneContainer fun dismissAction_executesBeforeItsReset_sceneContainerOn_swipeAuth_fromQsScene() = testScope.runTest { val canSwipeToEnter by collectLastValue(kosmos.deviceEntryInteractor.canSwipeToEnter) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(currentScene!!) ) kosmos.sceneInteractor.setTransitionState(transitionState) val executeDismissAction by collectLastValue(underTest.executeDismissAction) val resetDismissAction by collectLastValue(underTest.resetDismissAction) assertThat(executeDismissAction).isNull() assertThat(resetDismissAction).isNull() kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None ) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) assertThat(canSwipeToEnter).isTrue() kosmos.sceneInteractor.changeScene(Scenes.QuickSettings, "") transitionState.value = ObservableTransitionState.Idle(Scenes.QuickSettings) assertThat(currentScene).isEqualTo(Scenes.QuickSettings) assertThat(executeDismissAction).isNull() assertThat(resetDismissAction).isNull() val dismissAction = DismissAction.RunImmediately( onDismissAction = { KeyguardDone.LATER }, onCancelAction = {}, message = "message", willAnimateOnLockscreen = true, ) underTest.setDismissAction(dismissAction) // Should still be null because the transition to Gone has not yet happened. assertThat(executeDismissAction).isNull() assertThat(resetDismissAction).isNull() transitionState.value = ObservableTransitionState.Transition.ChangeScene( fromScene = Scenes.QuickSettings, toScene = Scenes.Gone, currentScene = flowOf(Scenes.QuickSettings), currentOverlays = emptySet(), progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) runCurrent() assertThat(executeDismissAction).isNull() assertThat(resetDismissAction).isNull() transitionState.value = ObservableTransitionState.Transition.ChangeScene( fromScene = Scenes.QuickSettings, toScene = Scenes.Gone, currentScene = flowOf(Scenes.Gone), currentOverlays = emptySet(), progress = flowOf(1f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(currentScene).isEqualTo(Scenes.Gone) runCurrent() assertThat(executeDismissAction).isNotNull() assertThat(resetDismissAction).isNull() } } packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt +34 −5 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardDone import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.power.domain.interactor.PowerInteractor 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.shade.domain.interactor.ShadeInteractor Loading Loading @@ -64,6 +65,7 @@ constructor( alternateBouncerInteractor: AlternateBouncerInteractor, shadeInteractor: Lazy<ShadeInteractor>, keyguardInteractor: Lazy<KeyguardInteractor>, sceneInteractor: Lazy<SceneInteractor>, ) { val dismissAction: Flow<DismissAction> = repository.dismissAction Loading Loading @@ -125,7 +127,20 @@ constructor( val executeDismissAction: Flow<() -> KeyguardDone> = merge( finishedTransitionToGone, if (SceneContainerFlag.isEnabled) { // Using currentScene instead of finishedTransitionToGone because of a race // condition that forms between finishedTransitionToGone and // isOnShadeWhileUnlocked where the latter emits false before the former emits // true, causing the merge to not emit until it's too late. sceneInteractor .get() .currentScene .map { it == Scenes.Gone } .distinctUntilChanged() .filter { it } } else { finishedTransitionToGone }, isOnShadeWhileUnlocked.filter { it }.map {}, dismissInteractor.dismissKeyguardRequestWithImmediateDismissAction, ) Loading @@ -135,10 +150,24 @@ constructor( val resetDismissAction: Flow<Unit> = combine( if (SceneContainerFlag.isEnabled) { // Using currentScene instead of isFinishedIn because of a race condition that // forms between isFinishedIn(Gone) and isOnShadeWhileUnlocked where the latter // emits false before the former emits true, causing the evaluation of the // combine to come up with true, temporarily, before settling on false, which is // a valid final state. That causes an incorrect reset of the dismiss action to // occur before it gets executed. sceneInteractor .get() .currentScene .map { it == Scenes.Gone } .distinctUntilChanged() } else { transitionInteractor.isFinishedIn( scene = Scenes.Gone, stateWithoutSceneContainer = GONE, ), ) }, transitionInteractor.isFinishedIn( scene = Scenes.Bouncer, stateWithoutSceneContainer = PRIMARY_BOUNCER, Loading packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import kotlinx.coroutines.ExperimentalCoroutinesApi Loading @@ -38,5 +39,6 @@ val Kosmos.keyguardDismissActionInteractor by alternateBouncerInteractor = alternateBouncerInteractor, shadeInteractor = { shadeInteractor }, keyguardInteractor = { keyguardInteractor }, sceneInteractor = { sceneInteractor }, ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt +86 −0 Original line number Diff line number Diff line Loading @@ -19,17 +19,22 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState 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.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository 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.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.domain.interactor.powerInteractor Loading @@ -38,11 +43,13 @@ 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 import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest Loading Loading @@ -80,6 +87,7 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { alternateBouncerInteractor = kosmos.alternateBouncerInteractor, shadeInteractor = { kosmos.shadeInteractor }, keyguardInteractor = { kosmos.keyguardInteractor }, sceneInteractor = { kosmos.sceneInteractor }, ) } Loading Loading @@ -178,7 +186,11 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { ) assertThat(executeDismissAction).isNull() kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) kosmos.setSceneTransition(Idle(Scenes.Gone)) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(executeDismissAction).isNotNull() } Loading Loading @@ -301,4 +313,78 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { underTest.setKeyguardDone(KeyguardDone.IMMEDIATE) assertThat(keyguardDoneTiming).isEqualTo(KeyguardDone.IMMEDIATE) } @Test @EnableSceneContainer fun dismissAction_executesBeforeItsReset_sceneContainerOn_swipeAuth_fromQsScene() = testScope.runTest { val canSwipeToEnter by collectLastValue(kosmos.deviceEntryInteractor.canSwipeToEnter) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(currentScene!!) ) kosmos.sceneInteractor.setTransitionState(transitionState) val executeDismissAction by collectLastValue(underTest.executeDismissAction) val resetDismissAction by collectLastValue(underTest.resetDismissAction) assertThat(executeDismissAction).isNull() assertThat(resetDismissAction).isNull() kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None ) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) assertThat(canSwipeToEnter).isTrue() kosmos.sceneInteractor.changeScene(Scenes.QuickSettings, "") transitionState.value = ObservableTransitionState.Idle(Scenes.QuickSettings) assertThat(currentScene).isEqualTo(Scenes.QuickSettings) assertThat(executeDismissAction).isNull() assertThat(resetDismissAction).isNull() val dismissAction = DismissAction.RunImmediately( onDismissAction = { KeyguardDone.LATER }, onCancelAction = {}, message = "message", willAnimateOnLockscreen = true, ) underTest.setDismissAction(dismissAction) // Should still be null because the transition to Gone has not yet happened. assertThat(executeDismissAction).isNull() assertThat(resetDismissAction).isNull() transitionState.value = ObservableTransitionState.Transition.ChangeScene( fromScene = Scenes.QuickSettings, toScene = Scenes.Gone, currentScene = flowOf(Scenes.QuickSettings), currentOverlays = emptySet(), progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) runCurrent() assertThat(executeDismissAction).isNull() assertThat(resetDismissAction).isNull() transitionState.value = ObservableTransitionState.Transition.ChangeScene( fromScene = Scenes.QuickSettings, toScene = Scenes.Gone, currentScene = flowOf(Scenes.Gone), currentOverlays = emptySet(), progress = flowOf(1f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(false), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) kosmos.sceneInteractor.changeScene(Scenes.Gone, "") assertThat(currentScene).isEqualTo(Scenes.Gone) runCurrent() assertThat(executeDismissAction).isNotNull() assertThat(resetDismissAction).isNull() } }
packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt +34 −5 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardDone import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.power.domain.interactor.PowerInteractor 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.shade.domain.interactor.ShadeInteractor Loading Loading @@ -64,6 +65,7 @@ constructor( alternateBouncerInteractor: AlternateBouncerInteractor, shadeInteractor: Lazy<ShadeInteractor>, keyguardInteractor: Lazy<KeyguardInteractor>, sceneInteractor: Lazy<SceneInteractor>, ) { val dismissAction: Flow<DismissAction> = repository.dismissAction Loading Loading @@ -125,7 +127,20 @@ constructor( val executeDismissAction: Flow<() -> KeyguardDone> = merge( finishedTransitionToGone, if (SceneContainerFlag.isEnabled) { // Using currentScene instead of finishedTransitionToGone because of a race // condition that forms between finishedTransitionToGone and // isOnShadeWhileUnlocked where the latter emits false before the former emits // true, causing the merge to not emit until it's too late. sceneInteractor .get() .currentScene .map { it == Scenes.Gone } .distinctUntilChanged() .filter { it } } else { finishedTransitionToGone }, isOnShadeWhileUnlocked.filter { it }.map {}, dismissInteractor.dismissKeyguardRequestWithImmediateDismissAction, ) Loading @@ -135,10 +150,24 @@ constructor( val resetDismissAction: Flow<Unit> = combine( if (SceneContainerFlag.isEnabled) { // Using currentScene instead of isFinishedIn because of a race condition that // forms between isFinishedIn(Gone) and isOnShadeWhileUnlocked where the latter // emits false before the former emits true, causing the evaluation of the // combine to come up with true, temporarily, before settling on false, which is // a valid final state. That causes an incorrect reset of the dismiss action to // occur before it gets executed. sceneInteractor .get() .currentScene .map { it == Scenes.Gone } .distinctUntilChanged() } else { transitionInteractor.isFinishedIn( scene = Scenes.Gone, stateWithoutSceneContainer = GONE, ), ) }, transitionInteractor.isFinishedIn( scene = Scenes.Bouncer, stateWithoutSceneContainer = PRIMARY_BOUNCER, Loading
packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import kotlinx.coroutines.ExperimentalCoroutinesApi Loading @@ -38,5 +39,6 @@ val Kosmos.keyguardDismissActionInteractor by alternateBouncerInteractor = alternateBouncerInteractor, shadeInteractor = { shadeInteractor }, keyguardInteractor = { keyguardInteractor }, sceneInteractor = { sceneInteractor }, ) }