diff options
| author | 2024-03-08 15:09:45 +0000 | |
|---|---|---|
| committer | 2024-03-08 18:41:19 +0000 | |
| commit | 2b8b6f23c4cd7af2ef2ab9cd6aed3f8d4067af54 (patch) | |
| tree | dec37d4392db8fd73a14e0691e0741acaf78ef18 | |
| parent | 6ce00f979e5bd69851967f48371807b6bc2b56e3 (diff) | |
Replace desktop mode screen size check with config property flag
The screen layout of a device can be modified by the user through
setting a higher or lower density, meaning the screen layout size can
change from the default value making it an unreliable gate. Calculating
the screen size in inches using the window metrics was also explored but
did not result in accureate values given current APIs and thus is also
an unreliable gate.
Instead introduce a new config property flag, to gate the feature,
which represents whether a device supports desktop mode. By default
this will be false but set to true in the config of supported devices.
Bug: 326957646
Fixes: 328684732
Test: atest WMShellUnitTests:DesktopModeWindowDecorViewModelTests
Test: atest WMShellUnitTests:DesktopTasksControllerTest
Change-Id: I54329693746c6cb44ea4a54a6de00b33f9bc0a9c
6 files changed, 87 insertions, 51 deletions
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index a541c590575f..c68b0be47228 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -148,4 +148,7 @@ <!-- Whether pointer pilfer is required to start back animation. --> <bool name="config_backAnimationRequiresPointerPilfer">true</bool> + + <!-- Whether desktop mode is supported on the current device --> + <bool name="config_isDesktopModeSupported">false</bool> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java index 7b8486870a40..494d89307514 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java @@ -16,13 +16,13 @@ package com.android.wm.shell.desktopmode; -import static android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE; - import android.annotation.NonNull; -import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; import android.os.SystemProperties; +import com.android.internal.annotations.VisibleForTesting; import com.android.window.flags.Flags; +import com.android.wm.shell.R; /** * Constants for desktop mode feature @@ -70,8 +70,11 @@ public class DesktopModeStatus { private static final boolean USE_ROUNDED_CORNERS = SystemProperties.getBoolean( "persist.wm.debug.desktop_use_rounded_corners", true); - private static final boolean ENFORCE_DISPLAY_RESTRICTIONS = SystemProperties.getBoolean( - "persist.wm.debug.desktop_mode_enforce_display_restrictions", true); + /** + * Flag to indicate whether to restrict desktop mode to supported devices. + */ + private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean( + "persist.wm.debug.desktop_mode_enforce_device_restrictions", true); /** * Return {@code true} if desktop windowing is enabled @@ -113,19 +116,25 @@ public class DesktopModeStatus { } /** - * Return whether the display size restrictions should be enforced. + * Return {@code true} if desktop mode should be restricted to supported devices. + */ + @VisibleForTesting + public static boolean enforceDeviceRestrictions() { + return ENFORCE_DEVICE_RESTRICTIONS; + } + + /** + * Return {@code true} if the current device supports desktop mode. */ - public static boolean enforceDisplayRestrictions() { - return ENFORCE_DISPLAY_RESTRICTIONS; + @VisibleForTesting + public static boolean isDesktopModeSupported(@NonNull Context context) { + return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported); } /** - * Return {@code true} if the display associated with the task is at least of size - * {@link android.content.res.Configuration#SCREENLAYOUT_SIZE_XLARGE} or has been overridden to - * ignore the size constraint. + * Return {@code true} if desktop mode can be entered on the current device. */ - public static boolean meetsMinimumDisplayRequirements(@NonNull RunningTaskInfo taskInfo) { - return !enforceDisplayRestrictions() - || taskInfo.configuration.isLayoutSizeAtLeast(SCREENLAYOUT_SIZE_XLARGE); + public static boolean canEnterDesktopMode(@NonNull Context context) { + return !enforceDeviceRestrictions() || isDesktopModeSupported(context); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 2c66fd681f62..09a87aeccbc6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -305,7 +305,7 @@ class DesktopTasksController( task: RunningTaskInfo, wct: WindowContainerTransaction = WindowContainerTransaction() ) { - if (!DesktopModeStatus.meetsMinimumDisplayRequirements(task)) { + if (!DesktopModeStatus.canEnterDesktopMode(context)) { KtProtoLog.w( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: Cannot enter desktop, " + "display does not meet minimum size requirements") diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index dc9b532a70a6..6e6e0102de93 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -1058,7 +1058,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop() - && DesktopModeStatus.meetsMinimumDisplayRequirements(taskInfo); + && DesktopModeStatus.canEnterDesktopMode(mContext); } private void createWindowDecoration( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 4c8a3088a79f..17175db30b52 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -23,8 +23,6 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED -import android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL -import android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE import android.os.Binder import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY @@ -38,6 +36,7 @@ import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER 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.StaticMockitoSession @@ -88,6 +87,7 @@ import org.mockito.Mockito.anyInt import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever +import org.mockito.quality.Strictness @SmallTest @RunWith(AndroidTestingRunner::class) @@ -125,7 +125,8 @@ class DesktopTasksControllerTest : ShellTestCase() { @Before fun setUp() { - mockitoSession = mockitoSession().spyStatic(DesktopModeStatus::class.java).startMocking() + mockitoSession = mockitoSession().strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus::class.java).startMocking() whenever(DesktopModeStatus.isEnabled()).thenReturn(true) shellInit = Mockito.spy(ShellInit(testExecutor)) @@ -334,25 +335,25 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToDesktop_screenSizeBelowXLarge_doesNothing() { + fun moveToDesktop_deviceNotSupported_doesNothing() { val task = setUpFullscreenTask() - // Update screen layout to be below minimum size - task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL + // Simulate non compatible device + doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } controller.moveToDesktop(task) verifyWCTNotExecuted() } @Test - fun moveToDesktop_screenSizeBelowXLarge_displayRestrictionsOverridden_taskIsMovedToDesktop() { + fun moveToDesktop_deviceNotSupported_deviceRestrictionsOverridden_taskIsMovedToDesktop() { val task = setUpFullscreenTask() - // Update screen layout to be below minimum size - task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL + // Simulate non compatible device + doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } - // Simulate enforce display restrictions system property overridden to false - whenever(DesktopModeStatus.enforceDisplayRestrictions()).thenReturn(false) + // Simulate enforce device restrictions system property overridden to false + whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(false) controller.moveToDesktop(task) @@ -362,7 +363,7 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToDesktop_screenSizeXLarge_taskIsMovedToDesktop() { + fun moveToDesktop_deviceSupported_taskIsMovedToDesktop() { val task = setUpFullscreenTask() controller.moveToDesktop(task) @@ -857,7 +858,8 @@ class DesktopTasksControllerTest : ShellTestCase() { private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { val task = createFullscreenTask(displayId) - task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE + doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } + whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) runningTasks.add(task) return task @@ -865,7 +867,8 @@ class DesktopTasksControllerTest : ShellTestCase() { private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { val task = createSplitScreenTask(displayId) - task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE + doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } + whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true) whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) runningTasks.add(task) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 83519bbf624a..6940739d68b2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -23,8 +23,6 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.content.Context -import android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL -import android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE import android.graphics.Rect import android.hardware.display.DisplayManager import android.hardware.display.VirtualDisplay @@ -47,6 +45,7 @@ import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.window.flags.Flags import com.android.wm.shell.RootTaskDisplayAreaOrganizer @@ -367,30 +366,41 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun testWindowDecor_screenSizeBelowXLarge_decorNotCreated() { - val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) - // Update screen layout to be below minimum size - task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL + fun testWindowDecor_desktopModeUnsupportedOnDevice_decorNotCreated() { + val mockitoSession: StaticMockitoSession = mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus::class.java) + .startMocking() + try { + // Simulate default enforce device restrictions system property + whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true) - onTaskOpening(task) - verify(mockDesktopModeWindowDecorFactory, never()) - .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any()) + val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) + // Simulate device that doesn't support desktop mode + doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } + + onTaskOpening(task) + verify(mockDesktopModeWindowDecorFactory, never()) + .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any()) + } finally { + mockitoSession.finishMocking() + } } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun testWindowDecor_screenSizeBelowXLarge_displayRestrictionsOverridden_decorCreated() { + fun testWindowDecor_desktopModeUnsupportedOnDevice_deviceRestrictionsOverridden_decorCreated() { val mockitoSession: StaticMockitoSession = mockitoSession() .strictness(Strictness.LENIENT) .spyStatic(DesktopModeStatus::class.java) .startMocking() try { - // Simulate enforce display restrictions system property overridden to false - whenever(DesktopModeStatus.enforceDisplayRestrictions()).thenReturn(false) + // Simulate enforce device restrictions system property overridden to false + whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(false) + // Simulate device that doesn't support desktop mode + doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) - // Update screen layout to be below minimum size - task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL setUpMockDecorationsForTasks(task) onTaskOpening(task) @@ -403,14 +413,25 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun testWindowDecor_screenSizeXLarge_decorCreated() { - val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) - task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE - setUpMockDecorationsForTasks(task) + fun testWindowDecor_deviceSupportsDesktopMode_decorCreated() { + val mockitoSession: StaticMockitoSession = mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus::class.java) + .startMocking() + try { + // Simulate default enforce device restrictions system property + whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true) - onTaskOpening(task) - verify(mockDesktopModeWindowDecorFactory) - .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any()) + val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) + doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } + setUpMockDecorationsForTasks(task) + + onTaskOpening(task) + verify(mockDesktopModeWindowDecorFactory) + .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any()) + } finally { + mockitoSession.finishMocking() + } } private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) { |