Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java +20 −4 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ class TaskPositioner implements DragPositioningCallback { private final DisplayController mDisplayController; private final WindowDecoration mWindowDecoration; private final Rect mTempBounds = new Rect(); private final Rect mTaskBoundsAtDragStart = new Rect(); private final PointF mRepositionStartPoint = new PointF(); private final Rect mRepositionTaskBounds = new Rect(); Loading Loading @@ -117,17 +118,32 @@ class TaskPositioner implements DragPositioningCallback { final float deltaX = x - mRepositionStartPoint.x; final float deltaY = y - mRepositionStartPoint.y; mRepositionTaskBounds.set(mTaskBoundsAtDragStart); final Rect stableBounds = mTempBounds; // Make sure the new resizing destination in any direction falls within the stable bounds. // If not, set the bounds back to the old location that was valid to avoid conflicts with // some regions such as the gesture area. mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId()) .getStableBounds(stableBounds); if ((mCtrlType & CTRL_TYPE_LEFT) != 0) { mRepositionTaskBounds.left += deltaX; final int candidateLeft = mRepositionTaskBounds.left + (int) deltaX; mRepositionTaskBounds.left = (candidateLeft > stableBounds.left) ? candidateLeft : oldLeft; } if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) { mRepositionTaskBounds.right += deltaX; final int candidateRight = mRepositionTaskBounds.right + (int) deltaX; mRepositionTaskBounds.right = (candidateRight < stableBounds.right) ? candidateRight : oldRight; } if ((mCtrlType & CTRL_TYPE_TOP) != 0) { mRepositionTaskBounds.top += deltaY; final int candidateTop = mRepositionTaskBounds.top + (int) deltaY; mRepositionTaskBounds.top = (candidateTop > stableBounds.top) ? candidateTop : oldTop; } if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) { mRepositionTaskBounds.bottom += deltaY; final int candidateBottom = mRepositionTaskBounds.bottom + (int) deltaY; mRepositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom) ? candidateBottom : oldBottom; } if (mCtrlType == CTRL_TYPE_UNDEFINED) { mRepositionTaskBounds.offset((int) deltaX, (int) deltaY); Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt +91 −1 Original line number Diff line number Diff line Loading @@ -5,13 +5,16 @@ import android.app.WindowConfiguration import android.graphics.Rect import android.os.IBinder import android.testing.AndroidTestingRunner import android.view.Display import android.window.WindowContainerToken import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING import androidx.test.filters.SmallTest import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED Loading @@ -19,10 +22,11 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.any import org.mockito.Mockito.argThat import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations /** Loading Loading @@ -51,6 +55,8 @@ class TaskPositionerTest : ShellTestCase() { private lateinit var mockDisplayController: DisplayController @Mock private lateinit var mockDisplayLayout: DisplayLayout @Mock private lateinit var mockDisplay: Display private lateinit var taskPositioner: TaskPositioner Loading @@ -68,6 +74,9 @@ class TaskPositionerTest : ShellTestCase() { `when`(taskToken.asBinder()).thenReturn(taskBinder) `when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) `when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI) `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> (i.arguments.first() as Rect).set(STABLE_BOUNDS) } mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = TASK_ID Loading @@ -78,6 +87,8 @@ class TaskPositionerTest : ShellTestCase() { displayId = DISPLAY_ID configuration.windowConfiguration.bounds = STARTING_BOUNDS } mockWindowDecoration.mDisplay = mockDisplay `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID } } @Test Loading Loading @@ -451,6 +462,72 @@ class TaskPositionerTest : ShellTestCase() { }) } fun testDragResize_toDisallowedBounds_freezesAtLimit() { taskPositioner.onDragPositioningStart( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat() ) // Resize the task by 10px to the right and bottom, a valid destination val newBounds = Rect( STARTING_BOUNDS.left, STARTING_BOUNDS.top, STARTING_BOUNDS.right + 10, STARTING_BOUNDS.bottom + 10) taskPositioner.onDragPositioningMove( newBounds.right.toFloat(), newBounds.bottom.toFloat() ) // Resize the task by another 10px to the right (allowed) and to just in the disallowed // area of the Y coordinate. val newBounds2 = Rect( newBounds.left, newBounds.top, newBounds.right + 10, DISALLOWED_RESIZE_AREA.top ) taskPositioner.onDragPositioningMove( newBounds2.right.toFloat(), newBounds2.bottom.toFloat() ) taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat()) // The first resize falls in the allowed area, verify there's a change for it. verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && change.ofBounds(newBounds) } }) // The second resize falls in the disallowed area, verify there's no change for it. verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && change.ofBounds(newBounds2) } }) // Instead, there should be a change for its allowed portion (the X movement) with the Y // staying frozen in the last valid resize position. verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && change.ofBounds( Rect( newBounds2.left, newBounds2.top, newBounds2.right, newBounds.bottom // Stayed at the first resize destination. ) ) } }) } private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean { return ((windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) && bounds == configuration.windowConfiguration.bounds } companion object { private const val TASK_ID = 5 private const val MIN_WIDTH = 10 Loading @@ -458,6 +535,19 @@ class TaskPositionerTest : ShellTestCase() { private const val DENSITY_DPI = 20 private const val DEFAULT_MIN = 40 private const val DISPLAY_ID = 1 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 DISALLOWED_RESIZE_AREA = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT, DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom) private val STABLE_BOUNDS = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.top, DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT ) } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java +20 −4 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ class TaskPositioner implements DragPositioningCallback { private final DisplayController mDisplayController; private final WindowDecoration mWindowDecoration; private final Rect mTempBounds = new Rect(); private final Rect mTaskBoundsAtDragStart = new Rect(); private final PointF mRepositionStartPoint = new PointF(); private final Rect mRepositionTaskBounds = new Rect(); Loading Loading @@ -117,17 +118,32 @@ class TaskPositioner implements DragPositioningCallback { final float deltaX = x - mRepositionStartPoint.x; final float deltaY = y - mRepositionStartPoint.y; mRepositionTaskBounds.set(mTaskBoundsAtDragStart); final Rect stableBounds = mTempBounds; // Make sure the new resizing destination in any direction falls within the stable bounds. // If not, set the bounds back to the old location that was valid to avoid conflicts with // some regions such as the gesture area. mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId()) .getStableBounds(stableBounds); if ((mCtrlType & CTRL_TYPE_LEFT) != 0) { mRepositionTaskBounds.left += deltaX; final int candidateLeft = mRepositionTaskBounds.left + (int) deltaX; mRepositionTaskBounds.left = (candidateLeft > stableBounds.left) ? candidateLeft : oldLeft; } if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) { mRepositionTaskBounds.right += deltaX; final int candidateRight = mRepositionTaskBounds.right + (int) deltaX; mRepositionTaskBounds.right = (candidateRight < stableBounds.right) ? candidateRight : oldRight; } if ((mCtrlType & CTRL_TYPE_TOP) != 0) { mRepositionTaskBounds.top += deltaY; final int candidateTop = mRepositionTaskBounds.top + (int) deltaY; mRepositionTaskBounds.top = (candidateTop > stableBounds.top) ? candidateTop : oldTop; } if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) { mRepositionTaskBounds.bottom += deltaY; final int candidateBottom = mRepositionTaskBounds.bottom + (int) deltaY; mRepositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom) ? candidateBottom : oldBottom; } if (mCtrlType == CTRL_TYPE_UNDEFINED) { mRepositionTaskBounds.offset((int) deltaX, (int) deltaY); Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt +91 −1 Original line number Diff line number Diff line Loading @@ -5,13 +5,16 @@ import android.app.WindowConfiguration import android.graphics.Rect import android.os.IBinder import android.testing.AndroidTestingRunner import android.view.Display import android.window.WindowContainerToken import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING import androidx.test.filters.SmallTest import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED Loading @@ -19,10 +22,11 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.any import org.mockito.Mockito.argThat import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations /** Loading Loading @@ -51,6 +55,8 @@ class TaskPositionerTest : ShellTestCase() { private lateinit var mockDisplayController: DisplayController @Mock private lateinit var mockDisplayLayout: DisplayLayout @Mock private lateinit var mockDisplay: Display private lateinit var taskPositioner: TaskPositioner Loading @@ -68,6 +74,9 @@ class TaskPositionerTest : ShellTestCase() { `when`(taskToken.asBinder()).thenReturn(taskBinder) `when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) `when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI) `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> (i.arguments.first() as Rect).set(STABLE_BOUNDS) } mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = TASK_ID Loading @@ -78,6 +87,8 @@ class TaskPositionerTest : ShellTestCase() { displayId = DISPLAY_ID configuration.windowConfiguration.bounds = STARTING_BOUNDS } mockWindowDecoration.mDisplay = mockDisplay `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID } } @Test Loading Loading @@ -451,6 +462,72 @@ class TaskPositionerTest : ShellTestCase() { }) } fun testDragResize_toDisallowedBounds_freezesAtLimit() { taskPositioner.onDragPositioningStart( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat() ) // Resize the task by 10px to the right and bottom, a valid destination val newBounds = Rect( STARTING_BOUNDS.left, STARTING_BOUNDS.top, STARTING_BOUNDS.right + 10, STARTING_BOUNDS.bottom + 10) taskPositioner.onDragPositioningMove( newBounds.right.toFloat(), newBounds.bottom.toFloat() ) // Resize the task by another 10px to the right (allowed) and to just in the disallowed // area of the Y coordinate. val newBounds2 = Rect( newBounds.left, newBounds.top, newBounds.right + 10, DISALLOWED_RESIZE_AREA.top ) taskPositioner.onDragPositioningMove( newBounds2.right.toFloat(), newBounds2.bottom.toFloat() ) taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat()) // The first resize falls in the allowed area, verify there's a change for it. verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && change.ofBounds(newBounds) } }) // The second resize falls in the disallowed area, verify there's no change for it. verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && change.ofBounds(newBounds2) } }) // Instead, there should be a change for its allowed portion (the X movement) with the Y // staying frozen in the last valid resize position. verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && change.ofBounds( Rect( newBounds2.left, newBounds2.top, newBounds2.right, newBounds.bottom // Stayed at the first resize destination. ) ) } }) } private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean { return ((windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) && bounds == configuration.windowConfiguration.bounds } companion object { private const val TASK_ID = 5 private const val MIN_WIDTH = 10 Loading @@ -458,6 +535,19 @@ class TaskPositionerTest : ShellTestCase() { private const val DENSITY_DPI = 20 private const val DEFAULT_MIN = 40 private const val DISPLAY_ID = 1 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 DISALLOWED_RESIZE_AREA = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT, DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom) private val STABLE_BOUNDS = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.top, DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT ) } }