Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt +54 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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)) Loading @@ -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)) Loading @@ -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)) Loading @@ -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) } } packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +15 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,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 Loading Loading @@ -237,9 +238,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 packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt +28 −7 Original line number Diff line number Diff line Loading @@ -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<Set<Int>> = displayRepository.displayIds override val displayId: StateFlow<Int> get() = currentDisplayId override val displayId: StateFlow<Int> = 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 Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt +54 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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)) Loading @@ -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)) Loading @@ -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)) Loading @@ -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) } }
packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +15 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,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 Loading Loading @@ -237,9 +238,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
packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt +28 −7 Original line number Diff line number Diff line Loading @@ -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<Set<Int>> = displayRepository.displayIds override val displayId: StateFlow<Int> get() = currentDisplayId override val displayId: StateFlow<Int> = 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 Loading