diff options
13 files changed, 626 insertions, 152 deletions
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 ee94f30cfa69..b3109388da2c 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 @@ -16,7 +16,6 @@ package com.android.wm.shell.desktopmode -import android.app.ActivityManager import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD @@ -202,16 +201,15 @@ class DesktopTasksController( } } - /** * Move a task to fullscreen after being dragged from fullscreen and released back into * status bar area */ - fun cancelMoveToFreeform(task: RunningTaskInfo, startPosition: Point) { + fun cancelMoveToFreeform(task: RunningTaskInfo, position: Point) { val wct = WindowContainerTransaction() addMoveToFullscreenChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { - enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, startPosition, + enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, position, mOnAnimationFinishedCallback) } else { shellTaskOrganizer.applyTransaction(wct) @@ -219,13 +217,13 @@ class DesktopTasksController( } } - private fun moveToFullscreenWithAnimation(task: ActivityManager.RunningTaskInfo) { + private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) { val wct = WindowContainerTransaction() addMoveToFullscreenChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { exitDesktopTaskTransitionHandler.startTransition( - Transitions.TRANSIT_EXIT_DESKTOP_MODE, wct, mOnAnimationFinishedCallback) + Transitions.TRANSIT_EXIT_DESKTOP_MODE, wct, position, mOnAnimationFinishedCallback) } else { shellTaskOrganizer.applyTransaction(wct) releaseVisualIndicator() @@ -499,15 +497,15 @@ class DesktopTasksController( * Perform checks required on drag end. Move to fullscreen if drag ends in status bar area. * * @param taskInfo the task being dragged. - * @param y height of drag, to be checked against status bar height. + * @param position position of surface when drag ends */ fun onDragPositioningEnd( taskInfo: RunningTaskInfo, - y: Float + position: Point ) { val statusBarHeight = getStatusBarHeight(taskInfo) - if (y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { - moveToFullscreenWithAnimation(taskInfo) + if (position.y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { + moveToFullscreenWithAnimation(taskInfo, position) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java index d55fdddf4fea..3733b919e366 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java @@ -58,7 +58,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition public static final int FREEFORM_ANIMATION_DURATION = 336; private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); - private Point mStartPosition; + private Point mPosition; private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback; public EnterDesktopTaskTransitionHandler( @@ -90,15 +90,15 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition /** * Starts Transition of type TRANSIT_CANCEL_ENTERING_DESKTOP_MODE * @param wct WindowContainerTransaction for transition - * @param startPosition Position of task when transition is triggered + * @param position Position of task when transition is triggered * @param onAnimationEndCallback to be called after animation */ public void startCancelMoveToDesktopMode(@NonNull WindowContainerTransaction wct, - Point startPosition, + Point position, Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { - mStartPosition = startPosition; + mPosition = position; startTransition(Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE, wct, - mOnAnimationFinishedCallback); + onAnimationEndCallback); } @Override @@ -201,7 +201,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition if (type == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - && mStartPosition != null) { + && mPosition != null) { // This Transition animates a task to fullscreen after being dragged from the status // bar and then released back into the status bar area final SurfaceControl sc = change.getLeash(); @@ -217,7 +217,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition final SurfaceControl.Transaction t = mTransactionSupplier.get(); animator.addUpdateListener(animation -> { final float scale = (float) animation.getAnimatedValue(); - t.setPosition(sc, mStartPosition.x * (1 - scale), mStartPosition.y * (1 - scale)) + t.setPosition(sc, mPosition.x * (1 - scale), mPosition.y * (1 - scale)) .setScale(sc, scale, scale) .show(sc) .apply(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java index 160a83d7ed36..3ad5edf0e604 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java @@ -57,6 +57,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback; private Supplier<SurfaceControl.Transaction> mTransactionSupplier; + private Point mPosition; public ExitDesktopTaskTransitionHandler( Transitions transitions, @@ -77,11 +78,13 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH * Starts Transition of a given type * @param type Transition type * @param wct WindowContainerTransaction for transition + * @param position Position of the task when transition is started * @param onAnimationEndCallback to be called after animation */ public void startTransition(@WindowManager.TransitionType int type, - @NonNull WindowContainerTransaction wct, + @NonNull WindowContainerTransaction wct, Point position, Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { + mPosition = position; mOnAnimationFinishedCallback = onAnimationEndCallback; final IBinder token = mTransitions.startTransition(type, wct, this); mPendingTransitionTokens.add(token); @@ -143,17 +146,17 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH final ValueAnimator animator = new ValueAnimator(); animator.setFloatValues(0f, 1f); animator.setDuration(FULLSCREEN_ANIMATION_DURATION); + // The start bounds contain the correct dimensions of the task but hold the positioning + // before being dragged to the status bar to transition into fullscreen final Rect startBounds = change.getStartAbsBounds(); final float scaleX = (float) startBounds.width() / screenWidth; final float scaleY = (float) startBounds.height() / screenHeight; final SurfaceControl.Transaction t = mTransactionSupplier.get(); - Point startPos = new Point(startBounds.left, - startBounds.top); animator.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); float currentScaleX = scaleX + ((1 - scaleX) * fraction); float currentScaleY = scaleY + ((1 - scaleY) * fraction); - t.setPosition(sc, startPos.x * (1 - fraction), startPos.y * (1 - fraction)) + t.setPosition(sc, mPosition.x * (1 - fraction), mPosition.y * (1 - fraction)) .setScale(sc, currentScaleX, currentScaleY) .show(sc) .apply(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 212ad9ebceaf..2bb3cceb257f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; +import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; import android.util.SparseArray; @@ -185,18 +186,29 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mSyncQueue); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); - final FluidResizeTaskPositioner taskPositioner = - new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController); + final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback( + windowDecoration, taskInfo); final CaptionTouchEventListener touchEventListener = - new CaptionTouchEventListener(taskInfo, taskPositioner); + new CaptionTouchEventListener(taskInfo, dragPositioningCallback); windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); - windowDecoration.setDragPositioningCallback(taskPositioner); + windowDecoration.setDragPositioningCallback(dragPositioningCallback); windowDecoration.setDragDetector(touchEventListener.mDragDetector); windowDecoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */); setupCaptionColor(taskInfo, windowDecoration); } + private FluidResizeTaskPositioner createDragPositioningCallback( + CaptionWindowDecoration windowDecoration, RunningTaskInfo taskInfo) { + final int screenWidth = mDisplayController.getDisplayLayout(taskInfo.displayId).width(); + final int statusBarHeight = mDisplayController.getDisplayLayout(taskInfo.displayId) + .stableInsets().top; + final Rect disallowedAreaForEndBounds = new Rect(0, 0, screenWidth, + statusBarHeight); + return new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, + mDisplayController, disallowedAreaForEndBounds); + } + private class CaptionTouchEventListener implements View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler { 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 dffbedd197cf..0b821844e46c 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 @@ -33,7 +33,6 @@ import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; -import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; @@ -41,7 +40,6 @@ import android.hardware.input.InputManager; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.util.DisplayMetrics; import android.util.SparseArray; import android.view.Choreographer; import android.view.InputChannel; @@ -63,6 +61,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; @@ -392,10 +391,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { final int dragPointerIdx = e.findPointerIndex(mDragPointerId); + // Position of the task is calculated by subtracting the raw location of the + // motion event (the location of the motion relative to the display) by the + // location of the motion event relative to the task's bounds + final Point position = new Point( + (int) (e.getRawX(dragPointerIdx) - e.getX(dragPointerIdx)), + (int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx))); mDragPositioningCallback.onDragPositioningEnd( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo, - e.getRawY(dragPointerIdx))); + position)); final boolean wasDragging = mIsDragging; mIsDragging = false; return wasDragging; @@ -560,10 +565,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDragToDesktopAnimationStarted = false; return; } else if (mDragToDesktopAnimationStarted) { - Point startPosition = new Point((int) ev.getX(), (int) ev.getY()); + Point position = new Point((int) ev.getX(), (int) ev.getY()); mDesktopTasksController.ifPresent( c -> c.cancelMoveToFreeform(relevantDecor.mTaskInfo, - startPosition)); + position)); mDragToDesktopAnimationStarted = false; return; } @@ -615,11 +620,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * Gets bounds of a scaled window centered relative to the screen bounds * @param scale the amount to scale to relative to the Screen Bounds */ - private Rect calculateFreeformBounds(float scale) { - final Resources resources = mContext.getResources(); - final DisplayMetrics metrics = resources.getDisplayMetrics(); - final int screenWidth = metrics.widthPixels; - final int screenHeight = metrics.heightPixels; + private Rect calculateFreeformBounds(int displayId, float scale) { + final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId); + final int screenWidth = displayLayout.width(); + final int screenHeight = displayLayout.height(); final float adjustmentPercentage = (1f - scale) / 2; final Rect endBounds = new Rect((int) (screenWidth * adjustmentPercentage), @@ -648,7 +652,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); animator.setDuration(FREEFORM_ANIMATION_DURATION); final SurfaceControl sc = relevantDecor.mTaskSurface; - final Rect endBounds = calculateFreeformBounds(DRAG_FREEFORM_SCALE); + final Rect endBounds = calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE); final Transaction t = mTransactionFactory.get(); final float diffX = endBounds.centerX() - ev.getX(); final float diffY = endBounds.top - ev.getY(); @@ -665,9 +669,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - mDesktopTasksController.ifPresent(c -> - c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo, - calculateFreeformBounds(FINAL_FREEFORM_SCALE))); + mDesktopTasksController.ifPresent( + c -> c.onDragPositioningEndThroughStatusBar( + relevantDecor.mTaskInfo, + calculateFreeformBounds(ev.getDisplayId(), FINAL_FREEFORM_SCALE))); } }); animator.start(); @@ -783,17 +788,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSyncQueue); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); - final DragPositioningCallback dragPositioningCallback; - if (!DesktopModeStatus.isVeiledResizeEnabled()) { - dragPositioningCallback = - new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, - mDisplayController, mDragStartListener); - } else { - windowDecoration.createResizeVeil(); - dragPositioningCallback = - new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration, - mDisplayController, mDragStartListener); - } + final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback( + windowDecoration, taskInfo); final DesktopModeTouchEventListener touchEventListener = new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback); @@ -805,7 +801,22 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { false /* applyStartTransactionOnDraw */); incrementEventReceiverTasks(taskInfo.displayId); } - + private DragPositioningCallback createDragPositioningCallback( + @NonNull DesktopModeWindowDecoration windowDecoration, + @NonNull RunningTaskInfo taskInfo) { + final int screenWidth = mDisplayController.getDisplayLayout(taskInfo.displayId).width(); + final Rect disallowedAreaForEndBounds = new Rect(0, 0, screenWidth, + getStatusBarHeight(taskInfo.displayId)); + if (!DesktopModeStatus.isVeiledResizeEnabled()) { + return new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, + mDisplayController, disallowedAreaForEndBounds, mDragStartListener, + mTransactionFactory); + } else { + windowDecoration.createResizeVeil(); + return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration, + mDisplayController, disallowedAreaForEndBounds, mDragStartListener); + } + } private class DragStartListenerImpl implements DragPositioningCallbackUtility.DragStartListener { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java index ae93a43f78e9..09e29bcbcf9f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java @@ -25,6 +25,7 @@ import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; +import android.view.SurfaceControl; import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; @@ -51,9 +52,9 @@ public class DragPositioningCallbackUtility { } /** - * Based on type of drag and delta provided, calculate the new bounds to display for this task. + * Based on type of resize and delta provided, calculate the new bounds to display for this + * task. * @param ctrlType type of drag being performed - * @param hasMoved whether the current drag has moved on a prior input event * @param repositionTaskBounds the bounds the task is being repositioned to * @param taskBoundsAtDragStart the bounds of the task on the first drag input event * @param stableBounds bounds that represent the resize limit of this task @@ -62,16 +63,19 @@ public class DragPositioningCallbackUtility { * @param windowDecoration window decoration of the task being dragged * @return whether this method changed repositionTaskBounds */ - static boolean changeBounds(int ctrlType, boolean hasMoved, - Rect repositionTaskBounds, Rect taskBoundsAtDragStart, Rect stableBounds, - PointF delta, DisplayController displayController, WindowDecoration windowDecoration) { - // |mRepositionTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not - // true, we can compare it against |mTaskBoundsAtDragStart|. - final int oldLeft = hasMoved ? repositionTaskBounds.left : taskBoundsAtDragStart.left; - final int oldTop = hasMoved ? repositionTaskBounds.top : taskBoundsAtDragStart.top; - final int oldRight = hasMoved ? repositionTaskBounds.right : taskBoundsAtDragStart.right; - final int oldBottom = - hasMoved ? repositionTaskBounds.bottom : taskBoundsAtDragStart.bottom; + static boolean changeBounds(int ctrlType, Rect repositionTaskBounds, Rect taskBoundsAtDragStart, + Rect stableBounds, PointF delta, DisplayController displayController, + WindowDecoration windowDecoration) { + // If task is being dragged rather than resized, return since this method only handles + // with resizing + if (ctrlType == CTRL_TYPE_UNDEFINED) { + return false; + } + + final int oldLeft = repositionTaskBounds.left; + final int oldTop = repositionTaskBounds.top; + final int oldRight = repositionTaskBounds.right; + final int oldBottom = repositionTaskBounds.bottom; repositionTaskBounds.set(taskBoundsAtDragStart); @@ -101,10 +105,6 @@ public class DragPositioningCallbackUtility { repositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom) ? candidateBottom : oldBottom; } - if (ctrlType == CTRL_TYPE_UNDEFINED) { - repositionTaskBounds.offset((int) delta.x, (int) delta.y); - } - // If width or height are negative or less than the minimum width or height, revert the // respective bounds to use previous bound dimensions. if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) { @@ -126,8 +126,26 @@ public class DragPositioningCallbackUtility { } /** + * Set bounds using a {@link SurfaceControl.Transaction}. + */ + static void setPositionOnDrag(WindowDecoration decoration, Rect repositionTaskBounds, + Rect taskBoundsAtDragStart, PointF repositionStartPoint, SurfaceControl.Transaction t, + float x, float y) { + updateTaskBounds(repositionTaskBounds, taskBoundsAtDragStart, repositionStartPoint, x, y); + t.setPosition(decoration.mTaskSurface, repositionTaskBounds.left, + repositionTaskBounds.top); + } + + static void updateTaskBounds(Rect repositionTaskBounds, Rect taskBoundsAtDragStart, + PointF repositionStartPoint, float x, float y) { + final float deltaX = x - repositionStartPoint.x; + final float deltaY = y - repositionStartPoint.y; + repositionTaskBounds.set(taskBoundsAtDragStart); + repositionTaskBounds.offset((int) deltaX, (int) deltaY); + } + + /** * Apply a bounds change to a task. - * @param wct provided {@link WindowContainerTransaction} that may contain other changes * @param windowDecoration decor of task we are changing bounds for * @param taskBounds new bounds of this task * @param taskOrganizer applies the provided WindowContainerTransaction diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java index b366c809537f..9bcb77f03abd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java @@ -18,11 +18,14 @@ package com.android.wm.shell.windowdecor; import android.graphics.PointF; import android.graphics.Rect; +import android.view.SurfaceControl; import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import java.util.function.Supplier; + /** * A task positioner that resizes/relocates task contents as it is dragged. * Utilizes {@link DragPositioningCallbackUtility} to determine new task bounds. @@ -30,27 +33,35 @@ import com.android.wm.shell.common.DisplayController; class FluidResizeTaskPositioner implements DragPositioningCallback { private final ShellTaskOrganizer mTaskOrganizer; private final WindowDecoration mWindowDecoration; + private final Supplier<SurfaceControl.Transaction> mTransactionSupplier; private DisplayController mDisplayController; private DragPositioningCallbackUtility.DragStartListener mDragStartListener; private final Rect mStableBounds = new Rect(); private final Rect mTaskBoundsAtDragStart = new Rect(); private final PointF mRepositionStartPoint = new PointF(); private final Rect mRepositionTaskBounds = new Rect(); + // If a task move (not resize) finishes in this region, the positioner will not attempt to + // finalize the bounds there using WCT#setBounds + private final Rect mDisallowedAreaForEndBounds = new Rect(); + private boolean mHasDragResized; private int mCtrlType; - private boolean mHasMoved; FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration, - DisplayController displayController) { - this(taskOrganizer, windowDecoration, displayController, dragStartListener -> {}); + DisplayController displayController, Rect disallowedAreaForEndBounds) { + this(taskOrganizer, windowDecoration, displayController, disallowedAreaForEndBounds, + dragStartListener -> {}, SurfaceControl.Transaction::new); } FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration, - DisplayController displayController, - DragPositioningCallbackUtility.DragStartListener dragStartListener) { + DisplayController displayController, Rect disallowedAreaForEndBounds, + DragPositioningCallbackUtility.DragStartListener dragStartListener, + Supplier<SurfaceControl.Transaction> supplier) { mTaskOrganizer = taskOrganizer; mWindowDecoration = windowDecoration; mDisplayController = displayController; + mDisallowedAreaForEndBounds.set(disallowedAreaForEndBounds); mDragStartListener = dragStartListener; + mTransactionSupplier = supplier; } @Override @@ -60,47 +71,66 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds()); mRepositionStartPoint.set(x, y); mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId); + mRepositionTaskBounds.set(mTaskBoundsAtDragStart); } @Override public void onDragPositioningMove(float x, float y) { final WindowContainerTransaction wct = new WindowContainerTransaction(); PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint); - if (DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved, + if (isResizing() && DragPositioningCallbackUtility.changeBounds(mCtrlType, mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta, mDisplayController, mWindowDecoration)) { // The task is being resized, send the |dragResizing| hint to core with the first // bounds-change wct. - if (!mHasMoved && mCtrlType != CTRL_TYPE_UNDEFINED) { + if (!mHasDragResized) { // This is the first bounds change since drag resize operation started. wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */); } DragPositioningCallbackUtility.applyTaskBoundsChange(wct, mWindowDecoration, mRepositionTaskBounds, mTaskOrganizer); - mHasMoved = true; + mHasDragResized = true; + } else if (mCtrlType == CTRL_TYPE_UNDEFINED) { + final SurfaceControl.Transaction t = mTransactionSupplier.get(); + DragPositioningCallbackUtility.setPositionOnDrag(mWindowDecoration, + mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, x, y); + t.apply(); } } @Override public void onDragPositioningEnd(float x, float y) { - // |mHasMoved| being false means there is no real change to the task bounds in WM core, so - // we don't need a WCT to finish it. - if (mHasMoved) { + // If task has been resized or task was dragged into area outside of + // mDisallowedAreaForEndBounds, apply WCT to finish it. + if (isResizing() && mHasDragResized) { final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */); PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint); - if (DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved, - mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta, - mDisplayController, mWindowDecoration)) { + if (DragPositioningCallbackUtility.changeBounds(mCtrlType, mRepositionTaskBounds, + mTaskBoundsAtDragStart, mStableBounds, delta, mDisplayController, + mWindowDecoration)) { wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds); } mTaskOrganizer.applyTransaction(wct); + } else if (mCtrlType == CTRL_TYPE_UNDEFINED + && !mDisallowedAreaForEndBounds.contains((int) x, (int) y)) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds, + mTaskBoundsAtDragStart, mRepositionStartPoint, x, y); + wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds); + mTaskOrganizer.applyTransaction(wct); } mTaskBoundsAtDragStart.setEmpty(); mRepositionStartPoint.set(0, 0); mCtrlType = CTRL_TYPE_UNDEFINED; - mHasMoved = false; + mHasDragResized = false; } + + private boolean isResizing() { + return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0 + || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0; + } + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java index 1d416c65851b..5253fb84a4be 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java @@ -18,11 +18,14 @@ package com.android.wm.shell.windowdecor; import android.graphics.PointF; import android.graphics.Rect; +import android.view.SurfaceControl; import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import java.util.function.Supplier; + /** * A task positioner that also takes into account resizing a * {@link com.android.wm.shell.windowdecor.ResizeVeil}. @@ -39,17 +42,32 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback { private final Rect mTaskBoundsAtDragStart = new Rect(); private final PointF mRepositionStartPoint = new PointF(); private final Rect mRepositionTaskBounds = new Rect(); + // If a task move (not resize) finishes in this region, the positioner will not attempt to + // finalize the bounds there using WCT#setBounds + private final Rect mDisallowedAreaForEndBounds = new Rect(); + private final Supplier<SurfaceControl.Transaction> mTransactionSupplier; + private boolean mHasDragResized; private int mCtrlType; - private boolean mHasMoved; - public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, DesktopModeWindowDecoration windowDecoration, DisplayController displayController, + Rect disallowedAreaForEndBounds, DragPositioningCallbackUtility.DragStartListener dragStartListener) { + this(taskOrganizer, windowDecoration, displayController, disallowedAreaForEndBounds, + dragStartListener, SurfaceControl.Transaction::new); + } + + public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, + DesktopModeWindowDecoration windowDecoration, DisplayController displayController, + Rect disallowedAreaForEndBounds, + DragPositioningCallbackUtility.DragStartListener dragStartListener, + Supplier<SurfaceControl.Transaction> supplier) { mTaskOrganizer = taskOrganizer; mDesktopWindowDecoration = windowDecoration; mDisplayController = displayController; mDragStartListener = dragStartListener; + mDisallowedAreaForEndBounds.set(disallowedAreaForEndBounds); + mTransactionSupplier = supplier; } @Override @@ -58,27 +76,28 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback { mTaskBoundsAtDragStart.set( mDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds()); mRepositionStartPoint.set(x, y); - if (mCtrlType != CTRL_TYPE_UNDEFINED) { + if (isResizing()) { mDesktopWindowDecoration.showResizeVeil(); } - mHasMoved = false; + mHasDragResized = false; mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId); + mRepositionTaskBounds.set(mTaskBoundsAtDragStart); } @Override public void onDragPositioningMove(float x, float y) { PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint); - if (DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved, + if (isResizing() && DragPositioningCallbackUtility.changeBounds(mCtrlType, mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta, mDisplayController, mDesktopWindowDecoration)) { - if (mCtrlType != CTRL_TYPE_UNDEFINED) { - mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds); - } else { - DragPositioningCallbackUtility.applyTaskBoundsChange( - new WindowContainerTransaction(), mDesktopWindowDecoration, - mRepositionTaskBounds, mTaskOrganizer); - } - mHasMoved = true; + mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds); + mHasDragResized = true; + } else if (mCtrlType == CTRL_TYPE_UNDEFINED) { + final SurfaceControl.Transaction t = mTransactionSupplier.get(); + DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration, + mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, + x, y); + t.apply(); } } @@ -86,22 +105,35 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback { public void onDragPositioningEnd(float x, float y) { PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint); - if (mHasMoved) { - DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved, - mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta, - mDisplayController, mDesktopWindowDecoration); - DragPositioningCallbackUtility.applyTaskBoundsChange( - new WindowContainerTransaction(), mDesktopWindowDecoration, - mRepositionTaskBounds, mTaskOrganizer); - } - // TODO: (b/279062291) Synchronize the start of hide to the end of the draw triggered above. - if (mCtrlType != CTRL_TYPE_UNDEFINED) { + if (isResizing()) { + if (mHasDragResized) { + DragPositioningCallbackUtility.changeBounds( + mCtrlType, mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, + delta, mDisplayController, mDesktopWindowDecoration); + DragPositioningCallbackUtility.applyTaskBoundsChange( + new WindowContainerTransaction(), mDesktopWindowDecoration, + mRepositionTaskBounds, mTaskOrganizer); + } + // TODO: (b/279062291) Synchronize the start of hide to the end of the draw triggered + // above. mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds); mDesktopWindowDecoration.hideResizeVeil(); + } else if (!mDisallowedAreaForEndBounds.contains((int) x, (int) y)) { + DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds, + mTaskBoundsAtDragStart, mRepositionStartPoint, x, y); + DragPositioningCallbackUtility.applyTaskBoundsChange(new WindowContainerTransaction(), + mDesktopWindowDecoration, mRepositionTaskBounds, mTaskOrganizer); } + mCtrlType = CTRL_TYPE_UNDEFINED; mTaskBoundsAtDragStart.setEmpty(); mRepositionStartPoint.set(0, 0); - mHasMoved = false; + mHasDragResized = false; } + + private boolean isResizing() { + return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0 + || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0; + } + } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java index 265b10df1945..0d0a08cb0ffb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java @@ -31,6 +31,7 @@ import android.app.ActivityManager; import android.app.WindowConfiguration; import android.content.Context; import android.content.res.Resources; +import android.graphics.Point; import android.os.IBinder; import android.util.DisplayMetrics; import android.view.SurfaceControl; @@ -75,6 +76,7 @@ public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase { @Mock ShellExecutor mExecutor; + private Point mPoint; private ExitDesktopTaskTransitionHandler mExitDesktopTaskTransitionHandler; @Before @@ -90,6 +92,7 @@ public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase { mExitDesktopTaskTransitionHandler = new ExitDesktopTaskTransitionHandler(mTransitions, mContext); + mPoint = new Point(0, 0); } @Test @@ -100,7 +103,8 @@ public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase { doReturn(mToken).when(mTransitions) .startTransition(transitionType, wct, mExitDesktopTaskTransitionHandler); - mExitDesktopTaskTransitionHandler.startTransition(transitionType, wct, null); + mExitDesktopTaskTransitionHandler.startTransition(transitionType, wct, mPoint, + null); TransitionInfo.Change change = createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FULLSCREEN); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java index 9a90996b786c..4c27706a39a3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -29,6 +30,7 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.WindowConfiguration; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.input.InputManager; @@ -47,6 +49,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -68,6 +71,7 @@ import java.util.function.Supplier; public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { private static final String TAG = "DesktopModeWindowDecorViewModelTests"; + private static final Rect STABLE_INSETS = new Rect(0, 100, 0, 0); @Mock private DesktopModeWindowDecoration mDesktopModeWindowDecoration; @Mock private DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory; @@ -76,6 +80,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { @Mock private Choreographer mMainChoreographer; @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private DisplayController mDisplayController; + @Mock private DisplayLayout mDisplayLayout; @Mock private SplitScreenController mSplitScreenController; @Mock private SyncTransactionQueue mSyncQueue; @Mock private DesktopModeController mDesktopModeController; @@ -113,6 +118,8 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { .when(mDesktopModeWindowDecorFactory) .create(any(), any(), any(), any(), any(), any(), any(), any()); doReturn(mTransaction).when(mTransactionFactory).get(); + doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt()); + doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets(); when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor); // InputChannel cannot be mocked because it passes to InputEventReceiver. diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt index 348b3659e864..de46b31879ed 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt @@ -85,7 +85,7 @@ class DragPositioningCallbackUtilityTest { @Test fun testChangeBoundsDoesNotChangeHeightWhenLessThanMin() { val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) - val repositionTaskBounds = Rect() + val repositionTaskBounds = Rect(STARTING_BOUNDS) // Resize to width of 95px and height of 5px with min width of 10px val newX = STARTING_BOUNDS.right.toFloat() - 5 @@ -93,8 +93,8 @@ class DragPositioningCallbackUtilityTest { val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) @@ -105,7 +105,7 @@ class DragPositioningCallbackUtilityTest { @Test fun testChangeBoundsDoesNotChangeWidthWhenLessThanMin() { val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) - val repositionTaskBounds = Rect() + val repositionTaskBounds = Rect(STARTING_BOUNDS) // Resize to height of 95px and width of 5px with min width of 10px val newX = STARTING_BOUNDS.right.toFloat() - 95 @@ -113,8 +113,8 @@ class DragPositioningCallbackUtilityTest { val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 5) @@ -125,7 +125,7 @@ class DragPositioningCallbackUtilityTest { @Test fun testChangeBoundsDoesNotChangeHeightWhenNegative() { val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) - val repositionTaskBounds = Rect() + val repositionTaskBounds = Rect(STARTING_BOUNDS) // Resize to width of 95px and width of -5px with minimum of 10px val newX = STARTING_BOUNDS.right.toFloat() - 5 @@ -133,8 +133,8 @@ class DragPositioningCallbackUtilityTest { val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) @@ -145,7 +145,7 @@ class DragPositioningCallbackUtilityTest { @Test fun testChangeBoundsRunsWhenResizeBoundsValid() { val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) - val repositionTaskBounds = Rect() + val repositionTaskBounds = Rect(STARTING_BOUNDS) // Shrink to height 20px and width 20px with both min height/width equal to 10px val newX = STARTING_BOUNDS.right.toFloat() - 80 @@ -153,7 +153,7 @@ class DragPositioningCallbackUtilityTest { val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 80) @@ -164,7 +164,7 @@ class DragPositioningCallbackUtilityTest { @Test fun testChangeBoundsDoesNotRunWithNegativeHeightAndWidth() { val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) - val repositionTaskBounds = Rect() + val repositionTaskBounds = Rect(STARTING_BOUNDS) // Shrink to height -5px and width -5px with both min height/width equal to 10px val newX = STARTING_BOUNDS.right.toFloat() - 105 val newY = STARTING_BOUNDS.top.toFloat() + 105 @@ -172,7 +172,7 @@ class DragPositioningCallbackUtilityTest { val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) @@ -185,21 +185,21 @@ class DragPositioningCallbackUtilityTest { var hasMoved = false val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat()) - val repositionTaskBounds = Rect() + val repositionTaskBounds = Rect(STARTING_BOUNDS) // Initial resize to width and height 110px. var newX = STARTING_BOUNDS.right.toFloat() + 10 var newY = STARTING_BOUNDS.bottom.toFloat() + 10 var delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, - hasMoved, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration)) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration)) hasMoved = true // Resize width to 120px, height to disallowed area which should not result in a change. newX += 10 newY = DISALLOWED_RESIZE_AREA.top.toFloat() delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, - hasMoved, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration)) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt index 5bea8f2d1c45..282a19e8e9a5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt @@ -6,14 +6,16 @@ import android.graphics.Rect import android.os.IBinder import android.testing.AndroidTestingRunner import android.view.Display +import android.view.SurfaceControl 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.common.DisplayController +import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED @@ -21,12 +23,14 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` as whenever 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 +import java.util.function.Supplier +import org.mockito.Mockito.`when` as whenever /** * Tests for [FluidResizeTaskPositioner]. @@ -56,6 +60,10 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { private lateinit var mockDisplayLayout: DisplayLayout @Mock private lateinit var mockDisplay: Display + @Mock + private lateinit var mockTransactionFactory: Supplier<SurfaceControl.Transaction> + @Mock + private lateinit var mockTransaction: SurfaceControl.Transaction private lateinit var taskPositioner: FluidResizeTaskPositioner @@ -68,8 +76,10 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { mockShellTaskOrganizer, mockWindowDecoration, mockDisplayController, - mockDragStartListener - ) + DISALLOWED_AREA_FOR_END_BOUNDS, + mockDragStartListener, + mockTransactionFactory + ) whenever(taskToken.asBinder()).thenReturn(taskBinder) whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) @@ -77,6 +87,8 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> (i.arguments.first() as Rect).set(STABLE_BOUNDS) } + `when`(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS) + `when`(mockTransactionFactory.get()).thenReturn(mockTransaction) mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = TASK_ID @@ -236,6 +248,318 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { }) } + @Test + fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenLessThanMin() { + taskPositioner.onDragPositioningStart( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top + STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // Resize to width of 95px and height of 5px with min width of 10px + val newX = STARTING_BOUNDS.right.toFloat() - 5 + val newY = STARTING_BOUNDS.top.toFloat() + 95 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) + != 0) && change.configuration.windowConfiguration.bounds.top == + STARTING_BOUNDS.top && + change.configuration.windowConfiguration.bounds.bottom == + STARTING_BOUNDS.bottom + } + }) + } + + @Test + fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenLessThanMin() { + taskPositioner.onDragPositioningStart( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top + STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // Resize to height of 95px and width of 5px with min width of 10px + val newX = STARTING_BOUNDS.right.toFloat() - 95 + val newY = STARTING_BOUNDS.top.toFloat() + 5 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) + != 0) && change.configuration.windowConfiguration.bounds.right == + STARTING_BOUNDS.right && + change.configuration.windowConfiguration.bounds.left == + STARTING_BOUNDS.left + } + }) + } + + @Test + fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenNegative() { + taskPositioner.onDragPositioningStart( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top + STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // Resize to height of -5px and width of 95px + val newX = STARTING_BOUNDS.right.toFloat() - 5 + val newY = STARTING_BOUNDS.top.toFloat() + 105 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) + != 0) && change.configuration.windowConfiguration.bounds.top == + STARTING_BOUNDS.top && + change.configuration.windowConfiguration.bounds.bottom == + STARTING_BOUNDS.bottom + } + }) + } + + @Test + fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenNegative() { + taskPositioner.onDragPositioningStart( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top + STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // Resize to width of -5px and height of 95px + val newX = STARTING_BOUNDS.right.toFloat() - 105 + val newY = STARTING_BOUNDS.top.toFloat() + 5 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) + != 0) && change.configuration.windowConfiguration.bounds.right == + STARTING_BOUNDS.right && + change.configuration.windowConfiguration.bounds.left == + STARTING_BOUNDS.left + } + }) + } + + @Test + fun testDragResize_resize_setBoundsRunsWhenResizeBoundsValid() { + taskPositioner.onDragPositioningStart( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top + STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // Shrink to height 20px and width 20px with both min height/width equal to 10px + val newX = STARTING_BOUNDS.right.toFloat() - 80 + val newY = STARTING_BOUNDS.top.toFloat() + 80 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) + } + }) + } + + @Test + fun testDragResize_resize_setBoundsDoesNotRunWithNegativeHeightAndWidth() { + taskPositioner.onDragPositioningStart( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top + STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // Shrink to height 5px and width 5px with both min height/width equal to 10px + val newX = STARTING_BOUNDS.right.toFloat() - 95 + val newY = STARTING_BOUNDS.top.toFloat() + 95 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) + } + }) + } + + @Test + fun testDragResize_resize_useDefaultMinWhenMinWidthInvalid() { + mockWindowDecoration.mTaskInfo.minWidth = -1 + + taskPositioner.onDragPositioningStart( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top + STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // Shrink to width and height of 3px with invalid minWidth = -1 and defaultMinSize = 5px + val newX = STARTING_BOUNDS.right.toFloat() - 97 + val newY = STARTING_BOUNDS.top.toFloat() + 97 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) + } + }) + } + + @Test + fun testDragResize_resize_useMinWidthWhenValid() { + taskPositioner.onDragPositioningStart( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top + STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // Shrink to width and height of 7px with valid minWidth = 10px and defaultMinSize = 5px + val newX = STARTING_BOUNDS.right.toFloat() - 93 + val newY = STARTING_BOUNDS.top.toFloat() + 93 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) + } + }) + } + + 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. + ) + ) + } + }) + } + + @Test + fun testDragResize_drag_setBoundsNotRunIfDragEndsInDisallowedEndArea() { + taskPositioner.onDragPositioningStart( + CTRL_TYPE_UNDEFINED, // drag + STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + val newX = STARTING_BOUNDS.right.toFloat() + 5 + val newY = STARTING_BOUNDS.top.toFloat() + 5 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) + } + }) + } + private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean { return ((windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) && bounds == configuration.windowConfiguration.bounds @@ -251,6 +575,8 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { 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 STABLE_INSETS = Rect(0, 50, 0, 0) + private val DISALLOWED_AREA_FOR_END_BOUNDS = Rect(0, 0, 300, 300) private val DISALLOWED_RESIZE_AREA = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt index 498082bd53e5..217bdbb07447 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt @@ -21,12 +21,13 @@ import android.graphics.Rect import android.os.IBinder import android.testing.AndroidTestingRunner import android.view.Display +import android.view.SurfaceControl import android.window.WindowContainerToken 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.common.DisplayController +import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED @@ -34,13 +35,16 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.any import org.mockito.Mockito.argThat +import org.mockito.Mockito.eq import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import java.util.function.Supplier +import org.mockito.Mockito.`when` as whenever /** * Tests for [VeiledResizeTaskPositioner]. @@ -70,6 +74,10 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { private lateinit var mockDisplayLayout: DisplayLayout @Mock private lateinit var mockDisplay: Display + @Mock + private lateinit var mockTransactionFactory: Supplier<SurfaceControl.Transaction> + @Mock + private lateinit var mockTransaction: SurfaceControl.Transaction private lateinit var taskPositioner: VeiledResizeTaskPositioner @@ -82,7 +90,9 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { mockShellTaskOrganizer, mockDesktopWindowDecoration, mockDisplayController, - mockDragStartListener + DISALLOWED_AREA_FOR_END_BOUNDS, + mockDragStartListener, + mockTransactionFactory ) whenever(taskToken.asBinder()).thenReturn(taskBinder) @@ -91,6 +101,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> (i.arguments.first() as Rect).set(STABLE_BOUNDS) } + `when`(mockTransactionFactory.get()).thenReturn(mockTransaction) mockDesktopWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = TASK_ID @@ -125,36 +136,31 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { fun testDragResize_movesTask_doesNotShowResizeVeil() { taskPositioner.onDragPositioningStart( CTRL_TYPE_UNDEFINED, - STARTING_BOUNDS.left.toFloat() + 50, + STARTING_BOUNDS.left.toFloat(), STARTING_BOUNDS.top.toFloat() ) taskPositioner.onDragPositioningMove( STARTING_BOUNDS.left.toFloat() + 60, - STARTING_BOUNDS.top.toFloat() + 10 + STARTING_BOUNDS.top.toFloat() + 100 ) val rectAfterMove = Rect(STARTING_BOUNDS) - rectAfterMove.left += 10 - rectAfterMove.right += 10 - rectAfterMove.top += 10 - rectAfterMove.bottom += 10 - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && - (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && - change.configuration.windowConfiguration.bounds == rectAfterMove - } - }) + rectAfterMove.left += 60 + rectAfterMove.right += 60 + rectAfterMove.top += 100 + rectAfterMove.bottom += 100 + verify(mockTransaction).setPosition(any(), eq(rectAfterMove.left.toFloat()), + eq(rectAfterMove.top.toFloat())) taskPositioner.onDragPositioningEnd( STARTING_BOUNDS.left.toFloat() + 70, STARTING_BOUNDS.top.toFloat() + 20 ) - val rectAfterEnd = Rect(rectAfterMove) - rectAfterEnd.left += 10 - rectAfterEnd.top += 10 - rectAfterEnd.right += 10 - rectAfterEnd.bottom += 10 + val rectAfterEnd = Rect(STARTING_BOUNDS) + rectAfterEnd.left += 70 + rectAfterEnd.right += 70 + rectAfterEnd.top += 20 + rectAfterEnd.bottom += 20 verify(mockDesktopWindowDecoration, never()).createResizeVeil() verify(mockDesktopWindowDecoration, never()).hideResizeVeil() @@ -239,6 +245,32 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { }) } + + @Test + fun testDragResize_drag_setBoundsNotRunIfDragEndsInDisallowedEndArea() { + taskPositioner.onDragPositioningStart( + CTRL_TYPE_UNDEFINED, // drag + STARTING_BOUNDS.left.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + val newX = STARTING_BOUNDS.left.toFloat() + 5 + val newY = STARTING_BOUNDS.top.toFloat() + 5 + taskPositioner.onDragPositioningMove( + newX, + newY + ) + + taskPositioner.onDragPositioningEnd(newX, newY) + + verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) + } + }) + } + companion object { private const val TASK_ID = 5 private const val MIN_WIDTH = 10 @@ -249,6 +281,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { 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_AREA_FOR_END_BOUNDS = Rect(0, 0, 50, 50) private val STABLE_BOUNDS = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.top, |