diff options
14 files changed, 452 insertions, 133 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 71bf487249fb..0ef047f44909 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -235,7 +235,8 @@ public abstract class WMShellModule { mainChoreographer, taskOrganizer, displayController, - syncQueue); + syncQueue, + transitions); } // 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 cf1692018518..cebc4006656a 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 @@ -54,6 +54,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private final Choreographer mMainChoreographer; private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; + private final Transitions mTransitions; private TaskOperations mTaskOperations; private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>(); @@ -64,13 +65,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { Choreographer mainChoreographer, ShellTaskOrganizer taskOrganizer, DisplayController displayController, - SyncTransactionQueue syncQueue) { + SyncTransactionQueue syncQueue, + Transitions transitions) { mContext = context; mMainHandler = mainHandler; mMainChoreographer = mainChoreographer; mTaskOrganizer = taskOrganizer; mDisplayController = displayController; mSyncQueue = syncQueue; + mTransitions = transitions; if (!Transitions.ENABLE_SHELL_TRANSITIONS) { mTaskOperations = new TaskOperations(null, mContext, mSyncQueue); } @@ -133,7 +136,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { if (decoration == null) { createWindowDecoration(taskInfo, taskSurface, startT, finishT); } else { - decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */); + decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, + false /* setTaskCropAndPosition */); } } @@ -145,7 +149,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId); if (decoration == null) return; - decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */); + decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, + false /* setTaskCropAndPosition */); } @Override @@ -191,16 +196,17 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mSyncQueue); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); - final DragPositioningCallback dragPositioningCallback = - new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController, - 0 /* disallowedAreaForEndBoundsHeight */); + final FluidResizeTaskPositioner taskPositioner = + new FluidResizeTaskPositioner(mTaskOrganizer, mTransitions, windowDecoration, + mDisplayController, 0 /* disallowedAreaForEndBoundsHeight */); final CaptionTouchEventListener touchEventListener = - new CaptionTouchEventListener(taskInfo, dragPositioningCallback); + new CaptionTouchEventListener(taskInfo, taskPositioner); windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); - windowDecoration.setDragPositioningCallback(dragPositioningCallback); + windowDecoration.setDragPositioningCallback(taskPositioner); windowDecoration.setDragDetector(touchEventListener.mDragDetector); + windowDecoration.setTaskDragResizer(taskPositioner); windowDecoration.relayout(taskInfo, startT, finishT, - false /* applyStartTransactionOnDraw */); + false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */); setupCaptionColor(taskInfo, windowDecoration); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 6e7d11d9082b..1debb02e86af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -157,15 +157,21 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL @Override void relayout(RunningTaskInfo taskInfo) { final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + // The crop and position of the task should only be set when a task is fluid resizing. In + // all other cases, it is expected that the transition handler positions and crops the task + // in order to allow the handler time to animate before the task before the final + // position and crop are set. + final boolean shouldSetTaskPositionAndCrop = mTaskDragResizer.isResizingOrAnimating(); // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is // synced with the buffer transaction (that draws the View). Both will be shown on screen // at the same, whereas applying them independently causes flickering. See b/270202228. - relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */); + relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */, + shouldSetTaskPositionAndCrop); } void relayout(RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, - boolean applyStartTransactionOnDraw) { + boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition) { final int shadowRadiusID = taskInfo.isFocused ? R.dimen.freeform_decor_shadow_focused_thickness : R.dimen.freeform_decor_shadow_unfocused_thickness; @@ -183,6 +189,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode()); mRelayoutParams.mShadowRadiusId = shadowRadiusID; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; + mRelayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition; relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo 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 ab29df1f780c..4fd362591151 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 @@ -335,7 +335,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (decoration == null) { createWindowDecoration(taskInfo, taskSurface, startT, finishT); } else { - decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */); + decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, + false /* shouldSetTaskPositionAndCrop */); } } @@ -347,7 +348,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId); if (decoration == null) return; - decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */); + decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, + false /* shouldSetTaskPositionAndCrop */); } @Override @@ -1010,8 +1012,23 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); windowDecoration.createResizeVeil(); - final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback( - windowDecoration); + final DragPositioningCallback dragPositioningCallback; + final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize( + R.dimen.desktop_mode_transition_area_height); + if (!DesktopModeStatus.isVeiledResizeEnabled()) { + dragPositioningCallback = new FluidResizeTaskPositioner( + mTaskOrganizer, mTransitions, windowDecoration, mDisplayController, + mDragStartListener, mTransactionFactory, transitionAreaHeight); + windowDecoration.setTaskDragResizer( + (FluidResizeTaskPositioner) dragPositioningCallback); + } else { + dragPositioningCallback = new VeiledResizeTaskPositioner( + mTaskOrganizer, windowDecoration, mDisplayController, + mDragStartListener, mTransitions, transitionAreaHeight); + windowDecoration.setTaskDragResizer( + (VeiledResizeTaskPositioner) dragPositioningCallback); + } + final DesktopModeTouchEventListener touchEventListener = new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback); @@ -1021,23 +1038,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { windowDecoration.setDragPositioningCallback(dragPositioningCallback); windowDecoration.setDragDetector(touchEventListener.mDragDetector); windowDecoration.relayout(taskInfo, startT, finishT, - false /* applyStartTransactionOnDraw */); + false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */); incrementEventReceiverTasks(taskInfo.displayId); } - private DragPositioningCallback createDragPositioningCallback( - @NonNull DesktopModeWindowDecoration windowDecoration) { - final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.desktop_mode_transition_area_height); - if (!DesktopModeStatus.isVeiledResizeEnabled()) { - return new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, - mDisplayController, mDragStartListener, mTransactionFactory, - transitionAreaHeight); - } else { - return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration, - mDisplayController, mDragStartListener, mTransitions, - transitionAreaHeight); - } - } private RunningTaskInfo getOtherSplitTask(int taskId) { @SplitPosition int remainingTaskPosition = mSplitScreenController @@ -1138,7 +1141,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } } - } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 2f51278e3c80..0c8e93b48d02 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -187,20 +187,28 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); + // The crop and position of the task should only be set when a task is fluid resizing. In + // all other cases, it is expected that the transition handler positions and crops the task + // in order to allow the handler time to animate before the task before the final + // position and crop are set. + final boolean shouldSetTaskPositionAndCrop = !DesktopModeStatus.isVeiledResizeEnabled() + && mTaskDragResizer.isResizingOrAnimating(); // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is // synced with the buffer transaction (that draws the View). Both will be shown on screen // at the same, whereas applying them independently causes flickering. See b/270202228. - relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */); + relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */, + shouldSetTaskPositionAndCrop); } void relayout(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, - boolean applyStartTransactionOnDraw) { + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { if (isHandleMenuActive()) { mHandleMenu.relayout(startT); } - updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw); + updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw, + shouldSetTaskPositionAndCrop); final WindowDecorLinearLayout oldRootView = mResult.mRootView; final SurfaceControl oldDecorationSurface = mDecorationContainerSurface; @@ -302,7 +310,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin RelayoutParams relayoutParams, Context context, ActivityManager.RunningTaskInfo taskInfo, - boolean applyStartTransactionOnDraw) { + boolean applyStartTransactionOnDraw, + boolean shouldSetTaskPositionAndCrop) { relayoutParams.reset(); relayoutParams.mRunningTaskInfo = taskInfo; relayoutParams.mLayoutResId = @@ -314,6 +323,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin : R.dimen.freeform_decor_shadow_unfocused_thickness; } relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; + relayoutParams.mSetTaskPositionAndCrop = shouldSetTaskPositionAndCrop; // The configuration used to lay out the window decoration. The system context's config is // used when the task density has been overridden to a custom density so that the resources // and views of the decoration aren't affected and match the rest of the System UI, if not 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 677c7f1fb5a8..5afbd54088d1 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 @@ -26,9 +26,7 @@ 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; import com.android.wm.shell.common.DisplayController; /** @@ -130,8 +128,7 @@ public class DragPositioningCallbackUtility { 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); + t.setPosition(decoration.mTaskSurface, repositionTaskBounds.left, repositionTaskBounds.top); } private static void updateTaskBounds(Rect repositionTaskBounds, Rect taskBoundsAtDragStart, @@ -188,18 +185,6 @@ public class DragPositioningCallbackUtility { } } - /** - * Apply a bounds change to a task. - * @param windowDecoration decor of task we are changing bounds for - * @param taskBounds new bounds of this task - * @param taskOrganizer applies the provided WindowContainerTransaction - */ - static void applyTaskBoundsChange(WindowContainerTransaction wct, - WindowDecoration windowDecoration, Rect taskBounds, ShellTaskOrganizer taskOrganizer) { - wct.setBounds(windowDecoration.mTaskInfo.token, taskBounds); - taskOrganizer.applyTransaction(wct); - } - private static float getMinWidth(DisplayController displayController, WindowDecoration windowDecoration) { return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinSize(displayController, 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 5d006fb4d9e2..6bfc7cdcb33e 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 @@ -16,23 +16,42 @@ package com.android.wm.shell.windowdecor; +import static android.view.WindowManager.TRANSIT_CHANGE; + import android.graphics.PointF; import android.graphics.Rect; +import android.os.IBinder; import android.view.Surface; import android.view.SurfaceControl; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.transition.Transitions; 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. + * + * This positioner applies the final bounds after a resize or drag using a shell transition in order + * to utilize the startAnimation callback to set the final task position and crop. In most cases, + * the transition will be aborted since the final bounds are usually the same bounds set in the + * final {@link #onDragPositioningMove} call. In this case, the cropping and positioning would be + * set by {@link WindowDecoration#relayout} due to the final bounds change; however, it is important + * that we send the final shell transition since we still utilize the {@link #onTransitionConsumed} + * callback. */ -class FluidResizeTaskPositioner implements DragPositioningCallback { +class FluidResizeTaskPositioner implements DragPositioningCallback, + TaskDragResizer, Transitions.TransitionHandler { private final ShellTaskOrganizer mTaskOrganizer; + private final Transitions mTransitions; private final WindowDecoration mWindowDecoration; private final Supplier<SurfaceControl.Transaction> mTransactionSupplier; private DisplayController mDisplayController; @@ -45,21 +64,28 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { // finalize the bounds there using WCT#setBounds private final int mDisallowedAreaForEndBoundsHeight; private boolean mHasDragResized; + private boolean mIsResizingOrAnimatingResize; private int mCtrlType; + private IBinder mDragResizeEndTransition; @Surface.Rotation private int mRotation; - FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration, - DisplayController displayController, int disallowedAreaForEndBoundsHeight) { - this(taskOrganizer, windowDecoration, displayController, dragStartListener -> {}, - SurfaceControl.Transaction::new, disallowedAreaForEndBoundsHeight); + FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, Transitions transitions, + WindowDecoration windowDecoration, DisplayController displayController, + int disallowedAreaForEndBoundsHeight) { + this(taskOrganizer, transitions, windowDecoration, displayController, + dragStartListener -> {}, SurfaceControl.Transaction::new, + disallowedAreaForEndBoundsHeight); } - FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration, + FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, + Transitions transitions, + WindowDecoration windowDecoration, DisplayController displayController, DragPositioningCallbackUtility.DragStartListener dragStartListener, Supplier<SurfaceControl.Transaction> supplier, int disallowedAreaForEndBoundsHeight) { mTaskOrganizer = taskOrganizer; + mTransitions = transitions; mWindowDecoration = windowDecoration; mDisplayController = displayController; mDragStartListener = dragStartListener; @@ -103,9 +129,10 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { // This is the first bounds change since drag resize operation started. wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */); } - DragPositioningCallbackUtility.applyTaskBoundsChange(wct, mWindowDecoration, - mRepositionTaskBounds, mTaskOrganizer); + wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds); + mTaskOrganizer.applyTransaction(wct); mHasDragResized = true; + mIsResizingOrAnimatingResize = true; } else if (mCtrlType == CTRL_TYPE_UNDEFINED) { final SurfaceControl.Transaction t = mTransactionSupplier.get(); DragPositioningCallbackUtility.setPositionOnDrag(mWindowDecoration, @@ -129,7 +156,7 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { mWindowDecoration)) { wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds); } - mTaskOrganizer.applyTransaction(wct); + mDragResizeEndTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this); } else if (mCtrlType == CTRL_TYPE_UNDEFINED && DragPositioningCallbackUtility.isBelowDisallowedArea( mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint, @@ -139,7 +166,7 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { mTaskBoundsAtDragStart, mRepositionStartPoint, x, y, mWindowDecoration.calculateValidDragArea()); wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds); - mTaskOrganizer.applyTransaction(wct); + mTransitions.startTransition(TRANSIT_CHANGE, wct, this); } mTaskBoundsAtDragStart.setEmpty(); @@ -154,4 +181,51 @@ class FluidResizeTaskPositioner implements DragPositioningCallback { || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0; } + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + for (TransitionInfo.Change change: info.getChanges()) { + final SurfaceControl sc = change.getLeash(); + final Rect endBounds = change.getEndAbsBounds(); + startTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height()) + .setPosition(sc, endBounds.left, endBounds.top); + finishTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height()) + .setPosition(sc, endBounds.left, endBounds.top); + } + + startTransaction.apply(); + if (transition.equals(mDragResizeEndTransition)) { + mIsResizingOrAnimatingResize = false; + mDragResizeEndTransition = null; + } + finishCallback.onTransitionFinished(null); + return true; + } + + /** + * We should never reach this as this handler's transitions are only started from shell + * explicitly. + */ + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + return null; + } + + @Override + public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, + @Nullable SurfaceControl.Transaction finishTransaction) { + if (transition.equals(mDragResizeEndTransition)) { + mIsResizingOrAnimatingResize = false; + mDragResizeEndTransition = null; + } + } + + @Override + public boolean isResizingOrAnimating() { + return mIsResizingOrAnimatingResize; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java new file mode 100644 index 000000000000..40421b599889 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.windowdecor; + +/** + * Holds the state of a drag resize. + */ +interface TaskDragResizer { + + /** + * Returns true if task is currently being resized or animating the final transition after + * a resize is complete. + */ + boolean isResizingOrAnimating(); +} 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 4363558ca00b..c1b18f959641 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 @@ -43,7 +43,7 @@ import java.util.function.Supplier; * If the drag is repositioning, we update in the typical manner. */ public class VeiledResizeTaskPositioner implements DragPositioningCallback, - Transitions.TransitionHandler { + TaskDragResizer, Transitions.TransitionHandler { private DesktopModeWindowDecoration mDesktopWindowDecoration; private ShellTaskOrganizer mTaskOrganizer; @@ -59,10 +59,12 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, private final int mDisallowedAreaForEndBoundsHeight; private final Supplier<SurfaceControl.Transaction> mTransactionSupplier; private int mCtrlType; + private boolean mIsResizingOrAnimatingResize; @Surface.Rotation private int mRotation; public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, - DesktopModeWindowDecoration windowDecoration, DisplayController displayController, + DesktopModeWindowDecoration windowDecoration, + DisplayController displayController, DragPositioningCallbackUtility.DragStartListener dragStartListener, Transitions transitions, int disallowedAreaForEndBoundsHeight) { @@ -71,12 +73,13 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, } public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, - DesktopModeWindowDecoration windowDecoration, DisplayController displayController, + DesktopModeWindowDecoration windowDecoration, + DisplayController displayController, DragPositioningCallbackUtility.DragStartListener dragStartListener, Supplier<SurfaceControl.Transaction> supplier, Transitions transitions, int disallowedAreaForEndBoundsHeight) { - mTaskOrganizer = taskOrganizer; mDesktopWindowDecoration = windowDecoration; + mTaskOrganizer = taskOrganizer; mDisplayController = displayController; mDragStartListener = dragStartListener; mTransactionSupplier = supplier; @@ -117,6 +120,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta, mDisplayController, mDesktopWindowDecoration)) { mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds); + mIsResizingOrAnimatingResize = true; } else if (mCtrlType == CTRL_TYPE_UNDEFINED) { final SurfaceControl.Transaction t = mTransactionSupplier.get(); DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration, @@ -138,24 +142,22 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds); final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds); - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mTransitions.startTransition(TRANSIT_CHANGE, wct, this); - } else { - mTaskOrganizer.applyTransaction(wct); - } + mTransitions.startTransition(TRANSIT_CHANGE, wct, this); } else { // If bounds haven't changed, perform necessary veil reset here as startAnimation // won't be called. mDesktopWindowDecoration.hideResizeVeil(); + mIsResizingOrAnimatingResize = false; } } else if (DragPositioningCallbackUtility.isBelowDisallowedArea( mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint, y)) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, x, y, mDesktopWindowDecoration.calculateValidDragArea()); - DragPositioningCallbackUtility.applyTaskBoundsChange(new WindowContainerTransaction(), - mDesktopWindowDecoration, mRepositionTaskBounds, mTaskOrganizer); + wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds); + mTransitions.startTransition(TRANSIT_CHANGE, wct, this); } mCtrlType = CTRL_TYPE_UNDEFINED; @@ -174,10 +176,20 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + for (TransitionInfo.Change change: info.getChanges()) { + final SurfaceControl sc = change.getLeash(); + final Rect endBounds = change.getEndAbsBounds(); + startTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height()) + .setPosition(sc, endBounds.left, endBounds.top); + finishTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height()) + .setPosition(sc, endBounds.left, endBounds.top); + } + startTransaction.apply(); mDesktopWindowDecoration.hideResizeVeil(); mCtrlType = CTRL_TYPE_UNDEFINED; finishCallback.onTransitionFinished(null); + mIsResizingOrAnimatingResize = false; return true; } @@ -191,4 +203,9 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, @NonNull TransitionRequestInfo request) { return null; } + + @Override + public boolean isResizingOrAnimating() { + return mIsResizingOrAnimatingResize; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index ee0e31ec3aef..b5373c67c602 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -124,6 +124,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> private WindowlessWindowManager mCaptionWindowManager; private SurfaceControlViewHost mViewHost; private Configuration mWindowDecorConfig; + TaskDragResizer mTaskDragResizer; private boolean mIsCaptionVisible; private final Binder mOwner = new Binder(); @@ -311,25 +312,21 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> float shadowRadius; final Point taskPosition = mTaskInfo.positionInParent; if (isFullscreen) { - // Setting the task crop to the width/height stops input events from being sent to - // some regions of the app window. See b/300324920 - // TODO(b/296921174): investigate whether crop/position needs to be set by window - // decorations at all when transition handlers are already taking ownership of the task - // surface placement/crop, especially when in fullscreen where tasks cannot be - // drag-resized by the window decoration. - startT.setWindowCrop(mTaskSurface, null); - finishT.setWindowCrop(mTaskSurface, null); // Shadow is not needed for fullscreen tasks shadowRadius = 0; } else { - startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); - finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); shadowRadius = loadDimension(resources, params.mShadowRadiusId); } + + if (params.mSetTaskPositionAndCrop) { + startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); + finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight) + .setPosition(mTaskSurface, taskPosition.x, taskPosition.y); + } + startT.setShadowRadius(mTaskSurface, shadowRadius) .show(mTaskSurface); - finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y) - .setShadowRadius(mTaskSurface, shadowRadius); + finishT.setShadowRadius(mTaskSurface, shadowRadius); if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { if (!DesktopModeStatus.isVeiledResizeEnabled()) { // When fluid resize is enabled, add a background to freeform tasks @@ -394,6 +391,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } } + void setTaskDragResizer(TaskDragResizer taskDragResizer) { + mTaskDragResizer = taskDragResizer; + } + private void setCaptionVisibility(View rootView, boolean visible) { if (rootView == null) { return; @@ -559,6 +560,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> Configuration mWindowDecorConfig; boolean mApplyStartTransactionOnDraw; + boolean mSetTaskPositionAndCrop; void reset() { mLayoutResId = Resources.ID_NULL; @@ -572,6 +574,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionY = 0; mApplyStartTransactionOnDraw = false; + mSetTaskPositionAndCrop = false; mWindowDecorConfig = null; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index 77667ca579f2..193f16da3e39 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -144,7 +144,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { RelayoutParams relayoutParams = new RelayoutParams(); DesktopModeWindowDecoration.updateRelayoutParams( - relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true); + relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL); } @@ -159,7 +160,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { relayoutParams, mTestableContext, taskInfo, - /* applyStartTransactionOnDraw= */ true); + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); assertThat(relayoutParams.mCornerRadius).isGreaterThan(0); } 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 2ce49cf62614..de6903d9a06a 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 @@ -10,6 +10,7 @@ import android.view.Surface import android.view.Surface.ROTATION_270 import android.view.Surface.ROTATION_90 import android.view.SurfaceControl +import android.view.WindowManager import android.window.WindowContainerToken import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING @@ -18,13 +19,17 @@ 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.transition.Transitions 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 +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito @@ -34,6 +39,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import org.mockito.kotlin.doReturn import java.util.function.Supplier import org.mockito.Mockito.`when` as whenever @@ -50,6 +56,8 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { @Mock private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer @Mock + private lateinit var mockTransitions: Transitions + @Mock private lateinit var mockWindowDecoration: WindowDecoration<*> @Mock private lateinit var mockDragStartListener: DragPositioningCallbackUtility.DragStartListener @@ -69,6 +77,8 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { private lateinit var mockTransactionFactory: Supplier<SurfaceControl.Transaction> @Mock private lateinit var mockTransaction: SurfaceControl.Transaction + @Mock + private lateinit var mockTransitionBinder: IBinder private lateinit var taskPositioner: FluidResizeTaskPositioner @@ -106,9 +116,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { `when`(mockWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA) mockWindowDecoration.mDisplay = mockDisplay whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } + whenever(mockTransitions.startTransition(anyInt(), any(), any())) + .doReturn(mockTransitionBinder) taskPositioner = FluidResizeTaskPositioner( mockShellTaskOrganizer, + mockTransitions, mockWindowDecoration, mockDisplayController, mockDragStartListener, @@ -118,7 +131,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { } @Test - fun testDragResize_notMove_skipsTransactionOnEnd() { + fun testDragResize_notMove_skipsTransitionOnEnd() { taskPositioner.onDragPositioningStart( CTRL_TYPE_TOP or CTRL_TYPE_RIGHT, STARTING_BOUNDS.left.toFloat(), @@ -130,16 +143,16 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { STARTING_BOUNDS.top.toFloat() + 10 ) - verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> + verify(mockTransitions, never()).startTransition( + eq(WindowManager.TRANSIT_CHANGE), argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) - } - }) + }}, eq(taskPositioner)) } @Test - fun testDragResize_noEffectiveMove_skipsTransactionOnMoveAndEnd() { + fun testDragResize_noEffectiveMove_skipsTransitionOnMoveAndEnd() { taskPositioner.onDragPositioningStart( CTRL_TYPE_TOP or CTRL_TYPE_RIGHT, STARTING_BOUNDS.left.toFloat(), @@ -151,21 +164,28 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { STARTING_BOUNDS.top.toFloat() ) + verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) + } + }) + taskPositioner.onDragPositioningEnd( STARTING_BOUNDS.left.toFloat() + 10, STARTING_BOUNDS.top.toFloat() + 10 ) - verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> + verify(mockTransitions, never()).startTransition( + eq(WindowManager.TRANSIT_CHANGE), argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) - } - }) + }}, eq(taskPositioner)) } @Test - fun testDragResize_hasEffectiveMove_issuesTransactionOnMoveAndEnd() { + fun testDragResize_hasEffectiveMove_issuesTransitionOnMoveAndEnd() { taskPositioner.onDragPositioningStart( CTRL_TYPE_TOP or CTRL_TYPE_RIGHT, STARTING_BOUNDS.left.toFloat(), @@ -192,13 +212,13 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { ) val rectAfterEnd = Rect(rectAfterMove) rectAfterEnd.top += 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 == rectAfterEnd - } - }) + verify(mockTransitions).startTransition( + eq(WindowManager.TRANSIT_CHANGE), argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && + change.configuration.windowConfiguration.bounds == rectAfterEnd + }}, eq(taskPositioner)) } @Test @@ -226,6 +246,13 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { change.dragResizing } }) + verify(mockTransitions, never()).startTransition( + eq(WindowManager.TRANSIT_CHANGE), argThat { wct -> + return@argThat wct.changes.any { (token, change) -> + token == taskBinder && + ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) && + change.dragResizing + }}, eq(taskPositioner)) } @Test @@ -253,13 +280,13 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { change.dragResizing } }) - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + verify(mockTransitions).startTransition( + eq(WindowManager.TRANSIT_CHANGE), argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) && !change.dragResizing - } - }) + }}, eq(taskPositioner)) } @Test @@ -270,7 +297,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { STARTING_BOUNDS.top.toFloat() ) - // Resize to width of 95px and height of 5px with min width of 10px + // Resize to width of 95px and height of 5px with min height of 10px val newX = STARTING_BOUNDS.right.toFloat() - 5 val newY = STARTING_BOUNDS.top.toFloat() + 95 taskPositioner.onDragPositioningMove( @@ -566,12 +593,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { taskPositioner.onDragPositioningEnd(newX, newY) - verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> + verify(mockTransitions, never()).startTransition( + eq(WindowManager.TRANSIT_CHANGE), argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) - } - }) + }}, eq(taskPositioner)) } private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean { @@ -650,14 +677,14 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { ) // Verify task's top bound is set to stable bounds top since dragged outside stable bounds // but not in disallowed end bounds area. - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + verify(mockTransitions).startTransition( + eq(WindowManager.TRANSIT_CHANGE), argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && change.configuration.windowConfiguration.bounds.top == STABLE_BOUNDS_LANDSCAPE.top - } - }) + }}, eq(taskPositioner)) } @Test @@ -680,7 +707,8 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { newX, newY ) - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + verify(mockTransitions).startTransition( + eq(WindowManager.TRANSIT_CHANGE), argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && @@ -688,8 +716,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { VALID_DRAG_AREA.bottom && change.configuration.windowConfiguration.bounds.left == VALID_DRAG_AREA.left - } - }) + }}, eq(taskPositioner)) } @Test @@ -741,6 +768,59 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { verify(mockDisplayLayout, Mockito.times(2)).getStableBounds(any()) } + @Test + fun testIsResizingOrAnimatingResizeSet() { + assertFalse(taskPositioner.isResizingOrAnimating) + + taskPositioner.onDragPositioningStart( + CTRL_TYPE_TOP or CTRL_TYPE_RIGHT, + STARTING_BOUNDS.left.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + taskPositioner.onDragPositioningMove( + STARTING_BOUNDS.left.toFloat() - 20, + STARTING_BOUNDS.top.toFloat() - 20 + ) + + // isResizingOrAnimating should be set to true after move during a resize + assertTrue(taskPositioner.isResizingOrAnimating) + + taskPositioner.onDragPositioningEnd( + STARTING_BOUNDS.left.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // isResizingOrAnimating should be not be set till false until after transition animation + assertTrue(taskPositioner.isResizingOrAnimating) + } + + @Test + fun testIsResizingOrAnimatingResizeResetAfterAbortedTransition() { + performDrag(STARTING_BOUNDS.left.toFloat(), + STARTING_BOUNDS.top.toFloat(), STARTING_BOUNDS.left.toFloat() - 20, + STARTING_BOUNDS.top.toFloat() - 20, CTRL_TYPE_TOP or CTRL_TYPE_RIGHT) + + taskPositioner.onTransitionConsumed(mockTransitionBinder, true /* aborted */, + mockTransaction) + + // isResizingOrAnimating should be set to false until after transition successfully consumed + assertFalse(taskPositioner.isResizingOrAnimating) + } + + @Test + fun testIsResizingOrAnimatingResizeResetAfterNonAbortedTransition() { + performDrag(STARTING_BOUNDS.left.toFloat(), + STARTING_BOUNDS.top.toFloat(), STARTING_BOUNDS.left.toFloat() - 20, + STARTING_BOUNDS.top.toFloat() - 20, CTRL_TYPE_TOP or CTRL_TYPE_RIGHT) + + taskPositioner.onTransitionConsumed(mockTransitionBinder, false /* aborted */, + mockTransaction) + + // isResizingOrAnimating should be set to false until after transition successfully consumed + assertFalse(taskPositioner.isResizingOrAnimating) + } + private fun performDrag( startX: Float, startY: Float, 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 a759b53f4238..08412101c30c 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 @@ -26,6 +26,7 @@ import android.view.Surface.ROTATION_270 import android.view.Surface.ROTATION_90 import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE +import android.window.TransitionInfo import android.window.WindowContainerToken import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTaskOrganizer @@ -33,10 +34,12 @@ 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.transition.Transitions +import com.android.wm.shell.transition.Transitions.TransitionFinishCallback 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 +import junit.framework.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -85,6 +88,12 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { @Mock private lateinit var mockTransaction: SurfaceControl.Transaction @Mock + private lateinit var mockTransitionBinder: IBinder + @Mock + private lateinit var mockTransitionInfo: TransitionInfo + @Mock + private lateinit var mockFinishCallback: TransitionFinishCallback + @Mock private lateinit var mockTransitions: Transitions private lateinit var taskPositioner: VeiledResizeTaskPositioner @@ -188,13 +197,12 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { verify(mockDesktopWindowDecoration, never()).createResizeVeil() verify(mockDesktopWindowDecoration, never()).hideResizeVeil() - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && - change.configuration.windowConfiguration.bounds == rectAfterEnd - } - }) + change.configuration.windowConfiguration.bounds == rectAfterEnd }}, + eq(taskPositioner)) } @Test @@ -369,14 +377,13 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { ) // Verify task's top bound is set to stable bounds top since dragged outside stable bounds // but not in disallowed end bounds area. - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && change.configuration.windowConfiguration.bounds.top == - STABLE_BOUNDS_LANDSCAPE.top - } - }) + STABLE_BOUNDS_LANDSCAPE.top }}, + eq(taskPositioner)) } @Test @@ -399,16 +406,15 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { newX, newY ) - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> + verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct -> return@argThat wct.changes.any { (token, change) -> token == taskBinder && (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 && change.configuration.windowConfiguration.bounds.top == VALID_DRAG_AREA.bottom && change.configuration.windowConfiguration.bounds.left == - VALID_DRAG_AREA.left - } - }) + VALID_DRAG_AREA.left }}, + eq(taskPositioner)) } @Test @@ -456,6 +462,47 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { verify(mockDisplayLayout, times(2)).getStableBounds(any()) } + @Test + fun testIsResizingOrAnimatingResizeSet() { + Assert.assertFalse(taskPositioner.isResizingOrAnimating) + + taskPositioner.onDragPositioningStart( + CTRL_TYPE_TOP or CTRL_TYPE_RIGHT, + STARTING_BOUNDS.left.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + taskPositioner.onDragPositioningMove( + STARTING_BOUNDS.left.toFloat() - 20, + STARTING_BOUNDS.top.toFloat() - 20 + ) + + // isResizingOrAnimating should be set to true after move during a resize + Assert.assertTrue(taskPositioner.isResizingOrAnimating) + + taskPositioner.onDragPositioningEnd( + STARTING_BOUNDS.left.toFloat(), + STARTING_BOUNDS.top.toFloat() + ) + + // isResizingOrAnimating should be not be set till false until after transition animation + Assert.assertTrue(taskPositioner.isResizingOrAnimating) + } + + @Test + fun testIsResizingOrAnimatingResizeResetAfterStartAnimation() { + performDrag( + STARTING_BOUNDS.left.toFloat(), STARTING_BOUNDS.top.toFloat(), + STARTING_BOUNDS.left.toFloat() - 20, STARTING_BOUNDS.top.toFloat() - 20, + CTRL_TYPE_TOP or CTRL_TYPE_RIGHT) + + taskPositioner.startAnimation(mockTransitionBinder, mockTransitionInfo, mockTransaction, + mockTransaction, mockFinishCallback) + + // isResizingOrAnimating should be set to false until after transition successfully consumed + Assert.assertFalse(taskPositioner.isResizingOrAnimating) + } + private fun performDrag( startX: Float, startY: Float, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index fe508e23af33..32a91461e40f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -32,6 +32,7 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.argThat; @@ -261,11 +262,6 @@ public class WindowDecorationTests extends ShellTestCase { eq(new Rect(100, 300, 400, 364))); } - verify(mMockSurfaceControlFinishT) - .setPosition(mMockTaskSurface, TASK_POSITION_IN_PARENT.x, - TASK_POSITION_IN_PARENT.y); - verify(mMockSurfaceControlFinishT) - .setWindowCrop(mMockTaskSurface, 300, 100); verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS); verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS); verify(mMockSurfaceControlStartT) @@ -642,6 +638,66 @@ public class WindowDecorationTests extends ShellTestCase { eq(0) /* index */, eq(mandatorySystemGestures())); } + @Test + public void testTaskPositionAndCropNotSetWhenFalse() { + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setBounds(TASK_BOUNDS) + .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y) + .setVisible(true) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .build(); + taskInfo.isFocused = true; + // Density is 2. Shadow radius is 10px. Caption height is 64px. + taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2; + final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); + + + mRelayoutParams.mSetTaskPositionAndCrop = false; + windowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlStartT, never()).setWindowCrop( + eq(mMockTaskSurface), anyInt(), anyInt()); + verify(mMockSurfaceControlFinishT, never()).setPosition( + eq(mMockTaskSurface), anyFloat(), anyFloat()); + verify(mMockSurfaceControlFinishT, never()).setWindowCrop( + eq(mMockTaskSurface), anyInt(), anyInt()); + } + + @Test + public void testTaskPositionAndCropSetWhenSetTrue() { + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setBounds(TASK_BOUNDS) + .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y) + .setVisible(true) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .build(); + taskInfo.isFocused = true; + // Density is 2. Shadow radius is 10px. Caption height is 64px. + taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2; + final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); + + mRelayoutParams.mSetTaskPositionAndCrop = true; + windowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlStartT).setWindowCrop( + eq(mMockTaskSurface), anyInt(), anyInt()); + verify(mMockSurfaceControlFinishT).setPosition( + eq(mMockTaskSurface), anyFloat(), anyFloat()); + verify(mMockSurfaceControlFinishT).setWindowCrop( + eq(mMockTaskSurface), anyInt(), anyInt()); + } + + private TestWindowDecoration createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo) { return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer, taskInfo, mMockTaskSurface, mWindowConfiguration, |