Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -1474,6 +1474,7 @@ public abstract class WMShellModule { ShellTaskOrganizer shellTaskOrganizer, ShellTaskOrganizer shellTaskOrganizer, DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider, DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider, InputManager inputManager, InputManager inputManager, DisplayController displayController, @ShellMainThread Handler mainHandler @ShellMainThread Handler mainHandler ) { ) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { Loading @@ -1488,6 +1489,7 @@ public abstract class WMShellModule { shellTaskOrganizer, shellTaskOrganizer, desktopWallpaperActivityTokenProvider, desktopWallpaperActivityTokenProvider, inputManager, inputManager, displayController, mainHandler)); mainHandler)); } } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt +17 −3 Original line number Original line Diff line number Diff line Loading @@ -35,9 +35,11 @@ import com.android.internal.annotations.VisibleForTesting import com.android.internal.protolog.ProtoLog import com.android.internal.protolog.ProtoLog import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions /** Controls the display windowing mode in desktop mode */ /** Controls the display windowing mode in desktop mode */ Loading @@ -49,6 +51,7 @@ class DesktopDisplayModeController( private val shellTaskOrganizer: ShellTaskOrganizer, private val shellTaskOrganizer: ShellTaskOrganizer, private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider, private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider, private val inputManager: InputManager, private val inputManager: InputManager, private val displayController: DisplayController, @ShellMainThread private val mainHandler: Handler, @ShellMainThread private val mainHandler: Handler, ) { ) { Loading Loading @@ -128,14 +131,25 @@ class DesktopDisplayModeController( return windowManager.getWindowingMode(DEFAULT_DISPLAY) return windowManager.getWindowingMode(DEFAULT_DISPLAY) } } // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available. private fun isExtendedDisplayEnabled(): Boolean { private fun isExtendedDisplayEnabled() = if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue) { 0 != return rootTaskDisplayAreaOrganizer .getDisplayIds() .filter { it != DEFAULT_DISPLAY } .any { displayId -> displayController.getDisplay(displayId)?.let { display -> DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display) } ?: false } } return 0 != Settings.Global.getInt( Settings.Global.getInt( context.contentResolver, context.contentResolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0, 0, ) ) } private fun hasExternalDisplay() = private fun hasExternalDisplay() = rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY } rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt +98 −69 Original line number Original line Diff line number Diff line Loading @@ -26,28 +26,37 @@ import android.os.Binder import android.os.Handler import android.os.Handler 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.platform.test.annotations.UsesFlags import android.platform.test.flag.junit.FlagsParameterization import android.platform.test.flag.junit.FlagsParameterization import android.provider.Settings import android.provider.Settings import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS import android.view.Display import android.view.Display.DEFAULT_DISPLAY import android.view.Display.DEFAULT_DISPLAY import android.view.IWindowManager import android.view.IWindowManager import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_CHANGE import android.window.DisplayAreaInfo import android.window.DisplayAreaInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.ExtendedMockito.never import com.android.dx.mockito.inline.extended.ExtendedMockito.never import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.server.display.feature.flags.Flags as DisplayFlags import com.android.window.flags.Flags import com.android.window.flags.Flags import com.android.wm.shell.MockToken import com.android.wm.shell.MockToken import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.common.DisplayController import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import com.google.testing.junit.testparameterinjector.TestParameter import com.google.testing.junit.testparameterinjector.TestParameter import com.google.testing.junit.testparameterinjector.TestParameterInjector import com.google.testing.junit.testparameterinjector.TestParameterInjector import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider import org.junit.After 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 Loading @@ -60,6 +69,7 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.eq import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import org.mockito.kotlin.whenever import org.mockito.quality.Strictness /** /** * Test class for [DesktopDisplayModeController] * Test class for [DesktopDisplayModeController] Loading @@ -68,6 +78,7 @@ import org.mockito.kotlin.whenever */ */ @SmallTest @SmallTest @RunWith(TestParameterInjector::class) @RunWith(TestParameterInjector::class) @UsesFlags(com.android.server.display.feature.flags.Flags::class) class DesktopDisplayModeControllerTest( class DesktopDisplayModeControllerTest( @TestParameter(valuesProvider = FlagsParameterizationProvider::class) @TestParameter(valuesProvider = FlagsParameterizationProvider::class) flags: FlagsParameterization flags: FlagsParameterization Loading @@ -79,6 +90,7 @@ class DesktopDisplayModeControllerTest( private val desktopWallpaperActivityTokenProvider = private val desktopWallpaperActivityTokenProvider = mock<DesktopWallpaperActivityTokenProvider>() mock<DesktopWallpaperActivityTokenProvider>() private val inputManager = mock<InputManager>() private val inputManager = mock<InputManager>() private val displayController = mock<DisplayController>() private val mainHandler = mock<Handler>() private val mainHandler = mock<Handler>() private lateinit var controller: DesktopDisplayModeController private lateinit var controller: DesktopDisplayModeController Loading @@ -90,6 +102,12 @@ class DesktopDisplayModeControllerTest( TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build() TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build() private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) private val wallpaperToken = MockToken().token() private val wallpaperToken = MockToken().token() private val externalDisplay = mock<Display>() private lateinit var extendedDisplaySettingsRestoreSession: ExtendedDisplaySettingsRestoreSession private lateinit var mockitoSession: StaticMockitoSession init { init { mSetFlagsRule.setFlagsParameterization(flags) mSetFlagsRule.setFlagsParameterization(flags) Loading @@ -97,6 +115,13 @@ class DesktopDisplayModeControllerTest( @Before @Before fun setUp() { fun setUp() { mockitoSession = mockitoSession() .strictness(Strictness.LENIENT) .spyStatic(DesktopModeStatus::class.java) .startMocking() extendedDisplaySettingsRestoreSession = ExtendedDisplaySettingsRestoreSession(context.contentResolver) whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder()) whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder()) whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)) whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)) .thenReturn(defaultTDA) .thenReturn(defaultTDA) Loading @@ -109,19 +134,28 @@ class DesktopDisplayModeControllerTest( shellTaskOrganizer, shellTaskOrganizer, desktopWallpaperActivityTokenProvider, desktopWallpaperActivityTokenProvider, inputManager, inputManager, displayController, mainHandler, mainHandler, ) ) runningTasks.add(freeformTask) runningTasks.add(freeformTask) runningTasks.add(fullscreenTask) runningTasks.add(fullscreenTask) whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks)) whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks)) whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken) whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken) whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay) setTabletModeStatus(SwitchState.UNKNOWN) setTabletModeStatus(SwitchState.UNKNOWN) } } @After fun tearDown() { extendedDisplaySettingsRestoreSession.restore() mockitoSession.finishMocking() } private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) { private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) { defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN) whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN) ExtendedDisplaySettingsSession(context.contentResolver, 1).use { setExtendedMode(true) connectExternalDisplay() connectExternalDisplay() if (expectToSwitch) { if (expectToSwitch) { // Assumes [connectExternalDisplay] properly triggered the switching transition. // Assumes [connectExternalDisplay] properly triggered the switching transition. Loading @@ -146,7 +180,6 @@ class DesktopDisplayModeControllerTest( verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull()) verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull()) } } } } } @Test @Test @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) Loading Loading @@ -176,16 +209,11 @@ class DesktopDisplayModeControllerTest( disconnectExternalDisplay() disconnectExternalDisplay() } } setTabletModeStatus(tabletModeStatus) setTabletModeStatus(tabletModeStatus) setExtendedMode(param.extendedDisplayEnabled) ExtendedDisplaySettingsSession( context.contentResolver, if (param.extendedDisplayEnabled) 1 else 0, ) .use { assertThat(controller.getTargetWindowingModeForDefaultDisplay()) assertThat(controller.getTargetWindowingModeForDefaultDisplay()) .isEqualTo(param.expectedWindowingMode) .isEqualTo(param.expectedWindowingMode) } } } @Test @Test @EnableFlags( @EnableFlags( Loading @@ -199,35 +227,28 @@ class DesktopDisplayModeControllerTest( disconnectExternalDisplay() disconnectExternalDisplay() } } setTabletModeStatus(param.tabletModeStatus) setTabletModeStatus(param.tabletModeStatus) setExtendedMode(param.extendedDisplayEnabled) ExtendedDisplaySettingsSession( context.contentResolver, if (param.extendedDisplayEnabled) 1 else 0, ) .use { assertThat(controller.getTargetWindowingModeForDefaultDisplay()) assertThat(controller.getTargetWindowingModeForDefaultDisplay()) .isEqualTo(param.expectedWindowingMode) .isEqualTo(param.expectedWindowingMode) } } } @Test @Test @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) fun displayWindowingModeSwitch_existingTasksOnConnected() { fun displayWindowingModeSwitch_existingTasksOnConnected() { defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN) whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN) setExtendedMode(true) ExtendedDisplaySettingsSession(context.contentResolver, 1).use { connectExternalDisplay() connectExternalDisplay() val arg = argumentCaptor<WindowContainerTransaction>() val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(1)) verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) .isEqualTo(WINDOWING_MODE_UNDEFINED) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FULLSCREEN) .isEqualTo(WINDOWING_MODE_FULLSCREEN) } } } @Test @Test @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) Loading @@ -236,19 +257,17 @@ class DesktopDisplayModeControllerTest( whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { WINDOWING_MODE_FULLSCREEN WINDOWING_MODE_FULLSCREEN } } setExtendedMode(true) ExtendedDisplaySettingsSession(context.contentResolver, 1).use { disconnectExternalDisplay() disconnectExternalDisplay() val arg = argumentCaptor<WindowContainerTransaction>() val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(1)) verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FREEFORM) .isEqualTo(WINDOWING_MODE_FREEFORM) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) .isEqualTo(WINDOWING_MODE_UNDEFINED) } } } private fun connectExternalDisplay() { private fun connectExternalDisplay() { whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) Loading @@ -266,18 +285,27 @@ class DesktopDisplayModeControllerTest( whenever(inputManager.isInTabletMode()).thenReturn(status.value) whenever(inputManager.isInTabletMode()).thenReturn(status.value) } } private class ExtendedDisplaySettingsSession( private fun setExtendedMode(enabled: Boolean) { private val contentResolver: ContentResolver, if (DisplayFlags.enableDisplayContentModeManagement()) { private val overrideValue: Int, doReturn(enabled).`when` { ) : AutoCloseable { DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, externalDisplay) } } else { Settings.Global.putInt( context.contentResolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, if (enabled) 1 else 0, ) } } private class ExtendedDisplaySettingsRestoreSession( private val contentResolver: ContentResolver ) { private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0) private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0) init { fun restore() { Settings.Global.putInt(contentResolver, settingName, overrideValue) } override fun close() { Settings.Global.putInt(contentResolver, settingName, initialValue) Settings.Global.putInt(contentResolver, settingName, initialValue) } } } } Loading @@ -287,7 +315,8 @@ class DesktopDisplayModeControllerTest( context: TestParameterValuesProvider.Context context: TestParameterValuesProvider.Context ): List<FlagsParameterization> { ): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf( return FlagsParameterization.allCombinationsOf( Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH, DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT, ) ) } } } } Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -1474,6 +1474,7 @@ public abstract class WMShellModule { ShellTaskOrganizer shellTaskOrganizer, ShellTaskOrganizer shellTaskOrganizer, DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider, DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider, InputManager inputManager, InputManager inputManager, DisplayController displayController, @ShellMainThread Handler mainHandler @ShellMainThread Handler mainHandler ) { ) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { Loading @@ -1488,6 +1489,7 @@ public abstract class WMShellModule { shellTaskOrganizer, shellTaskOrganizer, desktopWallpaperActivityTokenProvider, desktopWallpaperActivityTokenProvider, inputManager, inputManager, displayController, mainHandler)); mainHandler)); } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt +17 −3 Original line number Original line Diff line number Diff line Loading @@ -35,9 +35,11 @@ import com.android.internal.annotations.VisibleForTesting import com.android.internal.protolog.ProtoLog import com.android.internal.protolog.ProtoLog import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions /** Controls the display windowing mode in desktop mode */ /** Controls the display windowing mode in desktop mode */ Loading @@ -49,6 +51,7 @@ class DesktopDisplayModeController( private val shellTaskOrganizer: ShellTaskOrganizer, private val shellTaskOrganizer: ShellTaskOrganizer, private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider, private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider, private val inputManager: InputManager, private val inputManager: InputManager, private val displayController: DisplayController, @ShellMainThread private val mainHandler: Handler, @ShellMainThread private val mainHandler: Handler, ) { ) { Loading Loading @@ -128,14 +131,25 @@ class DesktopDisplayModeController( return windowManager.getWindowingMode(DEFAULT_DISPLAY) return windowManager.getWindowingMode(DEFAULT_DISPLAY) } } // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available. private fun isExtendedDisplayEnabled(): Boolean { private fun isExtendedDisplayEnabled() = if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue) { 0 != return rootTaskDisplayAreaOrganizer .getDisplayIds() .filter { it != DEFAULT_DISPLAY } .any { displayId -> displayController.getDisplay(displayId)?.let { display -> DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display) } ?: false } } return 0 != Settings.Global.getInt( Settings.Global.getInt( context.contentResolver, context.contentResolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0, 0, ) ) } private fun hasExternalDisplay() = private fun hasExternalDisplay() = rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY } rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt +98 −69 Original line number Original line Diff line number Diff line Loading @@ -26,28 +26,37 @@ import android.os.Binder import android.os.Handler import android.os.Handler 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.platform.test.annotations.UsesFlags import android.platform.test.flag.junit.FlagsParameterization import android.platform.test.flag.junit.FlagsParameterization import android.provider.Settings import android.provider.Settings import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS import android.view.Display import android.view.Display.DEFAULT_DISPLAY import android.view.Display.DEFAULT_DISPLAY import android.view.IWindowManager import android.view.IWindowManager import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_CHANGE import android.window.DisplayAreaInfo import android.window.DisplayAreaInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.ExtendedMockito.never import com.android.dx.mockito.inline.extended.ExtendedMockito.never import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.server.display.feature.flags.Flags as DisplayFlags import com.android.window.flags.Flags import com.android.window.flags.Flags import com.android.wm.shell.MockToken import com.android.wm.shell.MockToken import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.common.DisplayController import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import com.google.testing.junit.testparameterinjector.TestParameter import com.google.testing.junit.testparameterinjector.TestParameter import com.google.testing.junit.testparameterinjector.TestParameterInjector import com.google.testing.junit.testparameterinjector.TestParameterInjector import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider import org.junit.After 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 Loading @@ -60,6 +69,7 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.eq import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import org.mockito.kotlin.whenever import org.mockito.quality.Strictness /** /** * Test class for [DesktopDisplayModeController] * Test class for [DesktopDisplayModeController] Loading @@ -68,6 +78,7 @@ import org.mockito.kotlin.whenever */ */ @SmallTest @SmallTest @RunWith(TestParameterInjector::class) @RunWith(TestParameterInjector::class) @UsesFlags(com.android.server.display.feature.flags.Flags::class) class DesktopDisplayModeControllerTest( class DesktopDisplayModeControllerTest( @TestParameter(valuesProvider = FlagsParameterizationProvider::class) @TestParameter(valuesProvider = FlagsParameterizationProvider::class) flags: FlagsParameterization flags: FlagsParameterization Loading @@ -79,6 +90,7 @@ class DesktopDisplayModeControllerTest( private val desktopWallpaperActivityTokenProvider = private val desktopWallpaperActivityTokenProvider = mock<DesktopWallpaperActivityTokenProvider>() mock<DesktopWallpaperActivityTokenProvider>() private val inputManager = mock<InputManager>() private val inputManager = mock<InputManager>() private val displayController = mock<DisplayController>() private val mainHandler = mock<Handler>() private val mainHandler = mock<Handler>() private lateinit var controller: DesktopDisplayModeController private lateinit var controller: DesktopDisplayModeController Loading @@ -90,6 +102,12 @@ class DesktopDisplayModeControllerTest( TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build() TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build() private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) private val wallpaperToken = MockToken().token() private val wallpaperToken = MockToken().token() private val externalDisplay = mock<Display>() private lateinit var extendedDisplaySettingsRestoreSession: ExtendedDisplaySettingsRestoreSession private lateinit var mockitoSession: StaticMockitoSession init { init { mSetFlagsRule.setFlagsParameterization(flags) mSetFlagsRule.setFlagsParameterization(flags) Loading @@ -97,6 +115,13 @@ class DesktopDisplayModeControllerTest( @Before @Before fun setUp() { fun setUp() { mockitoSession = mockitoSession() .strictness(Strictness.LENIENT) .spyStatic(DesktopModeStatus::class.java) .startMocking() extendedDisplaySettingsRestoreSession = ExtendedDisplaySettingsRestoreSession(context.contentResolver) whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder()) whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder()) whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)) whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)) .thenReturn(defaultTDA) .thenReturn(defaultTDA) Loading @@ -109,19 +134,28 @@ class DesktopDisplayModeControllerTest( shellTaskOrganizer, shellTaskOrganizer, desktopWallpaperActivityTokenProvider, desktopWallpaperActivityTokenProvider, inputManager, inputManager, displayController, mainHandler, mainHandler, ) ) runningTasks.add(freeformTask) runningTasks.add(freeformTask) runningTasks.add(fullscreenTask) runningTasks.add(fullscreenTask) whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks)) whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks)) whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken) whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken) whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay) setTabletModeStatus(SwitchState.UNKNOWN) setTabletModeStatus(SwitchState.UNKNOWN) } } @After fun tearDown() { extendedDisplaySettingsRestoreSession.restore() mockitoSession.finishMocking() } private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) { private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) { defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN) whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN) ExtendedDisplaySettingsSession(context.contentResolver, 1).use { setExtendedMode(true) connectExternalDisplay() connectExternalDisplay() if (expectToSwitch) { if (expectToSwitch) { // Assumes [connectExternalDisplay] properly triggered the switching transition. // Assumes [connectExternalDisplay] properly triggered the switching transition. Loading @@ -146,7 +180,6 @@ class DesktopDisplayModeControllerTest( verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull()) verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull()) } } } } } @Test @Test @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) Loading Loading @@ -176,16 +209,11 @@ class DesktopDisplayModeControllerTest( disconnectExternalDisplay() disconnectExternalDisplay() } } setTabletModeStatus(tabletModeStatus) setTabletModeStatus(tabletModeStatus) setExtendedMode(param.extendedDisplayEnabled) ExtendedDisplaySettingsSession( context.contentResolver, if (param.extendedDisplayEnabled) 1 else 0, ) .use { assertThat(controller.getTargetWindowingModeForDefaultDisplay()) assertThat(controller.getTargetWindowingModeForDefaultDisplay()) .isEqualTo(param.expectedWindowingMode) .isEqualTo(param.expectedWindowingMode) } } } @Test @Test @EnableFlags( @EnableFlags( Loading @@ -199,35 +227,28 @@ class DesktopDisplayModeControllerTest( disconnectExternalDisplay() disconnectExternalDisplay() } } setTabletModeStatus(param.tabletModeStatus) setTabletModeStatus(param.tabletModeStatus) setExtendedMode(param.extendedDisplayEnabled) ExtendedDisplaySettingsSession( context.contentResolver, if (param.extendedDisplayEnabled) 1 else 0, ) .use { assertThat(controller.getTargetWindowingModeForDefaultDisplay()) assertThat(controller.getTargetWindowingModeForDefaultDisplay()) .isEqualTo(param.expectedWindowingMode) .isEqualTo(param.expectedWindowingMode) } } } @Test @Test @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) fun displayWindowingModeSwitch_existingTasksOnConnected() { fun displayWindowingModeSwitch_existingTasksOnConnected() { defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN) whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN) setExtendedMode(true) ExtendedDisplaySettingsSession(context.contentResolver, 1).use { connectExternalDisplay() connectExternalDisplay() val arg = argumentCaptor<WindowContainerTransaction>() val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(1)) verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) .isEqualTo(WINDOWING_MODE_UNDEFINED) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FULLSCREEN) .isEqualTo(WINDOWING_MODE_FULLSCREEN) } } } @Test @Test @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING) Loading @@ -236,19 +257,17 @@ class DesktopDisplayModeControllerTest( whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { WINDOWING_MODE_FULLSCREEN WINDOWING_MODE_FULLSCREEN } } setExtendedMode(true) ExtendedDisplaySettingsSession(context.contentResolver, 1).use { disconnectExternalDisplay() disconnectExternalDisplay() val arg = argumentCaptor<WindowContainerTransaction>() val arg = argumentCaptor<WindowContainerTransaction>() verify(transitions, times(1)) verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull()) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FREEFORM) .isEqualTo(WINDOWING_MODE_FREEFORM) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) .isEqualTo(WINDOWING_MODE_UNDEFINED) } } } private fun connectExternalDisplay() { private fun connectExternalDisplay() { whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) whenever(rootTaskDisplayAreaOrganizer.getDisplayIds()) Loading @@ -266,18 +285,27 @@ class DesktopDisplayModeControllerTest( whenever(inputManager.isInTabletMode()).thenReturn(status.value) whenever(inputManager.isInTabletMode()).thenReturn(status.value) } } private class ExtendedDisplaySettingsSession( private fun setExtendedMode(enabled: Boolean) { private val contentResolver: ContentResolver, if (DisplayFlags.enableDisplayContentModeManagement()) { private val overrideValue: Int, doReturn(enabled).`when` { ) : AutoCloseable { DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, externalDisplay) } } else { Settings.Global.putInt( context.contentResolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, if (enabled) 1 else 0, ) } } private class ExtendedDisplaySettingsRestoreSession( private val contentResolver: ContentResolver ) { private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0) private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0) init { fun restore() { Settings.Global.putInt(contentResolver, settingName, overrideValue) } override fun close() { Settings.Global.putInt(contentResolver, settingName, initialValue) Settings.Global.putInt(contentResolver, settingName, initialValue) } } } } Loading @@ -287,7 +315,8 @@ class DesktopDisplayModeControllerTest( context: TestParameterValuesProvider.Context context: TestParameterValuesProvider.Context ): List<FlagsParameterization> { ): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf( return FlagsParameterization.allCombinationsOf( Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH, DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT, ) ) } } } } Loading