Loading packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +350 −8 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.internal.logging.uiEventLoggerFake import com.android.internal.policy.IKeyguardDismissCallback Loading Loading @@ -88,9 +89,11 @@ import com.android.systemui.scene.data.model.asIterable import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.domain.interactor.sceneBackInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.shared.system.QuickStepContract import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor Loading Loading @@ -161,6 +164,7 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test @DisableFlags(DualShade.FLAG_NAME) fun hydrateVisibility() = testScope.runTest { val currentDesiredSceneKey by collectLastValue(sceneInteractor.currentScene) Loading Loading @@ -220,6 +224,87 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(isVisible).isFalse() } @Test @EnableFlags(DualShade.FLAG_NAME) fun hydrateVisibility_dualShade() = testScope.runTest { val currentDesiredSceneKey by collectLastValue(sceneInteractor.currentScene) val currentDesiredOverlays by collectLastValue(sceneInteractor.currentOverlays) val isVisible by collectLastValue(sceneInteractor.isVisible) val transitionStateFlow = prepareState( authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = true, initialSceneKey = Scenes.Gone, ) assertThat(currentDesiredSceneKey).isEqualTo(Scenes.Gone) assertThat(currentDesiredOverlays).isEmpty() assertThat(isVisible).isTrue() underTest.start() assertThat(isVisible).isFalse() // Expand the notifications shade. fakeSceneDataSource.pause() sceneInteractor.showOverlay(Overlays.NotificationsShade, "reason") transitionStateFlow.value = ObservableTransitionState.Transition.ShowOrHideOverlay( overlay = Overlays.NotificationsShade, fromContent = Scenes.Gone, toContent = Overlays.NotificationsShade, currentScene = Scenes.Gone, currentOverlays = flowOf(emptySet()), progress = flowOf(0.5f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) assertThat(isVisible).isTrue() fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) transitionStateFlow.value = ObservableTransitionState.Idle( currentScene = Scenes.Gone, currentOverlays = setOf(Overlays.NotificationsShade), ) assertThat(isVisible).isTrue() // Collapse the notifications shade. fakeSceneDataSource.pause() sceneInteractor.hideOverlay(Overlays.NotificationsShade, "reason") transitionStateFlow.value = ObservableTransitionState.Transition.ShowOrHideOverlay( overlay = Overlays.NotificationsShade, fromContent = Overlays.NotificationsShade, toContent = Scenes.Gone, currentScene = Scenes.Gone, currentOverlays = flowOf(setOf(Overlays.NotificationsShade)), progress = flowOf(0.5f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) assertThat(isVisible).isTrue() fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) transitionStateFlow.value = ObservableTransitionState.Idle( currentScene = Scenes.Gone, currentOverlays = emptySet(), ) assertThat(isVisible).isFalse() kosmos.headsUpNotificationRepository.setNotifications( buildNotificationRows(isPinned = true) ) assertThat(isVisible).isTrue() kosmos.headsUpNotificationRepository.setNotifications( buildNotificationRows(isPinned = false) ) assertThat(isVisible).isFalse() } @Test fun hydrateVisibility_basedOnDeviceProvisioning() = testScope.runTest { Loading Loading @@ -1621,6 +1706,7 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test @DisableFlags(DualShade.FLAG_NAME) fun hydrateInteractionState_whileLocked() = testScope.runTest { val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen) Loading Loading @@ -1707,6 +1793,7 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test @DisableFlags(DualShade.FLAG_NAME) fun hydrateInteractionState_whileUnlocked() = testScope.runTest { val transitionStateFlow = Loading Loading @@ -1794,6 +1881,186 @@ class SceneContainerStartableTest : SysuiTestCase() { ) } @Test @EnableFlags(DualShade.FLAG_NAME) fun hydrateInteractionState_dualShade_whileLocked() = testScope.runTest { val currentDesiredOverlays by collectLastValue(sceneInteractor.currentOverlays) val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen) underTest.start() runCurrent() verify(centralSurfaces).setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true) assertThat(currentDesiredOverlays).isEmpty() clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces) .setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces).setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true) }, ) clearInvocations(centralSurfaces) emulateOverlayTransition( transitionStateFlow = transitionStateFlow, toOverlay = Overlays.NotificationsShade, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces) .setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces).setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true) }, ) clearInvocations(centralSurfaces) emulateOverlayTransition( transitionStateFlow = transitionStateFlow, toOverlay = Overlays.QuickSettingsShade, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) } @Test @EnableFlags(DualShade.FLAG_NAME) fun hydrateInteractionState_dualShade_whileUnlocked() = testScope.runTest { val currentDesiredOverlays by collectLastValue(sceneInteractor.currentOverlays) val transitionStateFlow = prepareState( authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = true, initialSceneKey = Scenes.Gone, ) underTest.start() verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) assertThat(currentDesiredOverlays).isEmpty() clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Shade, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.QuickSettings, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) } @Test fun respondToFalsingDetections() = testScope.runTest { Loading Loading @@ -2131,19 +2398,40 @@ class SceneContainerStartableTest : SysuiTestCase() { verifyAfterTransition: (() -> Unit)? = null, ) { val fromScene = sceneInteractor.currentScene.value val fromOverlays = sceneInteractor.currentOverlays.value sceneInteractor.changeScene(toScene, "reason") runCurrent() verifyBeforeTransition?.invoke() transitionStateFlow.value = ObservableTransitionState.Transition( if (fromOverlays.isEmpty()) { // Regular scene-to-scene transition. ObservableTransitionState.Transition.ChangeScene( fromScene = fromScene, toScene = toScene, currentScene = flowOf(fromScene), currentOverlays = fromOverlays, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } else { // An overlay is present; hide it. ObservableTransitionState.Transition.ShowOrHideOverlay( overlay = fromOverlays.first(), fromContent = fromOverlays.first(), toContent = toScene, currentScene = fromScene, currentOverlays = sceneInteractor.currentOverlays, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } runCurrent() verifyDuringTransition?.invoke() Loading @@ -2152,6 +2440,60 @@ class SceneContainerStartableTest : SysuiTestCase() { verifyAfterTransition?.invoke() } private fun TestScope.emulateOverlayTransition( transitionStateFlow: MutableStateFlow<ObservableTransitionState>, toOverlay: OverlayKey, verifyBeforeTransition: (() -> Unit)? = null, verifyDuringTransition: (() -> Unit)? = null, verifyAfterTransition: (() -> Unit)? = null, ) { val fromScene = sceneInteractor.currentScene.value val fromOverlays = sceneInteractor.currentOverlays.value sceneInteractor.showOverlay(toOverlay, "reason") runCurrent() verifyBeforeTransition?.invoke() transitionStateFlow.value = if (fromOverlays.isEmpty()) { // Show a new overlay. ObservableTransitionState.Transition.ShowOrHideOverlay( overlay = toOverlay, fromContent = fromScene, toContent = toOverlay, currentScene = fromScene, currentOverlays = sceneInteractor.currentOverlays, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } else { // Overlay-to-overlay transition. ObservableTransitionState.Transition.ReplaceOverlay( fromOverlay = fromOverlays.first(), toOverlay = toOverlay, currentScene = fromScene, currentOverlays = sceneInteractor.currentOverlays, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } runCurrent() verifyDuringTransition?.invoke() transitionStateFlow.value = ObservableTransitionState.Idle( currentScene = fromScene, currentOverlays = setOf(toOverlay), ) runCurrent() verifyAfterTransition?.invoke() } private fun TestScope.prepareState( isDeviceUnlocked: Boolean = false, isBypassEnabled: Boolean = false, Loading packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +12 −7 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.session.shared.SessionStorage import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.logger.SceneLogger import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.NotificationShadeWindowController Loading Loading @@ -228,8 +229,10 @@ constructor( is ObservableTransitionState.Idle -> { if (state.currentScene != Scenes.Gone) { true to "scene is not Gone" } else if (state.currentOverlays.isNotEmpty()) { true to "overlay is shown" } else { false to "scene is Gone" false to "scene is Gone and no overlays are shown" } } is ObservableTransitionState.Transition -> { Loading Loading @@ -712,19 +715,21 @@ constructor( if (isDeviceLocked) { sceneInteractor.transitionState .mapNotNull { it as? ObservableTransitionState.Idle } .map { it.currentScene } .map { it.currentScene to it.currentOverlays } .distinctUntilChanged() .map { sceneKey -> when (sceneKey) { .map { (sceneKey, currentOverlays) -> when { // When locked, showing the lockscreen scene should be reported // as "interacting" while showing other scenes should report as // "not interacting". // // This is done here in order to match the legacy // implementation. The real reason why is lost to lore and myth. Scenes.Lockscreen -> true Scenes.Bouncer -> false Scenes.Shade -> false Overlays.NotificationsShade in currentOverlays -> false Overlays.QuickSettingsShade in currentOverlays -> null sceneKey == Scenes.Lockscreen -> true sceneKey == Scenes.Bouncer -> false sceneKey == Scenes.Shade -> false else -> null } } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +350 −8 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.internal.logging.uiEventLoggerFake import com.android.internal.policy.IKeyguardDismissCallback Loading Loading @@ -88,9 +89,11 @@ import com.android.systemui.scene.data.model.asIterable import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.domain.interactor.sceneBackInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.shared.system.QuickStepContract import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor Loading Loading @@ -161,6 +164,7 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test @DisableFlags(DualShade.FLAG_NAME) fun hydrateVisibility() = testScope.runTest { val currentDesiredSceneKey by collectLastValue(sceneInteractor.currentScene) Loading Loading @@ -220,6 +224,87 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(isVisible).isFalse() } @Test @EnableFlags(DualShade.FLAG_NAME) fun hydrateVisibility_dualShade() = testScope.runTest { val currentDesiredSceneKey by collectLastValue(sceneInteractor.currentScene) val currentDesiredOverlays by collectLastValue(sceneInteractor.currentOverlays) val isVisible by collectLastValue(sceneInteractor.isVisible) val transitionStateFlow = prepareState( authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = true, initialSceneKey = Scenes.Gone, ) assertThat(currentDesiredSceneKey).isEqualTo(Scenes.Gone) assertThat(currentDesiredOverlays).isEmpty() assertThat(isVisible).isTrue() underTest.start() assertThat(isVisible).isFalse() // Expand the notifications shade. fakeSceneDataSource.pause() sceneInteractor.showOverlay(Overlays.NotificationsShade, "reason") transitionStateFlow.value = ObservableTransitionState.Transition.ShowOrHideOverlay( overlay = Overlays.NotificationsShade, fromContent = Scenes.Gone, toContent = Overlays.NotificationsShade, currentScene = Scenes.Gone, currentOverlays = flowOf(emptySet()), progress = flowOf(0.5f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) assertThat(isVisible).isTrue() fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) transitionStateFlow.value = ObservableTransitionState.Idle( currentScene = Scenes.Gone, currentOverlays = setOf(Overlays.NotificationsShade), ) assertThat(isVisible).isTrue() // Collapse the notifications shade. fakeSceneDataSource.pause() sceneInteractor.hideOverlay(Overlays.NotificationsShade, "reason") transitionStateFlow.value = ObservableTransitionState.Transition.ShowOrHideOverlay( overlay = Overlays.NotificationsShade, fromContent = Overlays.NotificationsShade, toContent = Scenes.Gone, currentScene = Scenes.Gone, currentOverlays = flowOf(setOf(Overlays.NotificationsShade)), progress = flowOf(0.5f), isInitiatedByUserInput = false, isUserInputOngoing = flowOf(false), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) assertThat(isVisible).isTrue() fakeSceneDataSource.unpause(expectedScene = Scenes.Gone) transitionStateFlow.value = ObservableTransitionState.Idle( currentScene = Scenes.Gone, currentOverlays = emptySet(), ) assertThat(isVisible).isFalse() kosmos.headsUpNotificationRepository.setNotifications( buildNotificationRows(isPinned = true) ) assertThat(isVisible).isTrue() kosmos.headsUpNotificationRepository.setNotifications( buildNotificationRows(isPinned = false) ) assertThat(isVisible).isFalse() } @Test fun hydrateVisibility_basedOnDeviceProvisioning() = testScope.runTest { Loading Loading @@ -1621,6 +1706,7 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test @DisableFlags(DualShade.FLAG_NAME) fun hydrateInteractionState_whileLocked() = testScope.runTest { val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen) Loading Loading @@ -1707,6 +1793,7 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test @DisableFlags(DualShade.FLAG_NAME) fun hydrateInteractionState_whileUnlocked() = testScope.runTest { val transitionStateFlow = Loading Loading @@ -1794,6 +1881,186 @@ class SceneContainerStartableTest : SysuiTestCase() { ) } @Test @EnableFlags(DualShade.FLAG_NAME) fun hydrateInteractionState_dualShade_whileLocked() = testScope.runTest { val currentDesiredOverlays by collectLastValue(sceneInteractor.currentOverlays) val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen) underTest.start() runCurrent() verify(centralSurfaces).setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true) assertThat(currentDesiredOverlays).isEmpty() clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces) .setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces).setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true) }, ) clearInvocations(centralSurfaces) emulateOverlayTransition( transitionStateFlow = transitionStateFlow, toOverlay = Overlays.NotificationsShade, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces) .setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces).setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true) }, ) clearInvocations(centralSurfaces) emulateOverlayTransition( transitionStateFlow = transitionStateFlow, toOverlay = Overlays.QuickSettingsShade, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) } @Test @EnableFlags(DualShade.FLAG_NAME) fun hydrateInteractionState_dualShade_whileUnlocked() = testScope.runTest { val currentDesiredOverlays by collectLastValue(sceneInteractor.currentOverlays) val transitionStateFlow = prepareState( authenticationMethod = AuthenticationMethodModel.Pin, isDeviceUnlocked = true, initialSceneKey = Scenes.Gone, ) underTest.start() verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) assertThat(currentDesiredOverlays).isEmpty() clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Bouncer, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Shade, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.Lockscreen, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) clearInvocations(centralSurfaces) emulateSceneTransition( transitionStateFlow = transitionStateFlow, toScene = Scenes.QuickSettings, verifyBeforeTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyDuringTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, verifyAfterTransition = { verify(centralSurfaces, never()).setInteracting(anyInt(), anyBoolean()) }, ) } @Test fun respondToFalsingDetections() = testScope.runTest { Loading Loading @@ -2131,19 +2398,40 @@ class SceneContainerStartableTest : SysuiTestCase() { verifyAfterTransition: (() -> Unit)? = null, ) { val fromScene = sceneInteractor.currentScene.value val fromOverlays = sceneInteractor.currentOverlays.value sceneInteractor.changeScene(toScene, "reason") runCurrent() verifyBeforeTransition?.invoke() transitionStateFlow.value = ObservableTransitionState.Transition( if (fromOverlays.isEmpty()) { // Regular scene-to-scene transition. ObservableTransitionState.Transition.ChangeScene( fromScene = fromScene, toScene = toScene, currentScene = flowOf(fromScene), currentOverlays = fromOverlays, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } else { // An overlay is present; hide it. ObservableTransitionState.Transition.ShowOrHideOverlay( overlay = fromOverlays.first(), fromContent = fromOverlays.first(), toContent = toScene, currentScene = fromScene, currentOverlays = sceneInteractor.currentOverlays, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } runCurrent() verifyDuringTransition?.invoke() Loading @@ -2152,6 +2440,60 @@ class SceneContainerStartableTest : SysuiTestCase() { verifyAfterTransition?.invoke() } private fun TestScope.emulateOverlayTransition( transitionStateFlow: MutableStateFlow<ObservableTransitionState>, toOverlay: OverlayKey, verifyBeforeTransition: (() -> Unit)? = null, verifyDuringTransition: (() -> Unit)? = null, verifyAfterTransition: (() -> Unit)? = null, ) { val fromScene = sceneInteractor.currentScene.value val fromOverlays = sceneInteractor.currentOverlays.value sceneInteractor.showOverlay(toOverlay, "reason") runCurrent() verifyBeforeTransition?.invoke() transitionStateFlow.value = if (fromOverlays.isEmpty()) { // Show a new overlay. ObservableTransitionState.Transition.ShowOrHideOverlay( overlay = toOverlay, fromContent = fromScene, toContent = toOverlay, currentScene = fromScene, currentOverlays = sceneInteractor.currentOverlays, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } else { // Overlay-to-overlay transition. ObservableTransitionState.Transition.ReplaceOverlay( fromOverlay = fromOverlays.first(), toOverlay = toOverlay, currentScene = fromScene, currentOverlays = sceneInteractor.currentOverlays, progress = flowOf(0.5f), isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } runCurrent() verifyDuringTransition?.invoke() transitionStateFlow.value = ObservableTransitionState.Idle( currentScene = fromScene, currentOverlays = setOf(toOverlay), ) runCurrent() verifyAfterTransition?.invoke() } private fun TestScope.prepareState( isDeviceUnlocked: Boolean = false, isBypassEnabled: Boolean = false, Loading
packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +12 −7 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.session.shared.SessionStorage import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.logger.SceneLogger import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.NotificationShadeWindowController Loading Loading @@ -228,8 +229,10 @@ constructor( is ObservableTransitionState.Idle -> { if (state.currentScene != Scenes.Gone) { true to "scene is not Gone" } else if (state.currentOverlays.isNotEmpty()) { true to "overlay is shown" } else { false to "scene is Gone" false to "scene is Gone and no overlays are shown" } } is ObservableTransitionState.Transition -> { Loading Loading @@ -712,19 +715,21 @@ constructor( if (isDeviceLocked) { sceneInteractor.transitionState .mapNotNull { it as? ObservableTransitionState.Idle } .map { it.currentScene } .map { it.currentScene to it.currentOverlays } .distinctUntilChanged() .map { sceneKey -> when (sceneKey) { .map { (sceneKey, currentOverlays) -> when { // When locked, showing the lockscreen scene should be reported // as "interacting" while showing other scenes should report as // "not interacting". // // This is done here in order to match the legacy // implementation. The real reason why is lost to lore and myth. Scenes.Lockscreen -> true Scenes.Bouncer -> false Scenes.Shade -> false Overlays.NotificationsShade in currentOverlays -> false Overlays.QuickSettingsShade in currentOverlays -> null sceneKey == Scenes.Lockscreen -> true sceneKey == Scenes.Bouncer -> false sceneKey == Scenes.Shade -> false else -> null } } Loading