diff options
| author | 2024-09-25 11:52:14 +0000 | |
|---|---|---|
| committer | 2024-09-27 12:03:24 +0000 | |
| commit | e46d53bb215e0723c5ebc44ed70f26bcb418cf3d (patch) | |
| tree | 956c363aa0a5b060946a58ac80d68ea3767a7394 | |
| parent | 0a415b7b132652597ffb8379ff9c50c99fea243e (diff) | |
[bc25] Collapse the shade when switching to bouncer.
Bug: 356596436
Bug: 369513770
Flag: com.android.systemui.scene_container
Flag: com.android.systemui.dual_shade
Test: Added unit tests.
Test: Existing unit tests still pass.
Test: Verified manually by opening both shades on lockscreen, long
pressing on Bluetooth, and observing that the shade is closed while the
bouncer opens.
Change-Id: I5edc7e72e65c5af729a957d0ad962b7c1fb2e5c2
6 files changed, 255 insertions, 12 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt index 3388c75bcf78..ada2138d4d52 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt @@ -21,19 +21,34 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository +import com.android.systemui.authentication.domain.interactor.AuthenticationResult +import com.android.systemui.authentication.domain.interactor.authenticationInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope +import com.android.systemui.lifecycle.activateIn +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest +import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.startable.sceneContainerStartable 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.shade.shared.flag.DualShade import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @@ -47,18 +62,84 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() { private val underTest by lazy { kosmos.notificationsShadeOverlayContentViewModel } + @Before + fun setUp() { + kosmos.sceneContainerStartable.start() + underTest.activateIn(testScope) + } + @Test fun onScrimClicked_hidesShade() = testScope.runTest { val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) - sceneInteractor.showOverlay( - overlay = Overlays.NotificationsShade, - loggingReason = "test", - ) + sceneInteractor.showOverlay(Overlays.NotificationsShade, "test") assertThat(currentOverlays).contains(Overlays.NotificationsShade) underTest.onScrimClicked() assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade) } + + @Test + fun deviceLocked_hidesShade() = + testScope.runTest { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + unlockDevice() + sceneInteractor.showOverlay(Overlays.NotificationsShade, "test") + assertThat(currentOverlays).contains(Overlays.NotificationsShade) + + lockDevice() + + assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade) + } + + @Test + fun bouncerShown_hidesShade() = + testScope.runTest { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + lockDevice() + sceneInteractor.showOverlay(Overlays.NotificationsShade, "test") + assertThat(currentOverlays).contains(Overlays.NotificationsShade) + + sceneInteractor.changeScene(Scenes.Bouncer, "test") + runCurrent() + + assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade) + } + + @Test + fun shadeNotTouchable_hidesShade() = + testScope.runTest { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + val isShadeTouchable by collectLastValue(kosmos.shadeInteractor.isShadeTouchable) + assertThat(isShadeTouchable).isTrue() + sceneInteractor.showOverlay(Overlays.NotificationsShade, "test") + assertThat(currentOverlays).contains(Overlays.NotificationsShade) + + lockDevice() + assertThat(isShadeTouchable).isFalse() + assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade) + } + + private fun TestScope.lockDevice() { + val currentScene by collectLastValue(sceneInteractor.currentScene) + kosmos.powerInteractor.setAsleepForTest() + runCurrent() + + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + } + + private suspend fun TestScope.unlockDevice() { + val currentScene by collectLastValue(sceneInteractor.currentScene) + kosmos.powerInteractor.setAwakeForTest() + runCurrent() + assertThat( + kosmos.authenticationInteractor.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN + ) + ) + .isEqualTo(AuthenticationResult.SUCCEEDED) + + assertThat(currentScene).isEqualTo(Scenes.Gone) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt index 8c7ec4743e7c..f32894dfd383 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt @@ -21,18 +21,33 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository +import com.android.systemui.authentication.domain.interactor.AuthenticationResult +import com.android.systemui.authentication.domain.interactor.authenticationInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope +import com.android.systemui.lifecycle.activateIn +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest +import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.startable.sceneContainerStartable 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.shade.shared.flag.DualShade import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @@ -46,18 +61,84 @@ class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() { private val underTest by lazy { kosmos.quickSettingsShadeOverlayContentViewModel } + @Before + fun setUp() { + kosmos.sceneContainerStartable.start() + underTest.activateIn(testScope) + } + @Test fun onScrimClicked_hidesShade() = testScope.runTest { val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) - sceneInteractor.showOverlay( - overlay = Overlays.QuickSettingsShade, - loggingReason = "test", - ) + sceneInteractor.showOverlay(Overlays.QuickSettingsShade, "test") assertThat(currentOverlays).contains(Overlays.QuickSettingsShade) underTest.onScrimClicked() assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade) } + + @Test + fun deviceLocked_hidesShade() = + testScope.runTest { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + unlockDevice() + sceneInteractor.showOverlay(Overlays.QuickSettingsShade, "test") + assertThat(currentOverlays).contains(Overlays.QuickSettingsShade) + + lockDevice() + + assertThat(currentOverlays).isEmpty() + } + + @Test + fun bouncerShown_hidesShade() = + testScope.runTest { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + lockDevice() + sceneInteractor.showOverlay(Overlays.QuickSettingsShade, "test") + assertThat(currentOverlays).contains(Overlays.QuickSettingsShade) + + sceneInteractor.changeScene(Scenes.Bouncer, "test") + runCurrent() + + assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade) + } + + @Test + fun shadeNotTouchable_hidesShade() = + testScope.runTest { + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + val isShadeTouchable by collectLastValue(kosmos.shadeInteractor.isShadeTouchable) + assertThat(isShadeTouchable).isTrue() + sceneInteractor.showOverlay(Overlays.QuickSettingsShade, "test") + assertThat(currentOverlays).contains(Overlays.QuickSettingsShade) + + lockDevice() + assertThat(isShadeTouchable).isFalse() + assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade) + } + + private fun TestScope.lockDevice() { + val currentScene by collectLastValue(sceneInteractor.currentScene) + kosmos.powerInteractor.setAsleepForTest() + runCurrent() + + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + } + + private suspend fun TestScope.unlockDevice() { + val currentScene by collectLastValue(sceneInteractor.currentScene) + kosmos.powerInteractor.setAwakeForTest() + runCurrent() + assertThat( + kosmos.authenticationInteractor.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN + ) + ) + .isEqualTo(AuthenticationResult.SUCCEEDED) + + assertThat(currentScene).isEqualTo(Scenes.Gone) + } } diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt index 219e45c36b50..0e5404164ba1 100644 --- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt @@ -16,11 +16,19 @@ package com.android.systemui.notifications.ui.viewmodel +import com.android.systemui.lifecycle.ExclusiveActivatable +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.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.launch /** * Models UI state used to render the content of the notifications shade overlay. @@ -33,10 +41,40 @@ class NotificationsShadeOverlayContentViewModel constructor( val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory, val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory, + val sceneInteractor: SceneInteractor, private val shadeInteractor: ShadeInteractor, -) { +) : ExclusiveActivatable() { + + override suspend fun onActivated(): Nothing { + coroutineScope { + launch { + sceneInteractor.currentScene.collect { currentScene -> + when (currentScene) { + // TODO(b/369513770): The ShadeSession should be preserved in this scenario. + Scenes.Bouncer -> + shadeInteractor.collapseNotificationsShade( + loggingReason = "bouncer shown while shade is open" + ) + } + } + } + + launch { + shadeInteractor.isShadeTouchable + .distinctUntilChanged() + .filter { !it } + .collect { + shadeInteractor.collapseNotificationsShade( + loggingReason = "device became non-interactive" + ) + } + } + } + awaitCancellation() + } + fun onScrimClicked() { - shadeInteractor.collapseNotificationsShade(loggingReason = "Shade scrim clicked") + shadeInteractor.collapseNotificationsShade(loggingReason = "shade scrim clicked") } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt index 7c8fbeaec0d5..afb9a788ec24 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt @@ -16,10 +16,18 @@ package com.android.systemui.qs.ui.viewmodel +import com.android.systemui.lifecycle.ExclusiveActivatable +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.shade.ui.viewmodel.ShadeHeaderViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.launch /** * Models UI state used to render the content of the quick settings shade overlay. @@ -31,11 +39,42 @@ class QuickSettingsShadeOverlayContentViewModel @AssistedInject constructor( val shadeInteractor: ShadeInteractor, + val sceneInteractor: SceneInteractor, val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory, val quickSettingsContainerViewModel: QuickSettingsContainerViewModel, -) { +) : ExclusiveActivatable() { + + override suspend fun onActivated(): Nothing { + coroutineScope { + launch { + sceneInteractor.currentScene.collect { currentScene -> + when (currentScene) { + // TODO(b/369513770): The ShadeSession should be preserved in this scenario. + Scenes.Bouncer -> + shadeInteractor.collapseQuickSettingsShade( + loggingReason = "bouncer shown while shade is open" + ) + } + } + } + + launch { + shadeInteractor.isShadeTouchable + .distinctUntilChanged() + .filter { !it } + .collect { + shadeInteractor.collapseQuickSettingsShade( + loggingReason = "device became non-interactive" + ) + } + } + } + + awaitCancellation() + } + fun onScrimClicked() { - shadeInteractor.collapseQuickSettingsShade(loggingReason = "Shade scrim clicked") + shadeInteractor.collapseQuickSettingsShade(loggingReason = "shade scrim clicked") } @AssistedFactory diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt index a80a4095a264..6540ed6bba45 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.ui.viewmodel import com.android.systemui.kosmos.Kosmos +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory @@ -24,6 +25,7 @@ val Kosmos.quickSettingsShadeOverlayContentViewModel: QuickSettingsShadeOverlayC Kosmos.Fixture { QuickSettingsShadeOverlayContentViewModel( shadeInteractor = shadeInteractor, + sceneInteractor = sceneInteractor, shadeHeaderViewModelFactory = shadeHeaderViewModelFactory, quickSettingsContainerViewModel = quickSettingsContainerViewModel, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt index 7a15fdf95734..718347fc3490 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.shade.ui.viewmodel import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory @@ -27,6 +28,7 @@ val Kosmos.notificationsShadeOverlayContentViewModel: NotificationsShadeOverlayContentViewModel( shadeHeaderViewModelFactory = shadeHeaderViewModelFactory, notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory, + sceneInteractor = sceneInteractor, shadeInteractor = shadeInteractor, ) } |