Loading packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +18 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest import com.android.compose.animation.scene.DefaultEdgeDetector import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.fakeFalsingManager import com.android.systemui.desktop.domain.interactor.enableDesktopFeatureSet import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository Loading Loading @@ -430,4 +431,21 @@ class SceneContainerViewModelTest : SysuiTestCase() { assertThat(underTest.swipeSourceDetector) .isInstanceOf(SceneContainerSwipeDetector::class.java) } @Test fun onEmptySpaceMotionEvent_hidesDualShadeOverlays_onDesktopMode() = kosmos.runTest { // GIVEN a device in desktop mode with dual shade enabled and an overlay present val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) enableDualShade() enableDesktopFeatureSet() sceneInteractor.showOverlay(Overlays.QuickSettingsShade, "test") assertThat(currentOverlays).isNotEmpty() // WHEN a touch event occurs outside the shade window underTest.onEmptySpaceMotionEvent(MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)) // THEN the overlay is hidden assertThat(currentOverlays).isEmpty() } } packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerTest.kt +128 −47 Original line number Diff line number Diff line Loading @@ -16,65 +16,81 @@ package com.android.systemui.statusbar.phone import android.content.res.Configuration import android.content.testableContext import android.graphics.Rect import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.ObservableTransitionState.Idle import com.android.compose.animation.scene.OverlayKey import com.android.internal.policy.SystemBarUtils import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 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.res.R import com.android.systemui.scene.data.repository.sceneContainerRepository 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.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.domain.interactor.shadeMode import com.android.systemui.shade.notificationShadeWindowView import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.testKosmos import com.android.systemui.util.kotlin.getValue import com.google.common.truth.Truth.assertThat import dagger.Lazy import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class ShadeTouchableRegionManagerTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val sceneRepository = kosmos.sceneContainerRepository private val underTest by Lazy { kosmos.shadeTouchableRegionManager } private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val Kosmos.underTest by Kosmos.Fixture { kosmos.shadeTouchableRegionManager } @Before fun setUp() { kosmos.notificationShadeWindowView.apply { whenever(width).thenReturn(1000) whenever(height).thenReturn(1000) } kosmos.underTest.setup(kosmos.notificationShadeWindowView) } @Test @EnableSceneContainer fun entireScreenTouchable_sceneContainerEnabled_isRemoteUserInteractionOngoing() = testScope.runTest { sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Gone)) ) runCurrent() kosmos.runTest { sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Gone))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() sceneRepository.isRemoteUserInputOngoing.value = true runCurrent() sceneContainerRepository.isRemoteUserInputOngoing.value = true assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue() sceneRepository.isRemoteUserInputOngoing.value = false runCurrent() sceneContainerRepository.isRemoteUserInputOngoing.value = false assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } @Test @DisableSceneContainer fun entireScreenTouchable_sceneContainerDisabled_isRemoteUserInteractionOngoing() = testScope.runTest { kosmos.runTest { assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() sceneRepository.isRemoteUserInputOngoing.value = true runCurrent() sceneContainerRepository.isRemoteUserInputOngoing.value = true assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } Loading @@ -82,36 +98,24 @@ class ShadeTouchableRegionManagerTest : SysuiTestCase() { @Test @EnableSceneContainer fun entireScreenTouchable_sceneContainerEnabled_isIdleOnGone() = testScope.runTest { sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Gone)) ) runCurrent() kosmos.runTest { sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Gone))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Shade)) ) runCurrent() sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Shade))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue() sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Gone)) ) runCurrent() sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Gone))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } @Test @DisableSceneContainer fun entireScreenTouchable_sceneContainerDisabled_isIdleOnGone() = testScope.runTest { kosmos.runTest { assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Shade)) ) runCurrent() sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Shade))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } Loading @@ -119,17 +123,94 @@ class ShadeTouchableRegionManagerTest : SysuiTestCase() { @Test @DisableSceneContainer fun entireScreenTouchable_communalVisible() = testScope.runTest { kosmos.runTest { assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() kosmos.fakeCommunalSceneRepository.instantlyTransitionTo(CommunalScenes.Communal) runCurrent() assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue() kosmos.fakeCommunalSceneRepository.instantlyTransitionTo(CommunalScenes.Blank) runCurrent() assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } @Test @EnableSceneContainer fun entireScreenTouchable_desktopMode() = kosmos.runTest { enableStatusBarForDesktop() openShadeOverlay(Overlays.QuickSettingsShade) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } @Test @EnableSceneContainer fun calculateTouchableRegionForDesktop_sceneGone_withShadeBounds() = kosmos.runTest { val bounds = Rect(0, 0, 100, 100) enableStatusBarForDesktop() lockDevice() unlockDevice() openShadeOverlay(Overlays.QuickSettingsShade) shadeInteractor.setShadeOverlayBounds(bounds) val rects = underTest.calculateTouchableRegionForDesktop() assertThat(rects).containsExactly(bounds) } @Test @EnableSceneContainer fun calculateTouchableRegionForDesktop_sceneVisible_withoutShadeBounds() = kosmos.runTest { enableStatusBarForDesktop() lockDevice() shadeInteractor.setShadeOverlayBounds(null) val statusBarHeight = SystemBarUtils.getStatusBarHeight(mContext) val expectedRect = Rect(0, statusBarHeight, 1000, 1000) val rects = underTest.calculateTouchableRegionForDesktop() assertThat(rects).containsExactly(expectedRect) } private fun Kosmos.openShadeOverlay(overlay: OverlayKey) { val shadeMode by collectLastValue(shadeMode) val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val initialScene = checkNotNull(currentScene) assertThat(shadeMode).isEqualTo(ShadeMode.Dual) sceneInteractor.showOverlay(overlay, "test") setSceneTransition(Idle(initialScene, checkNotNull(currentOverlays))) assertThat(currentScene).isEqualTo(initialScene) assertThat(currentOverlays).contains(overlay) } private fun Kosmos.closeShadeOverlay(overlay: OverlayKey) { sceneInteractor.hideOverlay(overlay, "test") } private fun Kosmos.enableStatusBarForDesktop() { enableDualShade() testableContext.orCreateTestableResources.addOverride( R.bool.config_enableDesktopFeatureSet, true, ) configurationController.onConfigurationChanged(Configuration()) } private fun Kosmos.unlockDevice() { fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) sceneInteractor.changeScene(Scenes.Gone, "unlock") sceneContainerRepository.setTransitionState(flowOf(Idle(Scenes.Gone))) } private fun Kosmos.lockDevice() { sceneContainerRepository.instantlyTransitionTo(Scenes.Lockscreen) } } packages/SystemUI/src/com/android/systemui/desktop/domain/interactor/DesktopInteractor.kt +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ constructor( configurationController: ConfigurationController, ) { // TODO(441100057): This StateFlow should support Connected Displays. /** Whether the desktop feature set is enabled. */ val isDesktopFeatureSetEnabled: StateFlow<Boolean> = configurationController.onConfigChanged Loading packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +28 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.desktop.domain.interactor.DesktopInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel Loading @@ -40,7 +41,6 @@ import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.qs.panels.ui.viewmodel.AnimateQsTilesViewModel import com.android.systemui.statusbar.notification.domain.interactor.NotificationContainerInteractor import com.android.systemui.scene.domain.interactor.OnBootTransitionInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.logger.SceneLogger Loading @@ -49,7 +49,9 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.domain.interactor.ShadeModeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.core.StatusBarForDesktop import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor import com.android.systemui.statusbar.notification.domain.interactor.NotificationContainerInteractor import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel import dagger.assisted.Assisted Loading @@ -66,12 +68,13 @@ class SceneContainerViewModel @AssistedInject constructor( private val sceneInteractor: SceneInteractor, private val desktopInteractor: DesktopInteractor, private val deviceUnlockedInteractor: DeviceUnlockedInteractor, private val falsingInteractor: FalsingInteractor, private val powerInteractor: PowerInteractor, private val onBootTransitionInteractor: OnBootTransitionInteractor, private val shadeModeInteractor: ShadeModeInteractor, private val notificationContainerInteractor: NotificationContainerInteractor, shadeModeInteractor: ShadeModeInteractor, private val remoteInputInteractor: RemoteInputInteractor, private val logger: SceneLogger, hapticsViewModelFactory: SceneContainerHapticsViewModel.Factory, Loading Loading @@ -124,6 +127,16 @@ constructor( initialValue = 1f, ) private val isDesktopStatusBarEnabled by hydrator.hydratedStateOf( traceName = "isDesktopStatusBarEnabled", source = desktopInteractor.isDesktopFeatureSetEnabled.map { enabled -> enabled && StatusBarForDesktop.isEnabled }, initialValue = false, ) override suspend fun onActivated(): Nothing { try { // Sends a MotionEventHandler to the owner of the view-model so they can report Loading Loading @@ -193,6 +206,19 @@ constructor( * Call this after the [MotionEvent] has finished propagating through the UI hierarchy. */ fun onEmptySpaceMotionEvent(event: MotionEvent) { // Hide dual shade overlays when there is a touch outside the shade window. // This is only applicable when the desktop status bar is enabled. if ( shadeModeInteractor.isDualShade && isDesktopStatusBarEnabled && event.action == MotionEvent.ACTION_OUTSIDE && sceneInteractor.currentOverlays.value.isNotEmpty() ) { sceneInteractor.currentOverlays.value.forEach { sceneInteractor.hideOverlay(it, "Empty space touch") } } // check if the touch is outside the window and if remote input is active. // If true, close any active remote inputs. if ( Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManager.java +172 −28 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +18 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest import com.android.compose.animation.scene.DefaultEdgeDetector import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.fakeFalsingManager import com.android.systemui.desktop.domain.interactor.enableDesktopFeatureSet import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository Loading Loading @@ -430,4 +431,21 @@ class SceneContainerViewModelTest : SysuiTestCase() { assertThat(underTest.swipeSourceDetector) .isInstanceOf(SceneContainerSwipeDetector::class.java) } @Test fun onEmptySpaceMotionEvent_hidesDualShadeOverlays_onDesktopMode() = kosmos.runTest { // GIVEN a device in desktop mode with dual shade enabled and an overlay present val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) enableDualShade() enableDesktopFeatureSet() sceneInteractor.showOverlay(Overlays.QuickSettingsShade, "test") assertThat(currentOverlays).isNotEmpty() // WHEN a touch event occurs outside the shade window underTest.onEmptySpaceMotionEvent(MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)) // THEN the overlay is hidden assertThat(currentOverlays).isEmpty() } }
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerTest.kt +128 −47 Original line number Diff line number Diff line Loading @@ -16,65 +16,81 @@ package com.android.systemui.statusbar.phone import android.content.res.Configuration import android.content.testableContext import android.graphics.Rect import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.ObservableTransitionState.Idle import com.android.compose.animation.scene.OverlayKey import com.android.internal.policy.SystemBarUtils import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 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.res.R import com.android.systemui.scene.data.repository.sceneContainerRepository 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.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.domain.interactor.shadeMode import com.android.systemui.shade.notificationShadeWindowView import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.testKosmos import com.android.systemui.util.kotlin.getValue import com.google.common.truth.Truth.assertThat import dagger.Lazy import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class ShadeTouchableRegionManagerTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val sceneRepository = kosmos.sceneContainerRepository private val underTest by Lazy { kosmos.shadeTouchableRegionManager } private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val Kosmos.underTest by Kosmos.Fixture { kosmos.shadeTouchableRegionManager } @Before fun setUp() { kosmos.notificationShadeWindowView.apply { whenever(width).thenReturn(1000) whenever(height).thenReturn(1000) } kosmos.underTest.setup(kosmos.notificationShadeWindowView) } @Test @EnableSceneContainer fun entireScreenTouchable_sceneContainerEnabled_isRemoteUserInteractionOngoing() = testScope.runTest { sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Gone)) ) runCurrent() kosmos.runTest { sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Gone))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() sceneRepository.isRemoteUserInputOngoing.value = true runCurrent() sceneContainerRepository.isRemoteUserInputOngoing.value = true assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue() sceneRepository.isRemoteUserInputOngoing.value = false runCurrent() sceneContainerRepository.isRemoteUserInputOngoing.value = false assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } @Test @DisableSceneContainer fun entireScreenTouchable_sceneContainerDisabled_isRemoteUserInteractionOngoing() = testScope.runTest { kosmos.runTest { assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() sceneRepository.isRemoteUserInputOngoing.value = true runCurrent() sceneContainerRepository.isRemoteUserInputOngoing.value = true assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } Loading @@ -82,36 +98,24 @@ class ShadeTouchableRegionManagerTest : SysuiTestCase() { @Test @EnableSceneContainer fun entireScreenTouchable_sceneContainerEnabled_isIdleOnGone() = testScope.runTest { sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Gone)) ) runCurrent() kosmos.runTest { sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Gone))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Shade)) ) runCurrent() sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Shade))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue() sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Gone)) ) runCurrent() sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Gone))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } @Test @DisableSceneContainer fun entireScreenTouchable_sceneContainerDisabled_isIdleOnGone() = testScope.runTest { kosmos.runTest { assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() sceneRepository.setTransitionState( flowOf(ObservableTransitionState.Idle(currentScene = Scenes.Shade)) ) runCurrent() sceneContainerRepository.setTransitionState(flowOf(Idle(currentScene = Scenes.Shade))) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } Loading @@ -119,17 +123,94 @@ class ShadeTouchableRegionManagerTest : SysuiTestCase() { @Test @DisableSceneContainer fun entireScreenTouchable_communalVisible() = testScope.runTest { kosmos.runTest { assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() kosmos.fakeCommunalSceneRepository.instantlyTransitionTo(CommunalScenes.Communal) runCurrent() assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue() kosmos.fakeCommunalSceneRepository.instantlyTransitionTo(CommunalScenes.Blank) runCurrent() assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } @Test @EnableSceneContainer fun entireScreenTouchable_desktopMode() = kosmos.runTest { enableStatusBarForDesktop() openShadeOverlay(Overlays.QuickSettingsShade) assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse() } @Test @EnableSceneContainer fun calculateTouchableRegionForDesktop_sceneGone_withShadeBounds() = kosmos.runTest { val bounds = Rect(0, 0, 100, 100) enableStatusBarForDesktop() lockDevice() unlockDevice() openShadeOverlay(Overlays.QuickSettingsShade) shadeInteractor.setShadeOverlayBounds(bounds) val rects = underTest.calculateTouchableRegionForDesktop() assertThat(rects).containsExactly(bounds) } @Test @EnableSceneContainer fun calculateTouchableRegionForDesktop_sceneVisible_withoutShadeBounds() = kosmos.runTest { enableStatusBarForDesktop() lockDevice() shadeInteractor.setShadeOverlayBounds(null) val statusBarHeight = SystemBarUtils.getStatusBarHeight(mContext) val expectedRect = Rect(0, statusBarHeight, 1000, 1000) val rects = underTest.calculateTouchableRegionForDesktop() assertThat(rects).containsExactly(expectedRect) } private fun Kosmos.openShadeOverlay(overlay: OverlayKey) { val shadeMode by collectLastValue(shadeMode) val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val initialScene = checkNotNull(currentScene) assertThat(shadeMode).isEqualTo(ShadeMode.Dual) sceneInteractor.showOverlay(overlay, "test") setSceneTransition(Idle(initialScene, checkNotNull(currentOverlays))) assertThat(currentScene).isEqualTo(initialScene) assertThat(currentOverlays).contains(overlay) } private fun Kosmos.closeShadeOverlay(overlay: OverlayKey) { sceneInteractor.hideOverlay(overlay, "test") } private fun Kosmos.enableStatusBarForDesktop() { enableDualShade() testableContext.orCreateTestableResources.addOverride( R.bool.config_enableDesktopFeatureSet, true, ) configurationController.onConfigurationChanged(Configuration()) } private fun Kosmos.unlockDevice() { fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) sceneInteractor.changeScene(Scenes.Gone, "unlock") sceneContainerRepository.setTransitionState(flowOf(Idle(Scenes.Gone))) } private fun Kosmos.lockDevice() { sceneContainerRepository.instantlyTransitionTo(Scenes.Lockscreen) } }
packages/SystemUI/src/com/android/systemui/desktop/domain/interactor/DesktopInteractor.kt +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ constructor( configurationController: ConfigurationController, ) { // TODO(441100057): This StateFlow should support Connected Displays. /** Whether the desktop feature set is enabled. */ val isDesktopFeatureSetEnabled: StateFlow<Boolean> = configurationController.onConfigChanged Loading
packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +28 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.desktop.domain.interactor.DesktopInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel Loading @@ -40,7 +41,6 @@ import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.qs.panels.ui.viewmodel.AnimateQsTilesViewModel import com.android.systemui.statusbar.notification.domain.interactor.NotificationContainerInteractor import com.android.systemui.scene.domain.interactor.OnBootTransitionInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.logger.SceneLogger Loading @@ -49,7 +49,9 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.domain.interactor.ShadeModeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.core.StatusBarForDesktop import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor import com.android.systemui.statusbar.notification.domain.interactor.NotificationContainerInteractor import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel import dagger.assisted.Assisted Loading @@ -66,12 +68,13 @@ class SceneContainerViewModel @AssistedInject constructor( private val sceneInteractor: SceneInteractor, private val desktopInteractor: DesktopInteractor, private val deviceUnlockedInteractor: DeviceUnlockedInteractor, private val falsingInteractor: FalsingInteractor, private val powerInteractor: PowerInteractor, private val onBootTransitionInteractor: OnBootTransitionInteractor, private val shadeModeInteractor: ShadeModeInteractor, private val notificationContainerInteractor: NotificationContainerInteractor, shadeModeInteractor: ShadeModeInteractor, private val remoteInputInteractor: RemoteInputInteractor, private val logger: SceneLogger, hapticsViewModelFactory: SceneContainerHapticsViewModel.Factory, Loading Loading @@ -124,6 +127,16 @@ constructor( initialValue = 1f, ) private val isDesktopStatusBarEnabled by hydrator.hydratedStateOf( traceName = "isDesktopStatusBarEnabled", source = desktopInteractor.isDesktopFeatureSetEnabled.map { enabled -> enabled && StatusBarForDesktop.isEnabled }, initialValue = false, ) override suspend fun onActivated(): Nothing { try { // Sends a MotionEventHandler to the owner of the view-model so they can report Loading Loading @@ -193,6 +206,19 @@ constructor( * Call this after the [MotionEvent] has finished propagating through the UI hierarchy. */ fun onEmptySpaceMotionEvent(event: MotionEvent) { // Hide dual shade overlays when there is a touch outside the shade window. // This is only applicable when the desktop status bar is enabled. if ( shadeModeInteractor.isDualShade && isDesktopStatusBarEnabled && event.action == MotionEvent.ACTION_OUTSIDE && sceneInteractor.currentOverlays.value.isNotEmpty() ) { sceneInteractor.currentOverlays.value.forEach { sceneInteractor.hideOverlay(it, "Empty space touch") } } // check if the touch is outside the window and if remote input is active. // If true, close any active remote inputs. if ( Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManager.java +172 −28 File changed.Preview size limit exceeded, changes collapsed. Show changes