From 0825ca60f8117a8ac8bdcab51c5b79b5eb736c59 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Mon, 9 Dec 2024 15:29:10 +0000 Subject: Move shade to the default display when the keyguard is visible This adds a flag in the dagger graph to decide whether to move the notification stack to the default display once the keyguard becomes visible. It's kept as a boolean constant in the dagger graph as it's not clear what the final behaviour will be there, and we want to be able to change it easily if needed. Bug: 362719719 Bug: 381258683 Test: StatusBarTouchShadeDisplayPolicyTest Flag: com.android.systemui.shade_window_goes_around Change-Id: I028208cf33b167c35fce9149a0f269c63cbfce77 --- .../StatusBarTouchShadeDisplayPolicyTest.kt | 55 +++++++++++++++++++++- .../systemui/shade/ShadeDisplayAwareModule.kt | 19 +++++++- .../display/StatusBarTouchShadeDisplayPolicy.kt | 35 +++++++++++--- 3 files changed, 100 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt index ef1ae093bcc9..fd9f5f02ee62 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.display.data.repository.display import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.testKosmos @@ -38,17 +39,31 @@ import org.junit.runner.RunWith class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val testScope = kosmos.testScope + private val keyguardRepository = kosmos.fakeKeyguardRepository private val displayRepository = kosmos.displayRepository - val underTest = StatusBarTouchShadeDisplayPolicy(displayRepository, testScope.backgroundScope) + + private fun createUnderTest( + shadeOnDefaultDisplayWhenLocked: Boolean = false + ): StatusBarTouchShadeDisplayPolicy { + return StatusBarTouchShadeDisplayPolicy( + displayRepository, + keyguardRepository, + testScope.backgroundScope, + shadeOnDefaultDisplayWhenLocked = shadeOnDefaultDisplayWhenLocked, + ) + } @Test fun displayId_defaultToDefaultDisplay() { + val underTest = createUnderTest() + assertThat(underTest.displayId.value).isEqualTo(Display.DEFAULT_DISPLAY) } @Test fun onStatusBarTouched_called_updatesDisplayId() = testScope.runTest { + val underTest = createUnderTest() val displayId by collectLastValue(underTest.displayId) displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL)) @@ -60,6 +75,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() { @Test fun onStatusBarTouched_notExistentDisplay_displayIdNotUpdated() = testScope.runTest { + val underTest = createUnderTest() val displayIds by collectValues(underTest.displayId) assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY)) @@ -72,6 +88,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() { @Test fun onStatusBarTouched_afterDisplayRemoved_goesBackToDefaultDisplay() = testScope.runTest { + val underTest = createUnderTest() val displayId by collectLastValue(underTest.displayId) displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL)) @@ -83,4 +100,40 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() { assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) } + + @Test + fun onStatusBarTouched_afterKeyguardVisible_goesBackToDefaultDisplay() = + testScope.runTest { + val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true) + val displayId by collectLastValue(underTest.displayId) + + displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL)) + underTest.onStatusBarTouched(2) + + assertThat(displayId).isEqualTo(2) + + keyguardRepository.setKeyguardShowing(true) + + assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) + } + + @Test + fun onStatusBarTouched_afterKeyguardHides_goesBackToPreviousDisplay() = + testScope.runTest { + val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true) + val displayId by collectLastValue(underTest.displayId) + + displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL)) + underTest.onStatusBarTouched(2) + + assertThat(displayId).isEqualTo(2) + + keyguardRepository.setKeyguardShowing(true) + + assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) + + keyguardRepository.setKeyguardShowing(false) + + assertThat(displayId).isEqualTo(2) + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt index ff39a3ddc17c..2bd7393925f4 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt @@ -48,6 +48,7 @@ import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap import javax.inject.Provider +import javax.inject.Qualifier /** * Module responsible for managing display-specific components and resources for the notification @@ -203,7 +204,9 @@ object ShadeDisplayAwareModule { @Provides @IntoMap @ClassKey(ShadePrimaryDisplayCommand::class) - fun provideShadePrimaryDisplayCommand(impl: Provider): CoreStartable { + fun provideShadePrimaryDisplayCommand( + impl: Provider + ): CoreStartable { return if (ShadeWindowGoesAround.isEnabled) { impl.get() } else { @@ -221,9 +224,23 @@ object ShadeDisplayAwareModule { CoreStartable.NOP } } + + @Provides + @ShadeOnDefaultDisplayWhenLocked + fun provideShadeOnDefaultDisplayWhenLocked(): Boolean = true } @Module internal interface OptionalShadeDisplayAwareBindings { @BindsOptionalOf fun bindOptionalOfWindowRootView(): WindowRootView } + +/** + * Annotates the boolean value that defines whether the shade window should go back to the default + * display when the keyguard is visible. + * + * As of today (Dec 2024), This is a configuration parameter provided in the dagger graph as the + * final policy around keyguard display is still under discussion, and will be evaluated based on + * how well this solution behaves from the performance point of view. + */ +@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class ShadeOnDefaultDisplayWhenLocked diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt index 22e9487af84c..30b086f03d66 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt @@ -22,34 +22,55 @@ import com.android.app.tracing.coroutines.launchTraced import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.repository.DisplayRepository +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.shade.ShadeOnDefaultDisplayWhenLocked import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn /** * Moves the shade on the last display that received a status bar touch. * - * If the display is removed, falls back to the default one. + * If the display is removed, falls back to the default one. When [shadeOnDefaultDisplayWhenLocked] + * is true, the shade falls back to the default display when the keyguard is visible. */ @SysUISingleton class StatusBarTouchShadeDisplayPolicy @Inject -constructor(displayRepository: DisplayRepository, @Background val backgroundScope: CoroutineScope) : - ShadeDisplayPolicy { - override val name: String - get() = "status_bar_latest_touch" +constructor( + displayRepository: DisplayRepository, + keyguardRepository: KeyguardRepository, + @Background val backgroundScope: CoroutineScope, + @ShadeOnDefaultDisplayWhenLocked val shadeOnDefaultDisplayWhenLocked: Boolean, +) : ShadeDisplayPolicy { + override val name: String = "status_bar_latest_touch" private val currentDisplayId = MutableStateFlow(Display.DEFAULT_DISPLAY) private val availableDisplayIds: StateFlow> = displayRepository.displayIds - override val displayId: StateFlow - get() = currentDisplayId + override val displayId: StateFlow = + if (shadeOnDefaultDisplayWhenLocked) { + keyguardRepository.isKeyguardShowing + .combine(currentDisplayId) { isKeyguardShowing, currentDisplayId -> + if (isKeyguardShowing) { + Display.DEFAULT_DISPLAY + } else { + currentDisplayId + } + } + .stateIn(backgroundScope, SharingStarted.WhileSubscribed(), currentDisplayId.value) + } else { + currentDisplayId + } private var removalListener: Job? = null -- cgit v1.2.3-59-g8ed1b