diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 28cc69758308f..87f3164b33d21 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -59,16 +59,23 @@ constructor( } private fun listenForLockscreenToDreaming() { + val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING) scope.launch { keyguardInteractor.isAbleToDream - .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) - .collect { pair -> - val (isAbleToDream, lastStartedTransition) = pair - if ( - isAbleToDream && - lastStartedTransition.to == KeyguardState.LOCKSCREEN && - lastStartedTransition.from != KeyguardState.AOD - ) { + .sample( + combine( + keyguardTransitionInteractor.startedKeyguardTransitionStep, + keyguardTransitionInteractor.finishedKeyguardState, + ::Pair + ), + ::toTriple + ) + .collect { (isAbleToDream, lastStartedTransition, finishedKeyguardState) -> + val isOnLockscreen = finishedKeyguardState == KeyguardState.LOCKSCREEN + val isTransitionInterruptible = + lastStartedTransition.to == KeyguardState.LOCKSCREEN && + !invalidFromStates.contains(lastStartedTransition.from) + if (isAbleToDream && (isOnLockscreen || isTransitionInterruptible)) { keyguardTransitionRepository.startTransition( TransitionInfo( name, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 092fdca6fd417..e2d0ec3ae01b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -16,32 +16,31 @@ package com.android.systemui.keyguard.domain.interactor -import android.animation.ValueAnimator -import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN import com.android.systemui.SysuiTestCase -import com.android.systemui.animation.Interpolators import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository -import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.DozeTransitionModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionInfo +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.shared.model.WakeSleepReason import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessState -import com.android.systemui.keyguard.util.KeyguardTransitionRunner import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat @@ -57,6 +56,7 @@ import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock +import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -67,17 +67,13 @@ import org.mockito.MockitoAnnotations */ @SmallTest @RunWith(JUnit4::class) -@FlakyTest(bugId = 265303901) class KeyguardTransitionScenariosTest : SysuiTestCase() { private lateinit var testScope: TestScope private lateinit var keyguardRepository: FakeKeyguardRepository private lateinit var bouncerRepository: FakeKeyguardBouncerRepository private lateinit var shadeRepository: ShadeRepository - - // Used to issue real transition steps for test input - private lateinit var runner: KeyguardTransitionRunner - private lateinit var transitionRepository: KeyguardTransitionRepository + private lateinit var transitionRepository: FakeKeyguardTransitionRepository // Used to verify transition requests for test output @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository @@ -103,10 +99,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { keyguardRepository = FakeKeyguardRepository() bouncerRepository = FakeKeyguardBouncerRepository() shadeRepository = FakeShadeRepository() - - /* Used to issue full transition steps, to better simulate a real device */ - transitionRepository = KeyguardTransitionRepositoryImpl() - runner = KeyguardTransitionRunner(transitionRepository) + transitionRepository = FakeKeyguardTransitionRepository() whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN) @@ -195,21 +188,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to DREAMING - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DREAMING, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING) // WHEN doze is complete keyguardRepository.setDozeTransitionModel( @@ -243,20 +222,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to LOCKSCREEN - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.OFF, - to = KeyguardState.LOCKSCREEN, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() + runTransition(KeyguardState.OFF, KeyguardState.LOCKSCREEN) // WHEN the primary bouncer is set to show bouncerRepository.setPrimaryShow(true) @@ -283,21 +249,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to OCCLUDED - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED) // WHEN the device begins to sleep keyguardRepository.setWakefulnessModel(startingToSleep()) @@ -324,21 +276,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to OCCLUDED - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED) // WHEN the device begins to sleep keyguardRepository.setWakefulnessModel(startingToSleep()) @@ -369,20 +307,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to LOCKSCREEN - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.GONE, - to = KeyguardState.LOCKSCREEN, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - reset(mockTransitionRepository) + runTransition(KeyguardState.GONE, KeyguardState.LOCKSCREEN) // WHEN the device begins to dream keyguardRepository.setDreamingWithOverlay(true) @@ -409,21 +334,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to LOCKSCREEN - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.GONE, - to = KeyguardState.LOCKSCREEN, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.GONE, KeyguardState.LOCKSCREEN) // WHEN the device begins to sleep keyguardRepository.setWakefulnessModel(startingToSleep()) @@ -450,21 +361,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to LOCKSCREEN - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.GONE, - to = KeyguardState.LOCKSCREEN, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.GONE, KeyguardState.LOCKSCREEN) // WHEN the device begins to sleep keyguardRepository.setWakefulnessModel(startingToSleep()) @@ -487,21 +384,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fun `DOZING to LOCKSCREEN`() = testScope.runTest { // GIVEN a prior transition has run to DOZING - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DOZING, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DOZING) // WHEN the device begins to wake keyguardRepository.setWakefulnessModel(startingToWake()) @@ -521,25 +404,37 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test - fun `DOZING to GONE`() = + fun `DOZING to LOCKSCREEN cannot be interruped by DREAMING`() = testScope.runTest { - // GIVEN a prior transition has run to DOZING - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DOZING, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, + // GIVEN a prior transition has started to LOCKSCREEN + transitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, + value = 0.5f, + transitionState = TransitionState.RUNNING, + ownerName = "KeyguardTransitionScenariosTest", ) ) runCurrent() reset(mockTransitionRepository) + // WHEN a signal comes that dreaming is enabled + keyguardRepository.setDreamingWithOverlay(true) + advanceUntilIdle() + + // THEN the transition is ignored + verify(mockTransitionRepository, never()).startTransition(any(), anyBoolean()) + + coroutineContext.cancelChildren() + } + + @Test + fun `DOZING to GONE`() = + testScope.runTest { + // GIVEN a prior transition has run to DOZING + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DOZING) + // WHEN biometrics succeeds with wake and unlock mode keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK) runCurrent() @@ -565,21 +460,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to GONE - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE) // WHEN the device begins to sleep keyguardRepository.setWakefulnessModel(startingToSleep()) @@ -606,21 +487,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to GONE - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE) // WHEN the device begins to sleep keyguardRepository.setWakefulnessModel(startingToSleep()) @@ -643,21 +510,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fun `GONE to LOCKSREEN`() = testScope.runTest { // GIVEN a prior transition has run to GONE - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE) // WHEN the keyguard starts to show keyguardRepository.setKeyguardShowing(true) @@ -688,20 +541,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to GONE - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE) // WHEN the device begins to dream keyguardRepository.setDreamingWithOverlay(true) @@ -724,21 +564,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fun `ALTERNATE_BOUNCER to PRIMARY_BOUNCER`() = testScope.runTest { // GIVEN a prior transition has run to ALTERNATE_BOUNCER - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.ALTERNATE_BOUNCER, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER) // WHEN the alternateBouncer stops showing and then the primary bouncer shows bouncerRepository.setPrimaryShow(true) @@ -762,21 +588,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { testScope.runTest { // GIVEN a prior transition has run to ALTERNATE_BOUNCER bouncerRepository.setAlternateVisible(true) - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.ALTERNATE_BOUNCER, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER) // GIVEN the primary bouncer isn't showing, aod available and starting to sleep bouncerRepository.setPrimaryShow(false) @@ -805,21 +617,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { testScope.runTest { // GIVEN a prior transition has run to ALTERNATE_BOUNCER bouncerRepository.setAlternateVisible(true) - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.ALTERNATE_BOUNCER, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER) // GIVEN the primary bouncer isn't showing, aod not available and starting to sleep // to sleep @@ -849,21 +647,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { testScope.runTest { // GIVEN a prior transition has run to ALTERNATE_BOUNCER bouncerRepository.setAlternateVisible(true) - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.ALTERNATE_BOUNCER, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER) // GIVEN the primary bouncer isn't showing and device not sleeping bouncerRepository.setPrimaryShow(false) @@ -891,21 +675,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { testScope.runTest { // GIVEN a prior transition has run to PRIMARY_BOUNCER bouncerRepository.setPrimaryShow(true) - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.PRIMARY_BOUNCER, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER) // GIVEN aod available and starting to sleep keyguardRepository.setAodAvailable(true) @@ -933,21 +703,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { testScope.runTest { // GIVEN a prior transition has run to PRIMARY_BOUNCER bouncerRepository.setPrimaryShow(true) - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.PRIMARY_BOUNCER, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER) // GIVEN aod not available and starting to sleep to sleep keyguardRepository.setAodAvailable(false) @@ -975,21 +731,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { testScope.runTest { // GIVEN a prior transition has run to PRIMARY_BOUNCER bouncerRepository.setPrimaryShow(true) - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.PRIMARY_BOUNCER, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) - runCurrent() - reset(mockTransitionRepository) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER) // GIVEN device not sleeping keyguardRepository.setWakefulnessModel(startingToWake()) @@ -1019,22 +761,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to OCCLUDED - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED) keyguardRepository.setKeyguardOccluded(true) runCurrent() - reset(mockTransitionRepository) // WHEN keyguard goes away keyguardRepository.setKeyguardShowing(false) @@ -1063,22 +792,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // GIVEN a prior transition has run to OCCLUDED - runner.startTransition( - testScope, - TransitionInfo( - ownerName = "", - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - animator = - ValueAnimator().apply { - duration = 10 - interpolator = Interpolators.LINEAR - }, - ) - ) + runTransition(KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED) keyguardRepository.setKeyguardOccluded(true) runCurrent() - reset(mockTransitionRepository) // WHEN occlusion ends keyguardRepository.setKeyguardOccluded(false) @@ -1121,4 +837,35 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { bouncerRepository, ) } + + private suspend fun TestScope.runTransition(from: KeyguardState, to: KeyguardState) { + transitionRepository.sendTransitionStep( + TransitionStep( + from = from, + to = to, + value = 0f, + transitionState = TransitionState.STARTED, + ) + ) + runCurrent() + transitionRepository.sendTransitionStep( + TransitionStep( + from = from, + to = to, + value = 0.5f, + transitionState = TransitionState.RUNNING, + ) + ) + runCurrent() + transitionRepository.sendTransitionStep( + TransitionStep( + from = from, + to = to, + value = 1f, + transitionState = TransitionState.FINISHED, + ) + ) + runCurrent() + reset(mockTransitionRepository) + } }