Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeInstantExpansionCommandsTest.kt 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.shade import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState.Idle import com.android.compose.animation.scene.OverlayKey import com.android.systemui.SysuiTestCase import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.scene.data.repository.setSceneTransition 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.shade.domain.interactor.enableDualShade import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter import org.junit.Test import org.junit.runner.RunWith @SmallTest @EnableSceneContainer @RunWith(AndroidJUnit4::class) class ShadeInstantExpansionCommandsTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() @Test fun commandShadeShowNotifications_singleShade() = kosmos.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand( PrintWriter(StringWriter()), arrayOf("expand-notifications-instant"), ) assertThat(currentScene).isEqualTo(Scenes.Shade) } @Test fun commandShadeShowNotifications_dualShade() = kosmos.runTest { enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand( PrintWriter(StringWriter()), arrayOf("expand-notifications-instant"), ) assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade) } @Test fun commandShadeShowQs_singleShade() = kosmos.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand( PrintWriter(StringWriter()), arrayOf("expand-settings-instant"), ) assertThat(currentScene).isEqualTo(Scenes.QuickSettings) } @Test fun commandShadeShowQs_dualShade() = kosmos.runTest { enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand( PrintWriter(StringWriter()), arrayOf("expand-settings-instant"), ) assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade) } @Test fun commandShadeHideNotifications_singleShade() = kosmos.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) shadeInstantExpansionCommands.start() sceneInteractor.changeScene(Scenes.Shade, "test") setSceneTransition(Idle(Scenes.Shade)) commandRegistry.onShellCommand(PrintWriter(StringWriter()), arrayOf("collapse-instant")) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) } @Test fun commandShadeHideNotifications_dualShade() = kosmos.runTest { enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) shadeInstantExpansionCommands.start() setOverlay(Overlays.NotificationsShade) commandRegistry.onShellCommand(PrintWriter(StringWriter()), arrayOf("collapse-instant")) assertThat(currentOverlays).isEmpty() } @Test fun commandShadeHideQs_singleShade() = kosmos.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) shadeInstantExpansionCommands.start() sceneInteractor.changeScene(Scenes.QuickSettings, "test") setSceneTransition(Idle(Scenes.QuickSettings)) commandRegistry.onShellCommand(PrintWriter(StringWriter()), arrayOf("collapse-instant")) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) } @Test fun commandShadeHideQs_dualShade() = kosmos.runTest { enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) setOverlay(Overlays.QuickSettingsShade) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand(PrintWriter(StringWriter()), arrayOf("collapse-instant")) assertThat(currentOverlays).isEmpty() } private fun Kosmos.setOverlay(overlay: OverlayKey) { val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) sceneInteractor.showOverlay(overlay, "test") setSceneTransition(Idle(checkNotNull(currentScene), checkNotNull(currentOverlays))) } } packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +0 −2 Original line number Diff line number Diff line Loading @@ -165,8 +165,6 @@ public interface ShadeController extends CoreStartable { /** * Close the shade if it was open * * @return true if the shade was open, else false */ void collapseShade(); Loading packages/SystemUI/src/com/android/systemui/shade/ShadeInstantExpansionCommands.kt 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.shade import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.TransitionKeys import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import java.io.PrintWriter import javax.inject.Inject import javax.inject.Provider @SysUISingleton class ShadeInstantExpansionCommands @Inject constructor( private val commandRegistry: CommandRegistry, private val instantExpandNotificationsCommand: Provider<InstantExpandNotificationsCommand>, private val instantExpandQsCommand: Provider<InstantExpandQsCommand>, private val instantCollapseShadeCommand: Provider<InstantCollapseShadeCommand>, ) : CoreStartable { override fun start() { if (SceneContainerFlag.isEnabled) { commandRegistry.registerCommand("expand-notifications-instant") { instantExpandNotificationsCommand.get() } commandRegistry.registerCommand("expand-settings-instant") { instantExpandQsCommand.get() } commandRegistry.registerCommand("collapse-instant") { instantCollapseShadeCommand.get() } } } } class InstantExpandNotificationsCommand @Inject constructor(private val shadeInteractor: ShadeInteractor) : Command { override fun execute(pw: PrintWriter, args: List<String>) { shadeInteractor.expandNotificationsShade("adb command", TransitionKeys.Instant) pw.println("Showing Notifications shade") } override fun help(pw: PrintWriter) { pw.println("expand-notifications-instant") pw.println("expands the Notifications shade without animating") pw.println() } } class InstantExpandQsCommand @Inject constructor(private val shadeInteractor: ShadeInteractor) : Command { override fun execute(pw: PrintWriter, args: List<String>) { shadeInteractor.expandQuickSettingsShade("adb command", TransitionKeys.Instant) pw.println("Showing Quick Settings shade") } override fun help(pw: PrintWriter) { pw.println("expand-settings-instant") pw.println("expands the Quick Settings shade without animating") pw.println() } } class InstantCollapseShadeCommand @Inject constructor(private val shadeInteractor: ShadeInteractor) : Command { override fun execute(pw: PrintWriter, args: List<String>) { shadeInteractor.collapseEitherShade("adb command", TransitionKeys.Instant) pw.println("hiding any expanded shade") } override fun help(pw: PrintWriter) { pw.println("collapse-instant") pw.println("collapses any expanded shade without animating") pw.println() } } packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt +9 −2 Original line number Diff line number Diff line Loading @@ -39,10 +39,17 @@ internal abstract class StartShadeModule { @Binds @IntoMap @ClassKey(ShadeStartable::class) abstract fun provideShadeStartable(startable: ShadeStartable): CoreStartable abstract fun bindShadeStartable(startable: ShadeStartable): CoreStartable @Binds @IntoMap @ClassKey(ShadeStateTraceLogger::class) abstract fun provideShadeStateTraceLogger(startable: ShadeStateTraceLogger): CoreStartable abstract fun bindShadeStateTraceLogger(startable: ShadeStateTraceLogger): CoreStartable @Binds @IntoMap @ClassKey(ShadeInstantExpansionCommands::class) abstract fun bindShadeInstantExpansionCommands( startable: ShadeInstantExpansionCommands ): CoreStartable } packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt +16 −7 Original line number Diff line number Diff line Loading @@ -149,9 +149,21 @@ constructor( loggingReason = loggingReason, transitionKey = transitionKey, ) } else { changeSingeShadeScene(Scenes.Shade, transitionKey, loggingReason) } } private fun changeSingeShadeScene( sceneKey: SceneKey, transitionKey: TransitionKey?, loggingReason: String, ) { if (transitionKey == Instant) { sceneInteractor.snapToScene(sceneKey, loggingReason) } else { sceneInteractor.changeScene( toScene = Scenes.Shade, toScene = sceneKey, loggingReason = loggingReason, transitionKey = transitionKey ?: ToSplitShade.takeIf { shadeModeInteractor.isSplitShade }, Loading @@ -174,12 +186,9 @@ constructor( transitionKey = transitionKey, ) } else { val isSplitShade = shadeModeInteractor.isSplitShade sceneInteractor.changeScene( toScene = if (isSplitShade) Scenes.Shade else Scenes.QuickSettings, loggingReason = loggingReason, transitionKey = transitionKey ?: ToSplitShade.takeIf { isSplitShade }, ) val toScene = if (shadeModeInteractor.isSplitShade) Scenes.Shade else Scenes.QuickSettings changeSingeShadeScene(toScene, transitionKey, loggingReason) } } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeInstantExpansionCommandsTest.kt 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.shade import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState.Idle import com.android.compose.animation.scene.OverlayKey import com.android.systemui.SysuiTestCase import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.scene.data.repository.setSceneTransition 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.shade.domain.interactor.enableDualShade import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter import org.junit.Test import org.junit.runner.RunWith @SmallTest @EnableSceneContainer @RunWith(AndroidJUnit4::class) class ShadeInstantExpansionCommandsTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() @Test fun commandShadeShowNotifications_singleShade() = kosmos.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand( PrintWriter(StringWriter()), arrayOf("expand-notifications-instant"), ) assertThat(currentScene).isEqualTo(Scenes.Shade) } @Test fun commandShadeShowNotifications_dualShade() = kosmos.runTest { enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand( PrintWriter(StringWriter()), arrayOf("expand-notifications-instant"), ) assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade) } @Test fun commandShadeShowQs_singleShade() = kosmos.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand( PrintWriter(StringWriter()), arrayOf("expand-settings-instant"), ) assertThat(currentScene).isEqualTo(Scenes.QuickSettings) } @Test fun commandShadeShowQs_dualShade() = kosmos.runTest { enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand( PrintWriter(StringWriter()), arrayOf("expand-settings-instant"), ) assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade) } @Test fun commandShadeHideNotifications_singleShade() = kosmos.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) shadeInstantExpansionCommands.start() sceneInteractor.changeScene(Scenes.Shade, "test") setSceneTransition(Idle(Scenes.Shade)) commandRegistry.onShellCommand(PrintWriter(StringWriter()), arrayOf("collapse-instant")) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) } @Test fun commandShadeHideNotifications_dualShade() = kosmos.runTest { enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) shadeInstantExpansionCommands.start() setOverlay(Overlays.NotificationsShade) commandRegistry.onShellCommand(PrintWriter(StringWriter()), arrayOf("collapse-instant")) assertThat(currentOverlays).isEmpty() } @Test fun commandShadeHideQs_singleShade() = kosmos.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) shadeInstantExpansionCommands.start() sceneInteractor.changeScene(Scenes.QuickSettings, "test") setSceneTransition(Idle(Scenes.QuickSettings)) commandRegistry.onShellCommand(PrintWriter(StringWriter()), arrayOf("collapse-instant")) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) } @Test fun commandShadeHideQs_dualShade() = kosmos.runTest { enableDualShade() val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) setOverlay(Overlays.QuickSettingsShade) shadeInstantExpansionCommands.start() commandRegistry.onShellCommand(PrintWriter(StringWriter()), arrayOf("collapse-instant")) assertThat(currentOverlays).isEmpty() } private fun Kosmos.setOverlay(overlay: OverlayKey) { val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) sceneInteractor.showOverlay(overlay, "test") setSceneTransition(Idle(checkNotNull(currentScene), checkNotNull(currentOverlays))) } }
packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +0 −2 Original line number Diff line number Diff line Loading @@ -165,8 +165,6 @@ public interface ShadeController extends CoreStartable { /** * Close the shade if it was open * * @return true if the shade was open, else false */ void collapseShade(); Loading
packages/SystemUI/src/com/android/systemui/shade/ShadeInstantExpansionCommands.kt 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.shade import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.TransitionKeys import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import java.io.PrintWriter import javax.inject.Inject import javax.inject.Provider @SysUISingleton class ShadeInstantExpansionCommands @Inject constructor( private val commandRegistry: CommandRegistry, private val instantExpandNotificationsCommand: Provider<InstantExpandNotificationsCommand>, private val instantExpandQsCommand: Provider<InstantExpandQsCommand>, private val instantCollapseShadeCommand: Provider<InstantCollapseShadeCommand>, ) : CoreStartable { override fun start() { if (SceneContainerFlag.isEnabled) { commandRegistry.registerCommand("expand-notifications-instant") { instantExpandNotificationsCommand.get() } commandRegistry.registerCommand("expand-settings-instant") { instantExpandQsCommand.get() } commandRegistry.registerCommand("collapse-instant") { instantCollapseShadeCommand.get() } } } } class InstantExpandNotificationsCommand @Inject constructor(private val shadeInteractor: ShadeInteractor) : Command { override fun execute(pw: PrintWriter, args: List<String>) { shadeInteractor.expandNotificationsShade("adb command", TransitionKeys.Instant) pw.println("Showing Notifications shade") } override fun help(pw: PrintWriter) { pw.println("expand-notifications-instant") pw.println("expands the Notifications shade without animating") pw.println() } } class InstantExpandQsCommand @Inject constructor(private val shadeInteractor: ShadeInteractor) : Command { override fun execute(pw: PrintWriter, args: List<String>) { shadeInteractor.expandQuickSettingsShade("adb command", TransitionKeys.Instant) pw.println("Showing Quick Settings shade") } override fun help(pw: PrintWriter) { pw.println("expand-settings-instant") pw.println("expands the Quick Settings shade without animating") pw.println() } } class InstantCollapseShadeCommand @Inject constructor(private val shadeInteractor: ShadeInteractor) : Command { override fun execute(pw: PrintWriter, args: List<String>) { shadeInteractor.collapseEitherShade("adb command", TransitionKeys.Instant) pw.println("hiding any expanded shade") } override fun help(pw: PrintWriter) { pw.println("collapse-instant") pw.println("collapses any expanded shade without animating") pw.println() } }
packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt +9 −2 Original line number Diff line number Diff line Loading @@ -39,10 +39,17 @@ internal abstract class StartShadeModule { @Binds @IntoMap @ClassKey(ShadeStartable::class) abstract fun provideShadeStartable(startable: ShadeStartable): CoreStartable abstract fun bindShadeStartable(startable: ShadeStartable): CoreStartable @Binds @IntoMap @ClassKey(ShadeStateTraceLogger::class) abstract fun provideShadeStateTraceLogger(startable: ShadeStateTraceLogger): CoreStartable abstract fun bindShadeStateTraceLogger(startable: ShadeStateTraceLogger): CoreStartable @Binds @IntoMap @ClassKey(ShadeInstantExpansionCommands::class) abstract fun bindShadeInstantExpansionCommands( startable: ShadeInstantExpansionCommands ): CoreStartable }
packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt +16 −7 Original line number Diff line number Diff line Loading @@ -149,9 +149,21 @@ constructor( loggingReason = loggingReason, transitionKey = transitionKey, ) } else { changeSingeShadeScene(Scenes.Shade, transitionKey, loggingReason) } } private fun changeSingeShadeScene( sceneKey: SceneKey, transitionKey: TransitionKey?, loggingReason: String, ) { if (transitionKey == Instant) { sceneInteractor.snapToScene(sceneKey, loggingReason) } else { sceneInteractor.changeScene( toScene = Scenes.Shade, toScene = sceneKey, loggingReason = loggingReason, transitionKey = transitionKey ?: ToSplitShade.takeIf { shadeModeInteractor.isSplitShade }, Loading @@ -174,12 +186,9 @@ constructor( transitionKey = transitionKey, ) } else { val isSplitShade = shadeModeInteractor.isSplitShade sceneInteractor.changeScene( toScene = if (isSplitShade) Scenes.Shade else Scenes.QuickSettings, loggingReason = loggingReason, transitionKey = transitionKey ?: ToSplitShade.takeIf { isSplitShade }, ) val toScene = if (shadeModeInteractor.isSplitShade) Scenes.Shade else Scenes.QuickSettings changeSingeShadeScene(toScene, transitionKey, loggingReason) } } Loading