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

Commit 9214f324 authored by Vania Desmonda's avatar Vania Desmonda
Browse files

Restrict maximum window size to the stable bounds of the display when

resizing a window in desktop windowing mode.

Fixes: 269663649
Flag: com.android.window.flags.enable_desktop_windowing_size_constraints
Test: manual test, atest CtsWindowManagerDeviceAnimations:ManifestLayoutTests and WMShellUnitTests:DragPositioningCallbackUtilityTest
Change-Id: I48ec0a8d7a85e70ebab0fb24cf8b78af44d3aa8a
parent f6ec228d
Loading
Loading
Loading
Loading
+32 −4
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.SurfaceControl;

import androidx.annotation.NonNull;

import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
@@ -106,13 +108,15 @@ public class DragPositioningCallbackUtility {
            repositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom)
                    ? candidateBottom : oldBottom;
        }
        // If width or height are negative or less than the minimum width or height, revert the
        // If width or height are negative or exceeding the width or height constraints, revert the
        // respective bounds to use previous bound dimensions.
        if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) {
        if (isExceedingWidthConstraint(repositionTaskBounds, stableBounds, displayController,
                windowDecoration)) {
            repositionTaskBounds.right = oldRight;
            repositionTaskBounds.left = oldLeft;
        }
        if (repositionTaskBounds.height() < getMinHeight(displayController, windowDecoration)) {
        if (isExceedingHeightConstraint(repositionTaskBounds, stableBounds, displayController,
                windowDecoration)) {
            repositionTaskBounds.top = oldTop;
            repositionTaskBounds.bottom = oldBottom;
        }
@@ -174,6 +178,30 @@ public class DragPositioningCallbackUtility {
        return result;
    }

    private static boolean isExceedingWidthConstraint(@NonNull Rect repositionTaskBounds,
            Rect maxResizeBounds, DisplayController displayController,
            WindowDecoration windowDecoration) {
        // Check if width is less than the minimum width constraint.
        if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) {
            return true;
        }
        // Check if width is more than the maximum resize bounds on desktop windowing mode.
        return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)
                && repositionTaskBounds.width() > maxResizeBounds.width();
    }

    private static boolean isExceedingHeightConstraint(@NonNull Rect repositionTaskBounds,
            Rect maxResizeBounds, DisplayController displayController,
            WindowDecoration windowDecoration) {
        // Check if height is less than the minimum height constraint.
        if (repositionTaskBounds.height() < getMinHeight(displayController, windowDecoration)) {
            return true;
        }
        // Check if height is more than the maximum resize bounds on desktop windowing mode.
        return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)
                && repositionTaskBounds.height() > maxResizeBounds.height();
    }

    private static float getMinWidth(DisplayController displayController,
            WindowDecoration windowDecoration) {
        return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinWidth(displayController,
+58 −0
Original line number Diff line number Diff line
@@ -21,7 +21,9 @@ import android.content.res.Resources
import android.graphics.PointF
import android.graphics.Rect
import android.os.IBinder
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display
import android.window.WindowContainerToken
@@ -36,6 +38,7 @@ import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -53,21 +56,32 @@ import org.mockito.MockitoAnnotations
class DragPositioningCallbackUtilityTest {
    @Mock
    private lateinit var mockWindowDecoration: WindowDecoration<*>

    @Mock
    private lateinit var taskToken: WindowContainerToken

    @Mock
    private lateinit var taskBinder: IBinder

    @Mock
    private lateinit var mockDisplayController: DisplayController

    @Mock
    private lateinit var mockDisplayLayout: DisplayLayout

    @Mock
    private lateinit var mockDisplay: Display

    @Mock
    private lateinit var mockContext: Context

    @Mock
    private lateinit var mockResources: Resources

    @JvmField
    @Rule
    val setFlagsRule = SetFlagsRule()

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
@@ -323,6 +337,49 @@ class DragPositioningCallbackUtilityTest {
        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom - 50)
    }

    @Test
    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
    fun testChangeBounds_windowSizeExceedsStableBounds_shouldBeAllowedToChangeBounds() {
        val startingPoint =
            PointF(OFF_CENTER_STARTING_BOUNDS.right.toFloat(),
                OFF_CENTER_STARTING_BOUNDS.bottom.toFloat())
        val repositionTaskBounds = Rect(OFF_CENTER_STARTING_BOUNDS)
        // Increase height and width by STABLE_BOUNDS. Subtract by 5px so that it doesn't reach
        // the disallowed drag area.
        val offset = 5
        val newX = STABLE_BOUNDS.right.toFloat() - offset
        val newY = STABLE_BOUNDS.bottom.toFloat() - offset
        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)

        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
            repositionTaskBounds, OFF_CENTER_STARTING_BOUNDS, STABLE_BOUNDS, delta,
            mockDisplayController, mockWindowDecoration)
        assertThat(repositionTaskBounds.width()).isGreaterThan(STABLE_BOUNDS.right)
        assertThat(repositionTaskBounds.height()).isGreaterThan(STABLE_BOUNDS.bottom)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
    fun testChangeBoundsInDesktopMode_windowSizeExceedsStableBounds_shouldBeLimitedToDisplaySize() {
        whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true)
        val startingPoint =
            PointF(OFF_CENTER_STARTING_BOUNDS.right.toFloat(),
                OFF_CENTER_STARTING_BOUNDS.bottom.toFloat())
        val repositionTaskBounds = Rect(OFF_CENTER_STARTING_BOUNDS)
        // Increase height and width by STABLE_BOUNDS. Subtract by 5px so that it doesn't reach
        // the disallowed drag area.
        val offset = 5
        val newX = STABLE_BOUNDS.right.toFloat() - offset
        val newY = STABLE_BOUNDS.bottom.toFloat() - offset
        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)

        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
            repositionTaskBounds, OFF_CENTER_STARTING_BOUNDS, STABLE_BOUNDS, delta,
            mockDisplayController, mockWindowDecoration)
        assertThat(repositionTaskBounds.width()).isLessThan(STABLE_BOUNDS.right)
        assertThat(repositionTaskBounds.height()).isLessThan(STABLE_BOUNDS.bottom)
    }

    private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) {
        mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
            taskId = TASK_ID
@@ -347,6 +404,7 @@ class DragPositioningCallbackUtilityTest {
        private const val NAVBAR_HEIGHT = 50
        private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
        private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
        private val OFF_CENTER_STARTING_BOUNDS = Rect(-100, -100, 10, 10)
        private val DISALLOWED_RESIZE_AREA = Rect(
            DISPLAY_BOUNDS.left,
            DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+9 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@ package com.android.wm.shell.windowdecor

import android.app.ActivityManager
import android.app.WindowConfiguration
import android.content.Context
import android.content.res.Resources
import android.graphics.Point
import android.graphics.Rect
import android.os.IBinder
@@ -98,6 +100,10 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
    private lateinit var mockFinishCallback: TransitionFinishCallback
    @Mock
    private lateinit var mockTransitions: Transitions
    @Mock
    private lateinit var mockContext: Context
    @Mock
    private lateinit var mockResources: Resources

    private lateinit var taskPositioner: VeiledResizeTaskPositioner

@@ -105,6 +111,9 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        mockDesktopWindowDecoration.mDisplay = mockDisplay
        mockDesktopWindowDecoration.mDecorWindowContext = mockContext
        whenever(mockContext.getResources()).thenReturn(mockResources)
        whenever(taskToken.asBinder()).thenReturn(taskBinder)
        whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
        whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)