Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6ce00f97 authored by Eghosa Ewansiha-Vlachavas's avatar Eghosa Ewansiha-Vlachavas
Browse files

Disable Desktop Windowing for devices with smaller screen sizes

For devices with screen size smaller than
`Configuration.SCREENLAYOUT_SIZE_XLARGE`, hide the desktop windowing app
handle and ignore `DesktopTaskController.moveToDesktop` calls. This
hides all the on screen entry points and disables the adb commands and
shortcuts to enter Desktop Windowing.

Fixes: 326957646
Test: atest WMShellUnitTests:DesktopModeWindowDecorViewModelTests
Test: atest WMShellUnitTests:DesktopTasksControllerTest
Change-Id: I6f3224052c7fd88beefb9e58b0009569376d3a62
parent f18df089
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

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.os.SystemProperties;

import com.android.window.flags.Flags;
@@ -66,6 +70,9 @@ 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);

    /**
     * Return {@code true} if desktop windowing is enabled
     */
@@ -104,4 +111,21 @@ public class DesktopModeStatus {
    public static boolean useRoundedCorners() {
        return USE_ROUNDED_CORNERS;
    }

    /**
     * Return whether the display size restrictions should be enforced.
     */
    public static boolean enforceDisplayRestrictions() {
        return ENFORCE_DISPLAY_RESTRICTIONS;
    }

    /**
     * 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.
     */
    public static boolean meetsMinimumDisplayRequirements(@NonNull RunningTaskInfo taskInfo) {
        return !enforceDisplayRestrictions()
                || taskInfo.configuration.isLayoutSizeAtLeast(SCREENLAYOUT_SIZE_XLARGE);
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -305,6 +305,12 @@ class DesktopTasksController(
            task: RunningTaskInfo,
            wct: WindowContainerTransaction = WindowContainerTransaction()
    ) {
        if (!DesktopModeStatus.meetsMinimumDisplayRequirements(task)) {
            KtProtoLog.w(
                WM_SHELL_DESKTOP_MODE, "DesktopTasksController: Cannot enter desktop, " +
                        "display does not meet minimum size requirements")
            return
        }
        KtProtoLog.v(
            WM_SHELL_DESKTOP_MODE,
            "DesktopTasksController: moveToDesktop taskId=%d",
+1 −2
Original line number Diff line number Diff line
@@ -1058,8 +1058,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
                && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
                && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop()
                && mDisplayController.getDisplayContext(taskInfo.displayId)
                .getResources().getConfiguration().smallestScreenWidthDp >= 600;
                && DesktopModeStatus.meetsMinimumDisplayRequirements(taskInfo);
    }

    private void createWindowDecoration(
+44 −1
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ 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
@@ -123,7 +125,7 @@ class DesktopTasksControllerTest : ShellTestCase() {

    @Before
    fun setUp() {
        mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking()
        mockitoSession = mockitoSession().spyStatic(DesktopModeStatus::class.java).startMocking()
        whenever(DesktopModeStatus.isEnabled()).thenReturn(true)

        shellInit = Mockito.spy(ShellInit(testExecutor))
@@ -331,6 +333,45 @@ class DesktopTasksControllerTest : ShellTestCase() {
        verifyWCTNotExecuted()
    }

    @Test
    fun moveToDesktop_screenSizeBelowXLarge_doesNothing() {
        val task = setUpFullscreenTask()

        // Update screen layout to be below minimum size
        task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL

        controller.moveToDesktop(task)
        verifyWCTNotExecuted()
    }

    @Test
    fun moveToDesktop_screenSizeBelowXLarge_displayRestrictionsOverridden_taskIsMovedToDesktop() {
        val task = setUpFullscreenTask()

        // Update screen layout to be below minimum size
        task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL

        // Simulate enforce display restrictions system property overridden to false
        whenever(DesktopModeStatus.enforceDisplayRestrictions()).thenReturn(false)

        controller.moveToDesktop(task)

        val wct = getLatestMoveToDesktopWct()
        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FREEFORM)
    }

    @Test
    fun moveToDesktop_screenSizeXLarge_taskIsMovedToDesktop() {
        val task = setUpFullscreenTask()

        controller.moveToDesktop(task)

        val wct = getLatestMoveToDesktopWct()
        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FREEFORM)
    }

    @Test
    fun moveToDesktop_otherFreeformTasksBroughtToFront() {
        val homeTask = setUpHomeTask()
@@ -816,6 +857,7 @@ class DesktopTasksControllerTest : ShellTestCase() {

    private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
        val task = createFullscreenTask(displayId)
        task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
        runningTasks.add(task)
        return task
@@ -823,6 +865,7 @@ class DesktopTasksControllerTest : ShellTestCase() {

    private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
        val task = createSplitScreenTask(displayId)
        task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
        whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
        runningTasks.add(task)
+62 −0
Original line number Diff line number Diff line
@@ -23,10 +23,14 @@ 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
import android.os.Handler
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.util.SparseArray
@@ -42,6 +46,9 @@ import android.view.SurfaceView
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.StaticMockitoSession
import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
@@ -51,6 +58,7 @@ import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeStatus
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.sysui.KeyguardChangeListener
import com.android.wm.shell.sysui.ShellCommandHandler
@@ -59,6 +67,7 @@ import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -71,6 +80,7 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
import java.util.Optional
import java.util.function.Supplier
import org.mockito.Mockito
@@ -82,6 +92,10 @@ import org.mockito.kotlin.spy
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
    @JvmField
    @Rule
    val setFlagsRule = SetFlagsRule()

    @Mock private lateinit var mockDesktopModeWindowDecorFactory:
            DesktopModeWindowDecoration.Factory
    @Mock private lateinit var mockMainHandler: Handler
@@ -351,6 +365,54 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
        inOrder.verify(windowDecorByTaskIdSpy).remove(task.taskId)
    }

    @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

        onTaskOpening(task)
        verify(mockDesktopModeWindowDecorFactory, never())
            .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
    fun testWindowDecor_screenSizeBelowXLarge_displayRestrictionsOverridden_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)

            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)
            verify(mockDesktopModeWindowDecorFactory)
                .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
        } finally {
            mockitoSession.finishMocking()
        }
    }

    @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)

        onTaskOpening(task)
        verify(mockDesktopModeWindowDecorFactory)
            .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
    }

    private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
        desktopModeWindowDecorViewModel.onTaskOpening(
                task,