From 2b8b6f23c4cd7af2ef2ab9cd6aed3f8d4067af54 Mon Sep 17 00:00:00 2001 From: Eghosa Ewansiha-Vlachavas Date: Fri, 8 Mar 2024 15:09:45 +0000 Subject: 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 --- libs/WindowManager/Shell/res/values/config.xml | 3 ++ .../wm/shell/desktopmode/DesktopModeStatus.java | 37 ++++++++----- .../wm/shell/desktopmode/DesktopTasksController.kt | 2 +- .../DesktopModeWindowDecorViewModel.java | 2 +- .../desktopmode/DesktopTasksControllerTest.kt | 31 ++++++----- .../DesktopModeWindowDecorViewModelTests.kt | 63 ++++++++++++++-------- 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 @@ true + + + false 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()) { -- cgit v1.2.3-59-g8ed1b