Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +9 −0 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,15 @@ public interface DesktopMode { void addVisibleTasksListener(DesktopRepository.VisibleTasksListener listener, void addVisibleTasksListener(DesktopRepository.VisibleTasksListener listener, Executor callbackExecutor); Executor callbackExecutor); /** * Adds a listener to find out about desk changes. * * @param listener the listener to add. * @param callbackExecutor the executor to call the listener on. */ void addDeskChangeListener(DesktopRepository.DeskChangeListener listener, Executor callbackExecutor); /** /** * Adds a consumer to listen for Desktop task corner changes. This is used for gesture * Adds a consumer to listen for Desktop task corner changes. This is used for gesture * exclusion. The SparseArray contains a list of four corner resize handles mapped to each * exclusion. The SparseArray contains a list of four corner resize handles mapped to each Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +19 −1 Original line number Original line Diff line number Diff line Loading @@ -5745,6 +5745,16 @@ class DesktopTasksController( userRepositories.current.addVisibleTasksListener(listener, callbackExecutor) userRepositories.current.addVisibleTasksListener(listener, callbackExecutor) } } /** * Adds a listener to find out about desk changes. * * @param listener the listener to add. * @param callbackExecutor the executor to call the listener on. */ fun addDeskChangeListener(listener: DeskChangeListener, callbackExecutor: Executor) { userRepositories.current.addDeskChangeListener(listener, callbackExecutor) } /** /** * Adds a listener to track changes to desktop task gesture exclusion regions * Adds a listener to track changes to desktop task gesture exclusion regions * * Loading Loading @@ -5821,7 +5831,6 @@ class DesktopTasksController( ) { ) { // Inherit parent's bounds. // Inherit parent's bounds. newWindowBounds.set(taskInfo.configuration.windowConfiguration.bounds) newWindowBounds.set(taskInfo.configuration.windowConfiguration.bounds) } else { } else { newWindowBounds.set(calculateDefaultDesktopTaskBounds(displayLayout)) newWindowBounds.set(calculateDefaultDesktopTaskBounds(displayLayout)) } } Loading Loading @@ -5988,6 +5997,15 @@ class DesktopTasksController( } } } } override fun addDeskChangeListener( listener: DeskChangeListener, callbackExecutor: Executor, ) { mainExecutor.execute { this@DesktopTasksController.addDeskChangeListener(listener, callbackExecutor) } } override fun addDesktopGestureExclusionRegionListener( override fun addDesktopGestureExclusionRegionListener( listener: Consumer<Region>, listener: Consumer<Region>, callbackExecutor: Executor, callbackExecutor: Executor, Loading packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Original line Diff line number Diff line Loading @@ -1332,6 +1332,16 @@ flag { } } } } flag { name: "shade_app_launch_animation_skip_in_desktop" namespace: "systemui" description: "Fixes the animation for notification shade entry points in Desktop Mode" bug: "383061244" metadata { purpose: PURPOSE_BUGFIX } } flag { flag { namespace: "systemui" namespace: "systemui" name: "enable_view_capture_tracing" name: "enable_view_capture_tracing" Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt +149 −0 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.widget.FrameLayout import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.app.displaylib.PerDisplayRepository import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.ActivityIntentHelper import com.android.systemui.ActivityIntentHelper import com.android.systemui.Flags import com.android.systemui.Flags Loading @@ -42,6 +43,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSettingsInteracto import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.testScope import com.android.systemui.model.SysUiState import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.settings.UserTracker import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeController import com.android.systemui.shade.ShadeController Loading @@ -49,6 +51,7 @@ import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.data.repository.ShadeAnimationRepository import com.android.systemui.shade.data.repository.ShadeAnimationRepository import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.NotificationShadeWindowController Loading Loading @@ -108,6 +111,8 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { @Mock private lateinit var activityIntentHelper: ActivityIntentHelper @Mock private lateinit var activityIntentHelper: ActivityIntentHelper @Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor @Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor @Mock private lateinit var communalSettingsInteractor: CommunalSettingsInteractor @Mock private lateinit var communalSettingsInteractor: CommunalSettingsInteractor @Mock private lateinit var perDisplaySysUiStateRepository: PerDisplayRepository<SysUiState> @Mock private lateinit var sysUIState: SysUiState private lateinit var underTest: LegacyActivityStarterInternalImpl private lateinit var underTest: LegacyActivityStarterInternalImpl private val kosmos = testKosmos() private val kosmos = testKosmos() private val mainExecutor = FakeExecutor(FakeSystemClock()) private val mainExecutor = FakeExecutor(FakeSystemClock()) Loading @@ -118,6 +123,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { fun setUp() { fun setUp() { MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this) `when`(statusBarWindowControllerStore.defaultDisplay).thenReturn(statusBarWindowController) `when`(statusBarWindowControllerStore.defaultDisplay).thenReturn(statusBarWindowController) `when`(perDisplaySysUiStateRepository[anyInt()]).thenReturn(sysUIState) underTest = underTest = LegacyActivityStarterInternalImpl( LegacyActivityStarterInternalImpl( centralSurfacesOptLazy = { Optional.of(centralSurfaces) }, centralSurfacesOptLazy = { Optional.of(centralSurfaces) }, Loading Loading @@ -145,6 +151,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { applicationScope = kosmos.testScope, applicationScope = kosmos.testScope, communalSceneInteractor = communalSceneInteractor, communalSceneInteractor = communalSceneInteractor, communalSettingsInteractor = communalSettingsInteractor, communalSettingsInteractor = communalSettingsInteractor, perDisplaySysUiStateRepository = perDisplaySysUiStateRepository, ) ) `when`(userTracker.userHandle).thenReturn(UserHandle.OWNER) `when`(userTracker.userHandle).thenReturn(UserHandle.OWNER) `when`(communalSceneInteractor.isCommunalVisible).thenReturn(MutableStateFlow(false)) `when`(communalSceneInteractor.isCommunalVisible).thenReturn(MutableStateFlow(false)) Loading Loading @@ -945,6 +952,148 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { ) ) } } @EnableFlags( Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION, Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP, ) @Test fun startActivity_skipAnimInDesktop_flagEnabled_inDesktop_noAnimate() { setupDesktopMode(enabled = true) val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard() underTest.startPendingIntentDismissingKeyguard( intent = pendingIntent, dismissShade = true, animationController = controller, showOverLockscreen = true, skipLockscreenChecks = true, ) mainExecutor.runAllReady() // Verify animate parameter is false verify(activityTransitionAnimator) .startPendingIntentWithAnimation( nullable(ActivityTransitionAnimator.Controller::class.java), eq(kosmos.testScope), /* animate */ eq(false), eq(false), eq(true), any(), ) } @EnableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION) @DisableFlags(Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) @Test fun startActivity_skipAnimInDesktop_flagDisabled_inDesktop_doAnimate() { setupDesktopMode(enabled = true) val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard() underTest.startPendingIntentDismissingKeyguard( intent = pendingIntent, dismissShade = true, animationController = controller, showOverLockscreen = true, skipLockscreenChecks = true, ) mainExecutor.runAllReady() // Verify animate parameter is true verify(activityTransitionAnimator) .startPendingIntentWithAnimation( nullable(ActivityTransitionAnimator.Controller::class.java), eq(kosmos.testScope), /* animate */ eq(true), eq(false), eq(true), any(), ) } @EnableFlags( Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION, Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP, ) @Test fun startActivity_skipAnimInDesktop_flagEnabled_notInDesktop_doAnimate() { setupDesktopMode(enabled = false) val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard() underTest.startPendingIntentDismissingKeyguard( intent = pendingIntent, dismissShade = true, animationController = controller, showOverLockscreen = true, skipLockscreenChecks = true, ) mainExecutor.runAllReady() // Verify animate parameter is true verify(activityTransitionAnimator) .startPendingIntentWithAnimation( nullable(ActivityTransitionAnimator.Controller::class.java), eq(kosmos.testScope), /* animate */ eq(true), eq(false), eq(true), any(), ) } @EnableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION) @DisableFlags(Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) @Test fun startActivity_skipAnimInDesktop_flagDisabled_notInDesktop_doAnimate() { setupDesktopMode(enabled = false) val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard() underTest.startPendingIntentDismissingKeyguard( intent = pendingIntent, dismissShade = true, animationController = controller, showOverLockscreen = true, skipLockscreenChecks = true, ) mainExecutor.runAllReady() // Verify animate parameter is true verify(activityTransitionAnimator) .startPendingIntentWithAnimation( nullable(ActivityTransitionAnimator.Controller::class.java), eq(kosmos.testScope), /* animate */ eq(true), eq(false), eq(true), any(), ) } private fun setupDesktopMode(enabled: Boolean) { val flags = if (enabled) { SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE } else { 0L } `when`(sysUIState.flags).thenReturn(flags) } private fun setupLaunchWithOccludedKeyguard(): Pair<ActivityTransitionAnimator.Controller?, PendingIntent> { val parent = FrameLayout(context) val view = object : View(context), LaunchableView { override fun setShouldBlockVisibilityChanges(block: Boolean) {} } parent.addView(view) val controller = ActivityTransitionAnimator.Controller.fromView(view) val pendingIntent = mock(PendingIntent::class.java) `when`(pendingIntent.isActivity).thenReturn(true) `when`(keyguardStateController.isShowing).thenReturn(true) `when`(keyguardStateController.isOccluded).thenReturn(true) return Pair(controller, pendingIntent) } private companion object { private companion object { private const val DISPLAY_ID = 0 private const val DISPLAY_ID = 0 } } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/WMShellTest.kt +127 −1 Original line number Original line Diff line number Diff line Loading @@ -19,16 +19,19 @@ import android.content.pm.UserInfo import android.graphics.Color import android.graphics.Color import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.EnableFlags import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.keyguard.keyguardUpdateMonitor import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND import com.android.systemui.Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel import com.android.systemui.communal.util.fakeCommunalColors import com.android.systemui.communal.util.fakeCommunalColors import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.display.data.repository.createPerDisplayInstanceSysUIStateRepository import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.ScreenLifecycle import com.android.systemui.keyguard.ScreenLifecycle Loading @@ -44,6 +47,7 @@ import com.android.systemui.model.sysUiState import com.android.systemui.notetask.NoteTaskInitializer import com.android.systemui.notetask.NoteTaskInitializer import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.userTracker import com.android.systemui.settings.userTracker import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.commandQueue import com.android.systemui.statusbar.commandQueue import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.statusbar.commandline.commandRegistry Loading @@ -53,6 +57,7 @@ import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.kotlin.javaAdapter import com.android.systemui.util.kotlin.javaAdapter import com.android.wm.shell.desktopmode.DesktopMode import com.android.wm.shell.desktopmode.DesktopMode import com.android.wm.shell.desktopmode.data.DesktopRepository import com.android.wm.shell.desktopmode.data.DesktopRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.data.DesktopRepository.VisibleTasksListener import com.android.wm.shell.onehanded.OneHanded import com.android.wm.shell.onehanded.OneHanded import com.android.wm.shell.onehanded.OneHandedEventCallback import com.android.wm.shell.onehanded.OneHandedEventCallback Loading @@ -61,12 +66,13 @@ import com.android.wm.shell.pip.Pip import com.android.wm.shell.recents.RecentTasks import com.android.wm.shell.recents.RecentTasks import com.android.wm.shell.splitscreen.SplitScreen import com.android.wm.shell.splitscreen.SplitScreen import com.android.wm.shell.sysui.ShellInterface import com.android.wm.shell.sysui.ShellInterface import com.google.common.truth.Truth.assertThat import java.util.Optional import java.util.Optional import java.util.concurrent.Executor import java.util.concurrent.Executor import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Before import org.junit.Test import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.never import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verify Loading @@ -91,6 +97,8 @@ class WMShellTest : SysuiTestCase() { private val Kosmos.screenLifecycle by Kosmos.Fixture { mock<ScreenLifecycle>() } private val Kosmos.screenLifecycle by Kosmos.Fixture { mock<ScreenLifecycle>() } private val Kosmos.displayTracker by Kosmos.Fixture { FakeDisplayTracker(context) } private val Kosmos.displayTracker by Kosmos.Fixture { FakeDisplayTracker(context) } private val Kosmos.shellInterface by Kosmos.Fixture { mock<ShellInterface>() } private val Kosmos.shellInterface by Kosmos.Fixture { mock<ShellInterface>() } private val Kosmos.perDisplayRepository by Kosmos.Fixture { createPerDisplayInstanceSysUIStateRepository() } private val Kosmos.underTest by private val Kosmos.underTest by Kosmos.Fixture { Kosmos.Fixture { Loading @@ -116,6 +124,7 @@ class WMShellTest : SysuiTestCase() { /* communalTransitionViewModel = */ communalTransitionViewModel, /* communalTransitionViewModel = */ communalTransitionViewModel, /* javaAdapter = */ javaAdapter, /* javaAdapter = */ javaAdapter, /* sysUiMainExecutor = */ fakeExecutor, /* sysUiMainExecutor = */ fakeExecutor, /* perDisplayRepository= */ perDisplayRepository, ) ) } } Loading Loading @@ -152,6 +161,123 @@ class WMShellTest : SysuiTestCase() { any(VisibleTasksListener::class.java), any(VisibleTasksListener::class.java), any(Executor::class.java), any(Executor::class.java), ) ) verify(desktopMode) .addDeskChangeListener( any(DesktopRepository.DeskChangeListener::class.java), any(Executor::class.java), ) } @Test @EnableFlags(FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) fun onActiveDeskChanged_enterDesktop_desktopStateIsActive() = kosmos.runTest { val displayId = Display.DEFAULT_DISPLAY val displaySysUiState = perDisplayRepository[displayId] underTest.initDesktopMode(desktopMode) val listenerCaptor = ArgumentCaptor.forClass(DesktopRepository.DeskChangeListener::class.java) verify(desktopMode) .addDeskChangeListener(listenerCaptor.capture(), any(Executor::class.java)) val listener = listenerCaptor.value displaySysUiState ?.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, false) ?.commitUpdate() listener.onActiveDeskChanged( displayId, newActiveDeskId = 1, oldActiveDeskId = DesktopRepository.INVALID_DESK_ID, ) fakeExecutor.runAllReady() assertThat( displaySysUiState?.isFlagEnabled(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) ) .isTrue() } @Test @EnableFlags(FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) fun onActiveDeskChanged_exitDesktop_desktopStateIsNotActive() = kosmos.runTest { val displayId = Display.DEFAULT_DISPLAY val displaySysUiState = perDisplayRepository[displayId] underTest.initDesktopMode(desktopMode) val listenerCaptor = ArgumentCaptor.forClass(DesktopRepository.DeskChangeListener::class.java) verify(desktopMode) .addDeskChangeListener(listenerCaptor.capture(), any(Executor::class.java)) val listener = listenerCaptor.value displaySysUiState ?.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, true) ?.commitUpdate() listener.onActiveDeskChanged( displayId, newActiveDeskId = DesktopRepository.INVALID_DESK_ID, oldActiveDeskId = 1, ) fakeExecutor.runAllReady() assertThat( displaySysUiState?.isFlagEnabled(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) ) .isFalse() } @Test @EnableFlags(FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) fun onActiveDeskChanged_stayInDesktop_desktopStateIsActive() = kosmos.runTest { val displayId = Display.DEFAULT_DISPLAY val displaySysUiState = perDisplayRepository[displayId] underTest.initDesktopMode(desktopMode) val listenerCaptor = ArgumentCaptor.forClass(DesktopRepository.DeskChangeListener::class.java) verify(desktopMode) .addDeskChangeListener(listenerCaptor.capture(), any(Executor::class.java)) val listener = listenerCaptor.value displaySysUiState ?.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, true) ?.commitUpdate() listener.onActiveDeskChanged(displayId, newActiveDeskId = 2, oldActiveDeskId = 1) fakeExecutor.runAllReady() assertThat( displaySysUiState?.isFlagEnabled(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) ) .isTrue() } @Test @EnableFlags(FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) fun onActiveDeskChanged_stayOutsideDesktop_desktopStateIsNotActive() = kosmos.runTest { val displayId = Display.DEFAULT_DISPLAY val displaySysUiState = perDisplayRepository[displayId] underTest.initDesktopMode(desktopMode) val listenerCaptor = ArgumentCaptor.forClass(DesktopRepository.DeskChangeListener::class.java) verify(desktopMode) .addDeskChangeListener(listenerCaptor.capture(), any(Executor::class.java)) val listener = listenerCaptor.value displaySysUiState ?.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, false) ?.commitUpdate() listener.onActiveDeskChanged( displayId, newActiveDeskId = DesktopRepository.INVALID_DESK_ID, oldActiveDeskId = DesktopRepository.INVALID_DESK_ID, ) fakeExecutor.runAllReady() assertThat( displaySysUiState?.isFlagEnabled(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) ) .isFalse() } } @Test @Test Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +9 −0 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,15 @@ public interface DesktopMode { void addVisibleTasksListener(DesktopRepository.VisibleTasksListener listener, void addVisibleTasksListener(DesktopRepository.VisibleTasksListener listener, Executor callbackExecutor); Executor callbackExecutor); /** * Adds a listener to find out about desk changes. * * @param listener the listener to add. * @param callbackExecutor the executor to call the listener on. */ void addDeskChangeListener(DesktopRepository.DeskChangeListener listener, Executor callbackExecutor); /** /** * Adds a consumer to listen for Desktop task corner changes. This is used for gesture * Adds a consumer to listen for Desktop task corner changes. This is used for gesture * exclusion. The SparseArray contains a list of four corner resize handles mapped to each * exclusion. The SparseArray contains a list of four corner resize handles mapped to each Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +19 −1 Original line number Original line Diff line number Diff line Loading @@ -5745,6 +5745,16 @@ class DesktopTasksController( userRepositories.current.addVisibleTasksListener(listener, callbackExecutor) userRepositories.current.addVisibleTasksListener(listener, callbackExecutor) } } /** * Adds a listener to find out about desk changes. * * @param listener the listener to add. * @param callbackExecutor the executor to call the listener on. */ fun addDeskChangeListener(listener: DeskChangeListener, callbackExecutor: Executor) { userRepositories.current.addDeskChangeListener(listener, callbackExecutor) } /** /** * Adds a listener to track changes to desktop task gesture exclusion regions * Adds a listener to track changes to desktop task gesture exclusion regions * * Loading Loading @@ -5821,7 +5831,6 @@ class DesktopTasksController( ) { ) { // Inherit parent's bounds. // Inherit parent's bounds. newWindowBounds.set(taskInfo.configuration.windowConfiguration.bounds) newWindowBounds.set(taskInfo.configuration.windowConfiguration.bounds) } else { } else { newWindowBounds.set(calculateDefaultDesktopTaskBounds(displayLayout)) newWindowBounds.set(calculateDefaultDesktopTaskBounds(displayLayout)) } } Loading Loading @@ -5988,6 +5997,15 @@ class DesktopTasksController( } } } } override fun addDeskChangeListener( listener: DeskChangeListener, callbackExecutor: Executor, ) { mainExecutor.execute { this@DesktopTasksController.addDeskChangeListener(listener, callbackExecutor) } } override fun addDesktopGestureExclusionRegionListener( override fun addDesktopGestureExclusionRegionListener( listener: Consumer<Region>, listener: Consumer<Region>, callbackExecutor: Executor, callbackExecutor: Executor, Loading
packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Original line Diff line number Diff line Loading @@ -1332,6 +1332,16 @@ flag { } } } } flag { name: "shade_app_launch_animation_skip_in_desktop" namespace: "systemui" description: "Fixes the animation for notification shade entry points in Desktop Mode" bug: "383061244" metadata { purpose: PURPOSE_BUGFIX } } flag { flag { namespace: "systemui" namespace: "systemui" name: "enable_view_capture_tracing" name: "enable_view_capture_tracing" Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt +149 −0 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.widget.FrameLayout import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.app.displaylib.PerDisplayRepository import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.ActivityIntentHelper import com.android.systemui.ActivityIntentHelper import com.android.systemui.Flags import com.android.systemui.Flags Loading @@ -42,6 +43,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSettingsInteracto import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.testScope import com.android.systemui.model.SysUiState import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.settings.UserTracker import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeController import com.android.systemui.shade.ShadeController Loading @@ -49,6 +51,7 @@ import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.data.repository.ShadeAnimationRepository import com.android.systemui.shade.data.repository.ShadeAnimationRepository import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.NotificationShadeWindowController Loading Loading @@ -108,6 +111,8 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { @Mock private lateinit var activityIntentHelper: ActivityIntentHelper @Mock private lateinit var activityIntentHelper: ActivityIntentHelper @Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor @Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor @Mock private lateinit var communalSettingsInteractor: CommunalSettingsInteractor @Mock private lateinit var communalSettingsInteractor: CommunalSettingsInteractor @Mock private lateinit var perDisplaySysUiStateRepository: PerDisplayRepository<SysUiState> @Mock private lateinit var sysUIState: SysUiState private lateinit var underTest: LegacyActivityStarterInternalImpl private lateinit var underTest: LegacyActivityStarterInternalImpl private val kosmos = testKosmos() private val kosmos = testKosmos() private val mainExecutor = FakeExecutor(FakeSystemClock()) private val mainExecutor = FakeExecutor(FakeSystemClock()) Loading @@ -118,6 +123,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { fun setUp() { fun setUp() { MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this) `when`(statusBarWindowControllerStore.defaultDisplay).thenReturn(statusBarWindowController) `when`(statusBarWindowControllerStore.defaultDisplay).thenReturn(statusBarWindowController) `when`(perDisplaySysUiStateRepository[anyInt()]).thenReturn(sysUIState) underTest = underTest = LegacyActivityStarterInternalImpl( LegacyActivityStarterInternalImpl( centralSurfacesOptLazy = { Optional.of(centralSurfaces) }, centralSurfacesOptLazy = { Optional.of(centralSurfaces) }, Loading Loading @@ -145,6 +151,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { applicationScope = kosmos.testScope, applicationScope = kosmos.testScope, communalSceneInteractor = communalSceneInteractor, communalSceneInteractor = communalSceneInteractor, communalSettingsInteractor = communalSettingsInteractor, communalSettingsInteractor = communalSettingsInteractor, perDisplaySysUiStateRepository = perDisplaySysUiStateRepository, ) ) `when`(userTracker.userHandle).thenReturn(UserHandle.OWNER) `when`(userTracker.userHandle).thenReturn(UserHandle.OWNER) `when`(communalSceneInteractor.isCommunalVisible).thenReturn(MutableStateFlow(false)) `when`(communalSceneInteractor.isCommunalVisible).thenReturn(MutableStateFlow(false)) Loading Loading @@ -945,6 +952,148 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { ) ) } } @EnableFlags( Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION, Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP, ) @Test fun startActivity_skipAnimInDesktop_flagEnabled_inDesktop_noAnimate() { setupDesktopMode(enabled = true) val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard() underTest.startPendingIntentDismissingKeyguard( intent = pendingIntent, dismissShade = true, animationController = controller, showOverLockscreen = true, skipLockscreenChecks = true, ) mainExecutor.runAllReady() // Verify animate parameter is false verify(activityTransitionAnimator) .startPendingIntentWithAnimation( nullable(ActivityTransitionAnimator.Controller::class.java), eq(kosmos.testScope), /* animate */ eq(false), eq(false), eq(true), any(), ) } @EnableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION) @DisableFlags(Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) @Test fun startActivity_skipAnimInDesktop_flagDisabled_inDesktop_doAnimate() { setupDesktopMode(enabled = true) val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard() underTest.startPendingIntentDismissingKeyguard( intent = pendingIntent, dismissShade = true, animationController = controller, showOverLockscreen = true, skipLockscreenChecks = true, ) mainExecutor.runAllReady() // Verify animate parameter is true verify(activityTransitionAnimator) .startPendingIntentWithAnimation( nullable(ActivityTransitionAnimator.Controller::class.java), eq(kosmos.testScope), /* animate */ eq(true), eq(false), eq(true), any(), ) } @EnableFlags( Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION, Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP, ) @Test fun startActivity_skipAnimInDesktop_flagEnabled_notInDesktop_doAnimate() { setupDesktopMode(enabled = false) val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard() underTest.startPendingIntentDismissingKeyguard( intent = pendingIntent, dismissShade = true, animationController = controller, showOverLockscreen = true, skipLockscreenChecks = true, ) mainExecutor.runAllReady() // Verify animate parameter is true verify(activityTransitionAnimator) .startPendingIntentWithAnimation( nullable(ActivityTransitionAnimator.Controller::class.java), eq(kosmos.testScope), /* animate */ eq(true), eq(false), eq(true), any(), ) } @EnableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION) @DisableFlags(Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) @Test fun startActivity_skipAnimInDesktop_flagDisabled_notInDesktop_doAnimate() { setupDesktopMode(enabled = false) val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard() underTest.startPendingIntentDismissingKeyguard( intent = pendingIntent, dismissShade = true, animationController = controller, showOverLockscreen = true, skipLockscreenChecks = true, ) mainExecutor.runAllReady() // Verify animate parameter is true verify(activityTransitionAnimator) .startPendingIntentWithAnimation( nullable(ActivityTransitionAnimator.Controller::class.java), eq(kosmos.testScope), /* animate */ eq(true), eq(false), eq(true), any(), ) } private fun setupDesktopMode(enabled: Boolean) { val flags = if (enabled) { SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE } else { 0L } `when`(sysUIState.flags).thenReturn(flags) } private fun setupLaunchWithOccludedKeyguard(): Pair<ActivityTransitionAnimator.Controller?, PendingIntent> { val parent = FrameLayout(context) val view = object : View(context), LaunchableView { override fun setShouldBlockVisibilityChanges(block: Boolean) {} } parent.addView(view) val controller = ActivityTransitionAnimator.Controller.fromView(view) val pendingIntent = mock(PendingIntent::class.java) `when`(pendingIntent.isActivity).thenReturn(true) `when`(keyguardStateController.isShowing).thenReturn(true) `when`(keyguardStateController.isOccluded).thenReturn(true) return Pair(controller, pendingIntent) } private companion object { private companion object { private const val DISPLAY_ID = 0 private const val DISPLAY_ID = 0 } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/WMShellTest.kt +127 −1 Original line number Original line Diff line number Diff line Loading @@ -19,16 +19,19 @@ import android.content.pm.UserInfo import android.graphics.Color import android.graphics.Color import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.EnableFlags import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.keyguard.keyguardUpdateMonitor import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.Flags.FLAG_COMMUNAL_HUB import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND import com.android.systemui.Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel import com.android.systemui.communal.util.fakeCommunalColors import com.android.systemui.communal.util.fakeCommunalColors import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.display.data.repository.createPerDisplayInstanceSysUIStateRepository import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.ScreenLifecycle import com.android.systemui.keyguard.ScreenLifecycle Loading @@ -44,6 +47,7 @@ import com.android.systemui.model.sysUiState import com.android.systemui.notetask.NoteTaskInitializer import com.android.systemui.notetask.NoteTaskInitializer import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.userTracker import com.android.systemui.settings.userTracker import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.commandQueue import com.android.systemui.statusbar.commandQueue import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.statusbar.commandline.commandRegistry Loading @@ -53,6 +57,7 @@ import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.kotlin.javaAdapter import com.android.systemui.util.kotlin.javaAdapter import com.android.wm.shell.desktopmode.DesktopMode import com.android.wm.shell.desktopmode.DesktopMode import com.android.wm.shell.desktopmode.data.DesktopRepository import com.android.wm.shell.desktopmode.data.DesktopRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.data.DesktopRepository.VisibleTasksListener import com.android.wm.shell.onehanded.OneHanded import com.android.wm.shell.onehanded.OneHanded import com.android.wm.shell.onehanded.OneHandedEventCallback import com.android.wm.shell.onehanded.OneHandedEventCallback Loading @@ -61,12 +66,13 @@ import com.android.wm.shell.pip.Pip import com.android.wm.shell.recents.RecentTasks import com.android.wm.shell.recents.RecentTasks import com.android.wm.shell.splitscreen.SplitScreen import com.android.wm.shell.splitscreen.SplitScreen import com.android.wm.shell.sysui.ShellInterface import com.android.wm.shell.sysui.ShellInterface import com.google.common.truth.Truth.assertThat import java.util.Optional import java.util.Optional import java.util.concurrent.Executor import java.util.concurrent.Executor import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Before import org.junit.Test import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.never import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verify Loading @@ -91,6 +97,8 @@ class WMShellTest : SysuiTestCase() { private val Kosmos.screenLifecycle by Kosmos.Fixture { mock<ScreenLifecycle>() } private val Kosmos.screenLifecycle by Kosmos.Fixture { mock<ScreenLifecycle>() } private val Kosmos.displayTracker by Kosmos.Fixture { FakeDisplayTracker(context) } private val Kosmos.displayTracker by Kosmos.Fixture { FakeDisplayTracker(context) } private val Kosmos.shellInterface by Kosmos.Fixture { mock<ShellInterface>() } private val Kosmos.shellInterface by Kosmos.Fixture { mock<ShellInterface>() } private val Kosmos.perDisplayRepository by Kosmos.Fixture { createPerDisplayInstanceSysUIStateRepository() } private val Kosmos.underTest by private val Kosmos.underTest by Kosmos.Fixture { Kosmos.Fixture { Loading @@ -116,6 +124,7 @@ class WMShellTest : SysuiTestCase() { /* communalTransitionViewModel = */ communalTransitionViewModel, /* communalTransitionViewModel = */ communalTransitionViewModel, /* javaAdapter = */ javaAdapter, /* javaAdapter = */ javaAdapter, /* sysUiMainExecutor = */ fakeExecutor, /* sysUiMainExecutor = */ fakeExecutor, /* perDisplayRepository= */ perDisplayRepository, ) ) } } Loading Loading @@ -152,6 +161,123 @@ class WMShellTest : SysuiTestCase() { any(VisibleTasksListener::class.java), any(VisibleTasksListener::class.java), any(Executor::class.java), any(Executor::class.java), ) ) verify(desktopMode) .addDeskChangeListener( any(DesktopRepository.DeskChangeListener::class.java), any(Executor::class.java), ) } @Test @EnableFlags(FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) fun onActiveDeskChanged_enterDesktop_desktopStateIsActive() = kosmos.runTest { val displayId = Display.DEFAULT_DISPLAY val displaySysUiState = perDisplayRepository[displayId] underTest.initDesktopMode(desktopMode) val listenerCaptor = ArgumentCaptor.forClass(DesktopRepository.DeskChangeListener::class.java) verify(desktopMode) .addDeskChangeListener(listenerCaptor.capture(), any(Executor::class.java)) val listener = listenerCaptor.value displaySysUiState ?.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, false) ?.commitUpdate() listener.onActiveDeskChanged( displayId, newActiveDeskId = 1, oldActiveDeskId = DesktopRepository.INVALID_DESK_ID, ) fakeExecutor.runAllReady() assertThat( displaySysUiState?.isFlagEnabled(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) ) .isTrue() } @Test @EnableFlags(FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) fun onActiveDeskChanged_exitDesktop_desktopStateIsNotActive() = kosmos.runTest { val displayId = Display.DEFAULT_DISPLAY val displaySysUiState = perDisplayRepository[displayId] underTest.initDesktopMode(desktopMode) val listenerCaptor = ArgumentCaptor.forClass(DesktopRepository.DeskChangeListener::class.java) verify(desktopMode) .addDeskChangeListener(listenerCaptor.capture(), any(Executor::class.java)) val listener = listenerCaptor.value displaySysUiState ?.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, true) ?.commitUpdate() listener.onActiveDeskChanged( displayId, newActiveDeskId = DesktopRepository.INVALID_DESK_ID, oldActiveDeskId = 1, ) fakeExecutor.runAllReady() assertThat( displaySysUiState?.isFlagEnabled(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) ) .isFalse() } @Test @EnableFlags(FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) fun onActiveDeskChanged_stayInDesktop_desktopStateIsActive() = kosmos.runTest { val displayId = Display.DEFAULT_DISPLAY val displaySysUiState = perDisplayRepository[displayId] underTest.initDesktopMode(desktopMode) val listenerCaptor = ArgumentCaptor.forClass(DesktopRepository.DeskChangeListener::class.java) verify(desktopMode) .addDeskChangeListener(listenerCaptor.capture(), any(Executor::class.java)) val listener = listenerCaptor.value displaySysUiState ?.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, true) ?.commitUpdate() listener.onActiveDeskChanged(displayId, newActiveDeskId = 2, oldActiveDeskId = 1) fakeExecutor.runAllReady() assertThat( displaySysUiState?.isFlagEnabled(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) ) .isTrue() } @Test @EnableFlags(FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP) fun onActiveDeskChanged_stayOutsideDesktop_desktopStateIsNotActive() = kosmos.runTest { val displayId = Display.DEFAULT_DISPLAY val displaySysUiState = perDisplayRepository[displayId] underTest.initDesktopMode(desktopMode) val listenerCaptor = ArgumentCaptor.forClass(DesktopRepository.DeskChangeListener::class.java) verify(desktopMode) .addDeskChangeListener(listenerCaptor.capture(), any(Executor::class.java)) val listener = listenerCaptor.value displaySysUiState ?.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, false) ?.commitUpdate() listener.onActiveDeskChanged( displayId, newActiveDeskId = DesktopRepository.INVALID_DESK_ID, oldActiveDeskId = DesktopRepository.INVALID_DESK_ID, ) fakeExecutor.runAllReady() assertThat( displaySysUiState?.isFlagEnabled(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) ) .isFalse() } } @Test @Test Loading