Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt +144 −1 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith Loading Loading @@ -236,6 +237,58 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { assertThat(actual).isFalse() } @Test fun toggleNotificationsShade_singleShade_throwsException() = kosmos.runTest { // GIVEN single shade is enabled enableSingleShade() // WHEN the notifications shade is toggled // THEN an IllegalStateException is thrown assertThrows(IllegalStateException::class.java) { underTest.toggleNotificationsShade("reason") } } @Test fun toggleNotificationsShade_splitShade_throwsException() = kosmos.runTest { // GIVEN split shade is enabled enableSplitShade() // WHEN the notifications shade is toggled // THEN an IllegalStateException is thrown assertThrows(IllegalStateException::class.java) { underTest.toggleNotificationsShade("reason") } } @Test fun toggleQuickSettingsShade_singleShade_throwsException() = kosmos.runTest { // GIVEN single shade is enabled enableSingleShade() // WHEN the quick settings shade is toggled // THEN an IllegalStateException is thrown assertThrows(IllegalStateException::class.java) { underTest.toggleQuickSettingsShade("reason") } } @Test fun toggleQuickSettingsShade_splitShade_throwsException() = kosmos.runTest { // GIVEN split shade is enabled enableSplitShade() // WHEN the quick settings shade is toggled // THEN an IllegalStateException is thrown assertThrows(IllegalStateException::class.java) { underTest.toggleQuickSettingsShade("reason") } } @Test fun lockscreenShadeExpansion_idle_onScene() = kosmos.runTest { Loading Loading @@ -862,6 +915,96 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { assertThat(currentOverlays).isEmpty() } @Test fun toggleNotificationsShade_dualShade_showsNotificationsOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and no overlays are open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) assertThat(currentOverlays).isEmpty() // WHEN the notifications shade is toggled underTest.toggleNotificationsShade("reason") // THEN the notifications overlay is now visible assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade) } @Test fun toggleNotificationsShade_dualShadeWithNotificationsOpen_hidesOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and the notifications overlay is open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) openShade(Overlays.NotificationsShade) // WHEN the notifications shade is toggled underTest.toggleNotificationsShade("reason") // THEN all overlays are hidden assertThat(currentOverlays).isEmpty() } @Test fun toggleNotificationsShade_dualShadeWithQsOpen_replacesWithNotificationsOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and the QS overlay is open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) openShade(Overlays.QuickSettingsShade) // WHEN the notifications shade is toggled underTest.toggleNotificationsShade("reason") // THEN the QS overlay is replaced by the notifications overlay assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade) } @Test fun toggleQuickSettingsShade_dualShade_showsQsOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and no overlays are open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) assertThat(currentOverlays).isEmpty() // WHEN the QS shade is toggled underTest.toggleQuickSettingsShade("reason") // THEN the QS overlay is now visible assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade) } @Test fun toggleQuickSettingsShade_dualShadeWithQsOpen_hidesOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and the QS overlay is open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) openShade(Overlays.QuickSettingsShade) // WHEN the QS shade is toggled underTest.toggleQuickSettingsShade("reason") // THEN all overlays are hidden assertThat(currentOverlays).isEmpty() } @Test fun toggleQuickSettingsShade_dualShadeWithNotificationsOpen_replacesWithQsOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and the notifications overlay is open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) openShade(Overlays.NotificationsShade) // WHEN the QS shade is toggled underTest.toggleQuickSettingsShade("reason") // THEN the notifications overlay is replaced by the QS overlay assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade) } private fun Kosmos.openShade(overlay: OverlayKey) { val shadeMode by collectLastValue(shadeMode) val isAnyExpanded by collectLastValue(underTest.isAnyExpanded) Loading packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +9 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,9 @@ interface BaseShadeInteractor { /** The amount [0-1] that the Notifications Shade has been opened. */ val shadeExpansion: StateFlow<Float> /** Whether the Notifications Shade is expanded a non-zero amount. */ val isNotificationsExpanded: StateFlow<Boolean> /** * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will * report 0f. If split shade is enabled, value matches shadeExpansion. Loading Loading @@ -141,6 +144,12 @@ interface BaseShadeInteractor { bypassNotificationsShade: Boolean = false, ) /** Toggles the Notifications shade. Will replace the QuickSettings shade if it's open. */ fun toggleNotificationsShade(loggingReason: String, transitionKey: TransitionKey? = null) /** Toggles the Quick Settings shade. Will replace the Notifications shade if it's open. */ fun toggleQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey? = null) /** * Triggers the collapse (closing) of the notifications shade or quick settings shade, whichever * is open. If both are already collapsed, this has no effect. Loading packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt +5 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ class ShadeInteractorEmptyImpl @Inject constructor() : ShadeInteractor { override val isShadeEnabled: StateFlow<Boolean> = inactiveFlowBoolean override val isQsEnabled: StateFlow<Boolean> = inactiveFlowBoolean override val shadeExpansion: StateFlow<Float> = inactiveFlowFloat override val isNotificationsExpanded: StateFlow<Boolean> = inactiveFlowBoolean override val isShadeAnyExpanded: StateFlow<Boolean> = inactiveFlowBoolean override val qsExpansion: StateFlow<Float> = inactiveFlowFloat override val isQsExpanded: StateFlow<Boolean> = inactiveFlowBoolean Loading Loading @@ -59,5 +60,9 @@ class ShadeInteractorEmptyImpl @Inject constructor() : ShadeInteractor { bypassNotificationsShade: Boolean, ) {} override fun toggleNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {} override fun toggleQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) {} override fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey?) {} } packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt +16 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine Loading Loading @@ -82,6 +83,9 @@ constructor( .traceAsCounter("panel_expansion") { (it * 100f).toInt() } .stateIn(scope, SharingStarted.Eagerly, 0f) @Deprecated("Do not use. isNotificationsExpanded is only relevant in SceneContainer") override val isNotificationsExpanded: StateFlow<Boolean> = MutableStateFlow(false) override val qsExpansion: StateFlow<Float> = repository.qsExpansion override val isQsExpanded: StateFlow<Boolean> = repository.legacyIsQsExpanded Loading Loading @@ -138,6 +142,18 @@ constructor( ) } override fun toggleNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) { throw UnsupportedOperationException( "toggleNotificationShade() is not supported in legacy shade" ) } override fun toggleQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) { throw UnsupportedOperationException( "toggleQuickSettingsShade() is not supported in legacy shade" ) } override fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey?) { throw UnsupportedOperationException( "collapseEitherShade() is not supported in legacy shade" Loading packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt +32 −1 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ import kotlinx.coroutines.flow.stateIn class ShadeInteractorSceneContainerImpl @Inject constructor( @Application scope: CoroutineScope, @Application private val scope: CoroutineScope, private val sceneInteractor: SceneInteractor, private val shadeModeInteractor: ShadeModeInteractor, ) : BaseShadeInteractor { Loading @@ -67,6 +67,9 @@ constructor( .traceAsCounter("panel_expansion") { (it * 100f).toInt() } .stateIn(scope, SharingStarted.Eagerly, 0f) override val isNotificationsExpanded: StateFlow<Boolean> = shadeExpansion.map { it > 0 }.stateIn(scope, SharingStarted.Eagerly, false) override val qsExpansion: StateFlow<Float> = shadeModeInteractor.shadeMode .flatMapLatest { shadeMode -> transitionProgressExpansion(shadeMode.qsContentKey) } Loading Loading @@ -261,6 +264,34 @@ constructor( } } override fun toggleNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) { check(shadeModeInteractor.isDualShade) { "toggleNotificationsShade should only be called when dualShade is enabled." } if (isNotificationsExpanded.value) { android.util.Log.e("amehfooz", "Collapse notification shade") collapseNotificationsShade(loggingReason, transitionKey) } else { android.util.Log.e("amehfooz", "Expand Notifications Shade") expandNotificationsShade(loggingReason, transitionKey) } } override fun toggleQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) { check(shadeModeInteractor.isDualShade) { "toggleQuickSettingsShade should only be called when dualShade is enabled." } if (isQsExpanded.value) { collapseQuickSettingsShade( loggingReason = loggingReason, transitionKey = transitionKey, bypassNotificationsShade = true, ) } else { expandQuickSettingsShade(loggingReason, transitionKey) } } override fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey?) { // Note: The notifications shade and QS shade may be both partially expanded simultaneously, // so we don't use an 'else' clause here. Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt +144 −1 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith Loading Loading @@ -236,6 +237,58 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { assertThat(actual).isFalse() } @Test fun toggleNotificationsShade_singleShade_throwsException() = kosmos.runTest { // GIVEN single shade is enabled enableSingleShade() // WHEN the notifications shade is toggled // THEN an IllegalStateException is thrown assertThrows(IllegalStateException::class.java) { underTest.toggleNotificationsShade("reason") } } @Test fun toggleNotificationsShade_splitShade_throwsException() = kosmos.runTest { // GIVEN split shade is enabled enableSplitShade() // WHEN the notifications shade is toggled // THEN an IllegalStateException is thrown assertThrows(IllegalStateException::class.java) { underTest.toggleNotificationsShade("reason") } } @Test fun toggleQuickSettingsShade_singleShade_throwsException() = kosmos.runTest { // GIVEN single shade is enabled enableSingleShade() // WHEN the quick settings shade is toggled // THEN an IllegalStateException is thrown assertThrows(IllegalStateException::class.java) { underTest.toggleQuickSettingsShade("reason") } } @Test fun toggleQuickSettingsShade_splitShade_throwsException() = kosmos.runTest { // GIVEN split shade is enabled enableSplitShade() // WHEN the quick settings shade is toggled // THEN an IllegalStateException is thrown assertThrows(IllegalStateException::class.java) { underTest.toggleQuickSettingsShade("reason") } } @Test fun lockscreenShadeExpansion_idle_onScene() = kosmos.runTest { Loading Loading @@ -862,6 +915,96 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { assertThat(currentOverlays).isEmpty() } @Test fun toggleNotificationsShade_dualShade_showsNotificationsOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and no overlays are open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) assertThat(currentOverlays).isEmpty() // WHEN the notifications shade is toggled underTest.toggleNotificationsShade("reason") // THEN the notifications overlay is now visible assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade) } @Test fun toggleNotificationsShade_dualShadeWithNotificationsOpen_hidesOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and the notifications overlay is open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) openShade(Overlays.NotificationsShade) // WHEN the notifications shade is toggled underTest.toggleNotificationsShade("reason") // THEN all overlays are hidden assertThat(currentOverlays).isEmpty() } @Test fun toggleNotificationsShade_dualShadeWithQsOpen_replacesWithNotificationsOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and the QS overlay is open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) openShade(Overlays.QuickSettingsShade) // WHEN the notifications shade is toggled underTest.toggleNotificationsShade("reason") // THEN the QS overlay is replaced by the notifications overlay assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade) } @Test fun toggleQuickSettingsShade_dualShade_showsQsOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and no overlays are open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) assertThat(currentOverlays).isEmpty() // WHEN the QS shade is toggled underTest.toggleQuickSettingsShade("reason") // THEN the QS overlay is now visible assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade) } @Test fun toggleQuickSettingsShade_dualShadeWithQsOpen_hidesOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and the QS overlay is open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) openShade(Overlays.QuickSettingsShade) // WHEN the QS shade is toggled underTest.toggleQuickSettingsShade("reason") // THEN all overlays are hidden assertThat(currentOverlays).isEmpty() } @Test fun toggleQuickSettingsShade_dualShadeWithNotificationsOpen_replacesWithQsOverlay() = kosmos.runTest { // GIVEN dual shade is enabled and the notifications overlay is open enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) openShade(Overlays.NotificationsShade) // WHEN the QS shade is toggled underTest.toggleQuickSettingsShade("reason") // THEN the notifications overlay is replaced by the QS overlay assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade) } private fun Kosmos.openShade(overlay: OverlayKey) { val shadeMode by collectLastValue(shadeMode) val isAnyExpanded by collectLastValue(underTest.isAnyExpanded) Loading
packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +9 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,9 @@ interface BaseShadeInteractor { /** The amount [0-1] that the Notifications Shade has been opened. */ val shadeExpansion: StateFlow<Float> /** Whether the Notifications Shade is expanded a non-zero amount. */ val isNotificationsExpanded: StateFlow<Boolean> /** * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will * report 0f. If split shade is enabled, value matches shadeExpansion. Loading Loading @@ -141,6 +144,12 @@ interface BaseShadeInteractor { bypassNotificationsShade: Boolean = false, ) /** Toggles the Notifications shade. Will replace the QuickSettings shade if it's open. */ fun toggleNotificationsShade(loggingReason: String, transitionKey: TransitionKey? = null) /** Toggles the Quick Settings shade. Will replace the Notifications shade if it's open. */ fun toggleQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey? = null) /** * Triggers the collapse (closing) of the notifications shade or quick settings shade, whichever * is open. If both are already collapsed, this has no effect. Loading
packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt +5 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ class ShadeInteractorEmptyImpl @Inject constructor() : ShadeInteractor { override val isShadeEnabled: StateFlow<Boolean> = inactiveFlowBoolean override val isQsEnabled: StateFlow<Boolean> = inactiveFlowBoolean override val shadeExpansion: StateFlow<Float> = inactiveFlowFloat override val isNotificationsExpanded: StateFlow<Boolean> = inactiveFlowBoolean override val isShadeAnyExpanded: StateFlow<Boolean> = inactiveFlowBoolean override val qsExpansion: StateFlow<Float> = inactiveFlowFloat override val isQsExpanded: StateFlow<Boolean> = inactiveFlowBoolean Loading Loading @@ -59,5 +60,9 @@ class ShadeInteractorEmptyImpl @Inject constructor() : ShadeInteractor { bypassNotificationsShade: Boolean, ) {} override fun toggleNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {} override fun toggleQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) {} override fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey?) {} }
packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt +16 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine Loading Loading @@ -82,6 +83,9 @@ constructor( .traceAsCounter("panel_expansion") { (it * 100f).toInt() } .stateIn(scope, SharingStarted.Eagerly, 0f) @Deprecated("Do not use. isNotificationsExpanded is only relevant in SceneContainer") override val isNotificationsExpanded: StateFlow<Boolean> = MutableStateFlow(false) override val qsExpansion: StateFlow<Float> = repository.qsExpansion override val isQsExpanded: StateFlow<Boolean> = repository.legacyIsQsExpanded Loading Loading @@ -138,6 +142,18 @@ constructor( ) } override fun toggleNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) { throw UnsupportedOperationException( "toggleNotificationShade() is not supported in legacy shade" ) } override fun toggleQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) { throw UnsupportedOperationException( "toggleQuickSettingsShade() is not supported in legacy shade" ) } override fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey?) { throw UnsupportedOperationException( "collapseEitherShade() is not supported in legacy shade" Loading
packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt +32 −1 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ import kotlinx.coroutines.flow.stateIn class ShadeInteractorSceneContainerImpl @Inject constructor( @Application scope: CoroutineScope, @Application private val scope: CoroutineScope, private val sceneInteractor: SceneInteractor, private val shadeModeInteractor: ShadeModeInteractor, ) : BaseShadeInteractor { Loading @@ -67,6 +67,9 @@ constructor( .traceAsCounter("panel_expansion") { (it * 100f).toInt() } .stateIn(scope, SharingStarted.Eagerly, 0f) override val isNotificationsExpanded: StateFlow<Boolean> = shadeExpansion.map { it > 0 }.stateIn(scope, SharingStarted.Eagerly, false) override val qsExpansion: StateFlow<Float> = shadeModeInteractor.shadeMode .flatMapLatest { shadeMode -> transitionProgressExpansion(shadeMode.qsContentKey) } Loading Loading @@ -261,6 +264,34 @@ constructor( } } override fun toggleNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) { check(shadeModeInteractor.isDualShade) { "toggleNotificationsShade should only be called when dualShade is enabled." } if (isNotificationsExpanded.value) { android.util.Log.e("amehfooz", "Collapse notification shade") collapseNotificationsShade(loggingReason, transitionKey) } else { android.util.Log.e("amehfooz", "Expand Notifications Shade") expandNotificationsShade(loggingReason, transitionKey) } } override fun toggleQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) { check(shadeModeInteractor.isDualShade) { "toggleQuickSettingsShade should only be called when dualShade is enabled." } if (isQsExpanded.value) { collapseQuickSettingsShade( loggingReason = loggingReason, transitionKey = transitionKey, bypassNotificationsShade = true, ) } else { expandQuickSettingsShade(loggingReason, transitionKey) } } override fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey?) { // Note: The notifications shade and QS shade may be both partially expanded simultaneously, // so we don't use an 'else' clause here. Loading