diff options
| author | 2021-08-25 11:16:00 -0700 | |
|---|---|---|
| committer | 2022-06-06 18:51:52 -0700 | |
| commit | f5751858b38021f365ddb5da1067ad128cdae3cc (patch) | |
| tree | 3f71d583479e111f58e321da7f1063d3eb116a21 | |
| parent | 2d3382a8b41b6649e4062d2cc41f9c94d8487eca (diff) | |
Support moving and resizing freeform windows
Instead of an InputMonitor for the entire display, we use window
decoration surface to monitor window resizing gestures. This allows us
to consolidate hit test to InputFlinger.
There is an issue that gesture monitor for gesture nav steals touches
from the resize window while resizing if user moves pointers too fast.
Bug: 165794880
Test: Drag resize works.
Change-Id: I2685e074dd02af55928d20a93f7c0690cc498fc5
8 files changed, 534 insertions, 10 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 a4d1ce18f32b..f59e045958b5 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 @@ -137,11 +137,13 @@ public class WMShellModule { @Provides static WindowDecorViewModel<?> provideWindowDecorViewModel( Context context, + @ShellMainThread Handler mainHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, SyncTransactionQueue syncQueue) { return new CaptionWindowDecorViewModel( context, + mainHandler, taskOrganizer, displayController, syncQueue); 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 d678f031698e..6d28d73996f0 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 @@ -24,6 +24,7 @@ import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; +import android.os.Handler; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; @@ -43,15 +44,18 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption private final ActivityTaskManager mActivityTaskManager; private final ShellTaskOrganizer mTaskOrganizer; private final Context mContext; + private final Handler mMainHandler; private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; public CaptionWindowDecorViewModel( Context context, + Handler mainHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, SyncTransactionQueue syncQueue) { mContext = context; + mMainHandler = mainHandler; mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class); mTaskOrganizer = taskOrganizer; mDisplayController = displayController; @@ -67,9 +71,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption mTaskOrganizer, taskInfo, taskSurface, + mMainHandler, mSyncQueue); - CaptionTouchEventListener touchEventListener = new CaptionTouchEventListener(taskInfo); + TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration); + CaptionTouchEventListener touchEventListener = + new CaptionTouchEventListener(taskInfo, taskPositioner); windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); + windowDecoration.setDragResizeCallback(taskPositioner); onTaskInfoChanged(taskInfo, windowDecoration); return windowDecoration; } @@ -87,10 +95,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption private final int mTaskId; private final WindowContainerToken mTaskToken; + private final DragResizeCallback mDragResizeCallback; - private CaptionTouchEventListener(RunningTaskInfo taskInfo) { + private int mDragPointerId = -1; + + private CaptionTouchEventListener( + RunningTaskInfo taskInfo, DragResizeCallback dragResizeCallback) { mTaskId = taskInfo.taskId; mTaskToken = taskInfo.token; + mDragResizeCallback = dragResizeCallback; } @Override @@ -120,6 +133,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption if (v.getId() != R.id.caption) { return false; } + handleEventForMove(e); + if (e.getAction() != MotionEvent.ACTION_DOWN) { return false; } @@ -132,5 +147,28 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption mSyncQueue.queue(wct); return true; } + + private void handleEventForMove(MotionEvent e) { + switch (e.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mDragPointerId = e.getPointerId(0); + mDragResizeCallback.onDragResizeStart( + 0 /* ctrlType */, e.getRawX(0), e.getRawY(0)); + break; + case MotionEvent.ACTION_MOVE: { + int dragPointerIdx = e.findPointerIndex(mDragPointerId); + mDragResizeCallback.onDragResizeMove( + e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); + break; + } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + int dragPointerIdx = e.findPointerIndex(mDragPointerId); + mDragResizeCallback.onDragResizeEnd( + e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); + break; + } + } + } } } 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 569be23b9f52..572516d2f8c9 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 @@ -17,12 +17,14 @@ package com.android.wm.shell.windowdecor; import android.app.ActivityManager; +import android.app.WindowConfiguration; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.VectorDrawable; +import android.os.Handler; import android.view.SurfaceControl; import android.view.View; import android.window.WindowContainerTransaction; @@ -48,13 +50,22 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL // The thickness of shadows of a window that doesn't have focus in DIP. private static final int DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP = 5; + // Height of button (32dp) + 2 * margin (5dp each) private static final int DECOR_CAPTION_HEIGHT_IN_DIP = 42; + private static final int RESIZE_HANDLE_IN_DIP = 30; + private static final Rect EMPTY_OUTSET = new Rect(); + private static final Rect RESIZE_HANDLE_OUTSET = new Rect( + RESIZE_HANDLE_IN_DIP, RESIZE_HANDLE_IN_DIP, RESIZE_HANDLE_IN_DIP, RESIZE_HANDLE_IN_DIP); + private final Handler mHandler; private final SyncTransactionQueue mSyncQueue; private View.OnClickListener mOnCaptionButtonClickListener; private View.OnTouchListener mOnCaptionTouchListener; + private DragResizeCallback mDragResizeCallback; + + private DragResizeInputListener mDragResizeListener; private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult = new WindowDecoration.RelayoutResult<>(); @@ -65,9 +76,11 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, + Handler handler, SyncTransactionQueue syncQueue) { super(context, displayController, taskOrganizer, taskInfo, taskSurface); + mHandler = handler; mSyncQueue = syncQueue; } @@ -78,15 +91,24 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL mOnCaptionTouchListener = onCaptionTouchListener; } + void setDragResizeCallback(DragResizeCallback dragResizeCallback) { + mDragResizeCallback = dragResizeCallback; + } + void relayout(ActivityManager.RunningTaskInfo taskInfo) { final int shadowRadiusDp = taskInfo.isFocused ? DECOR_SHADOW_FOCUSED_THICKNESS_IN_DIP : DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP; + final boolean isFreeform = mTaskInfo.configuration.windowConfiguration.getWindowingMode() + == WindowConfiguration.WINDOWING_MODE_FREEFORM; + final boolean isDragResizeable = isFreeform && mTaskInfo.isResizeable; + final Rect outset = isDragResizeable ? RESIZE_HANDLE_OUTSET : EMPTY_OUTSET; WindowDecorLinearLayout oldRootView = mResult.mRootView; + final SurfaceControl oldDecorationSurface = mDecorationContainerSurface; final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); final WindowContainerTransaction wct = new WindowContainerTransaction(); relayout(taskInfo, R.layout.caption_window_decoration, oldRootView, - DECOR_CAPTION_HEIGHT_IN_DIP, EMPTY_OUTSET, shadowRadiusDp, t, wct, mResult); + DECOR_CAPTION_HEIGHT_IN_DIP, outset, shadowRadiusDp, t, wct, mResult); taskInfo = null; // Clear it just in case we use it accidentally mSyncQueue.runInSync(transaction -> { @@ -97,11 +119,31 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL }); if (mResult.mRootView == null) { + // This means the task is hidden. Nothing is set up in this case including the + // decoration surface. return; } if (oldRootView != mResult.mRootView) { setupRootView(); } + + if (!isDragResizeable) { + closeDragResizeListener(); + return; + } + + if (oldDecorationSurface != mDecorationContainerSurface) { + closeDragResizeListener(); + mDragResizeListener = new DragResizeInputListener( + mContext, + mHandler, + mDisplay.getDisplayId(), + mDecorationContainerSurface, + mDragResizeCallback); + } + + mDragResizeListener.setGeometry( + mResult.mWidth, mResult.mHeight, (int) (mResult.mDensity * RESIZE_HANDLE_IN_DIP)); } /** @@ -140,4 +182,18 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL VectorDrawable closeBackground = (VectorDrawable) close.getBackground(); closeBackground.setTintList(buttonTintColor); } + + private void closeDragResizeListener() { + if (mDragResizeListener == null) { + return; + } + mDragResizeListener.close(); + mDragResizeListener = null; + } + + @Override + public void close() { + closeDragResizeListener(); + super.close(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java new file mode 100644 index 000000000000..ee160a15df19 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 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; + +/** + * Callback called when receiving drag-resize or drag-move related input events. + */ +public interface DragResizeCallback { + /** + * Called when a drag resize starts. + * + * @param ctrlType {@link TaskPositioner.CtrlType} indicating the direction of resizing, use + * {@code 0} to indicate it's a move + * @param x x coordinate in window decoration coordinate system where the drag resize starts + * @param y y coordinate in window decoration coordinate system where the drag resize starts + */ + void onDragResizeStart(@TaskPositioner.CtrlType int ctrlType, float x, float y); + + /** + * Called when the pointer moves during a drag resize. + * @param x x coordinate in window decoration coordinate system of the new pointer location + * @param y y coordinate in window decoration coordinate system of the new pointer location + */ + void onDragResizeMove(float x, float y); + + /** + * Called when a drag resize stops. + * @param x x coordinate in window decoration coordinate system where the drag resize stops + * @param y y coordinate in window decoration coordinate system where the drag resize stops + */ + void onDragResizeEnd(float x, float y); +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java new file mode 100644 index 000000000000..c6bbb027c8e6 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2022 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; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.Region; +import android.hardware.input.InputManager; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.view.IWindowSession; +import android.view.InputChannel; +import android.view.InputEvent; +import android.view.InputEventReceiver; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.SurfaceControl; +import android.view.WindowManagerGlobal; + +import com.android.internal.view.BaseIWindow; + +/** + * An input event listener registered to InputDispatcher to receive input events on task edges and + * convert them to drag resize requests. + */ +class DragResizeInputListener implements AutoCloseable { + private static final String TAG = "DragResizeInputListener"; + + private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession(); + private final Handler mHandler; + private final InputManager mInputManager; + + private final int mDisplayId; + private final BaseIWindow mFakeWindow; + private final IBinder mFocusGrantToken; + private final SurfaceControl mDecorationSurface; + private final InputChannel mInputChannel; + private final TaskResizeInputEventReceiver mInputEventReceiver; + private final com.android.wm.shell.windowdecor.DragResizeCallback mCallback; + + private int mWidth; + private int mHeight; + private int mResizeHandleThickness; + + private int mDragPointerId = -1; + + DragResizeInputListener( + Context context, + Handler handler, + int displayId, + SurfaceControl decorationSurface, + DragResizeCallback callback) { + mInputManager = context.getSystemService(InputManager.class); + mHandler = handler; + mDisplayId = displayId; + mDecorationSurface = decorationSurface; + // Use a fake window as the backing surface is a container layer and we don't want to create + // a buffer layer for it so we can't use ViewRootImpl. + mFakeWindow = new BaseIWindow(); + mFakeWindow.setSession(mWindowSession); + mFocusGrantToken = new Binder(); + mInputChannel = new InputChannel(); + try { + mWindowSession.grantInputChannel( + mDisplayId, + new SurfaceControl(mDecorationSurface, TAG), + mFakeWindow, + null /* hostInputToken */, + FLAG_NOT_FOCUSABLE, + PRIVATE_FLAG_TRUSTED_OVERLAY, + TYPE_APPLICATION, + mFocusGrantToken, + TAG + " of " + decorationSurface.toString(), + mInputChannel); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + + mInputEventReceiver = new TaskResizeInputEventReceiver(mInputChannel, mHandler.getLooper()); + mCallback = callback; + } + + /** + * Updates geometry of this drag resize handler. Needs to be called every time there is a size + * change to notify the input event receiver it's ready to take the next input event. Otherwise + * it'll keep batching move events and the drag resize process is stalled. + * + * This is also used to update the touch regions of this handler every event dispatched here is + * a potential resize request. + * + * @param width The width of the drag resize handler in pixels, including resize handle + * thickness. That is task width + 2 * resize handle thickness. + * @param height The height of the drag resize handler in pixels, including resize handle + * thickness. That is task height + 2 * resize handle thickness. + * @param resizeHandleThickness The thickness of the resize handle in pixels. + */ + void setGeometry(int width, int height, int resizeHandleThickness) { + if (mWidth == width && mHeight == height + && mResizeHandleThickness == resizeHandleThickness) { + return; + } + + mWidth = width; + mHeight = height; + mResizeHandleThickness = resizeHandleThickness; + + Region touchRegion = new Region(); + final Rect topInputBounds = new Rect(0, 0, mWidth, mResizeHandleThickness); + touchRegion.union(topInputBounds); + + final Rect leftInputBounds = new Rect(0, mResizeHandleThickness, + mResizeHandleThickness, mHeight - mResizeHandleThickness); + touchRegion.union(leftInputBounds); + + final Rect rightInputBounds = new Rect( + mWidth - mResizeHandleThickness, mResizeHandleThickness, + mWidth, mHeight - mResizeHandleThickness); + touchRegion.union(rightInputBounds); + + final Rect bottomInputBounds = new Rect(0, mHeight - mResizeHandleThickness, + mWidth, mHeight); + touchRegion.union(bottomInputBounds); + + try { + mWindowSession.updateInputChannel( + mInputChannel.getToken(), + mDisplayId, + new SurfaceControl( + mDecorationSurface, "DragResizeInputListener#setTouchRegion"), + FLAG_NOT_FOCUSABLE, + PRIVATE_FLAG_TRUSTED_OVERLAY, + touchRegion); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + + // This marks all relevant components have handled the previous resize event and can take + // the next one now. + mInputEventReceiver.onHandledLastResizeEvent(); + } + + @Override + public void close() { + mInputChannel.dispose(); + try { + mWindowSession.remove(mFakeWindow); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + private class TaskResizeInputEventReceiver extends InputEventReceiver { + private boolean mWaitingForLastResizeEventHandled; + + private TaskResizeInputEventReceiver(InputChannel inputChannel, Looper looper) { + super(inputChannel, looper); + } + + private void onHandledLastResizeEvent() { + mWaitingForLastResizeEventHandled = false; + consumeBatchedInputEvents(-1); + } + + @Override + public void onBatchedInputEventPending(int source) { + // InputEventReceiver keeps continuous move events in a batched event until explicitly + // consuming it or an incompatible event shows up (likely an up event in this case). We + // continue to keep move events in the next batched event until we receive a geometry + // update so that we don't put too much pressure on the framework with excessive number + // of input events if it can't handle them fast enough. It's more responsive to always + // resize the task to the latest received coordinates. + if (!mWaitingForLastResizeEventHandled) { + consumeBatchedInputEvents(-1); + } + } + + @Override + public void onInputEvent(InputEvent inputEvent) { + finishInputEvent(inputEvent, handleInputEvent(inputEvent)); + } + + private boolean handleInputEvent(InputEvent inputEvent) { + if (!(inputEvent instanceof MotionEvent)) { + return false; + } + + MotionEvent e = (MotionEvent) inputEvent; + switch (e.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + mDragPointerId = e.getPointerId(0); + mCallback.onDragResizeStart( + calculateCtrlType(e.getX(0), e.getY(0)), e.getRawX(0), e.getRawY(0)); + mWaitingForLastResizeEventHandled = false; + break; + } + case MotionEvent.ACTION_MOVE: { + int dragPointerIndex = e.findPointerIndex(mDragPointerId); + mCallback.onDragResizeMove( + e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex)); + mWaitingForLastResizeEventHandled = true; + break; + } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + int dragPointerIndex = e.findPointerIndex(mDragPointerId); + mCallback.onDragResizeEnd( + e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex)); + mWaitingForLastResizeEventHandled = false; + mDragPointerId = -1; + break; + } + case MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_HOVER_MOVE: { + updateCursorType(e.getXCursorPosition(), e.getYCursorPosition()); + break; + } + case MotionEvent.ACTION_HOVER_EXIT: + mInputManager.setPointerIconType(PointerIcon.TYPE_DEFAULT); + break; + } + return true; + } + + @TaskPositioner.CtrlType + private int calculateCtrlType(float x, float y) { + int ctrlType = 0; + if (x < mResizeHandleThickness) { + ctrlType |= TaskPositioner.CTRL_TYPE_LEFT; + } + if (x > mWidth - mResizeHandleThickness) { + ctrlType |= TaskPositioner.CTRL_TYPE_RIGHT; + } + if (y < mResizeHandleThickness) { + ctrlType |= TaskPositioner.CTRL_TYPE_TOP; + } + if (y > mHeight - mResizeHandleThickness) { + ctrlType |= TaskPositioner.CTRL_TYPE_BOTTOM; + } + return ctrlType; + } + + private void updateCursorType(float x, float y) { + @TaskPositioner.CtrlType int ctrlType = calculateCtrlType(x, y); + + int cursorType = PointerIcon.TYPE_DEFAULT; + switch (ctrlType) { + case TaskPositioner.CTRL_TYPE_LEFT: + case TaskPositioner.CTRL_TYPE_RIGHT: + cursorType = PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW; + break; + case TaskPositioner.CTRL_TYPE_TOP: + case TaskPositioner.CTRL_TYPE_BOTTOM: + cursorType = PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW; + break; + case TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_TOP: + case TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_BOTTOM: + cursorType = PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW; + break; + case TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_BOTTOM: + case TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_TOP: + cursorType = PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW; + break; + } + mInputManager.setPointerIconType(cursorType); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java new file mode 100644 index 000000000000..280569b05d87 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2022 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; + +import android.annotation.IntDef; +import android.graphics.PointF; +import android.graphics.Rect; +import android.window.WindowContainerTransaction; + +import com.android.wm.shell.ShellTaskOrganizer; + +class TaskPositioner implements DragResizeCallback { + + @IntDef({CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM}) + @interface CtrlType {} + + static final int CTRL_TYPE_LEFT = 1; + static final int CTRL_TYPE_RIGHT = 2; + static final int CTRL_TYPE_TOP = 4; + static final int CTRL_TYPE_BOTTOM = 8; + + private final ShellTaskOrganizer mTaskOrganizer; + private final WindowDecoration mWindowDecoration; + + private final Rect mTaskBoundsAtDragStart = new Rect(); + private final PointF mResizeStartPoint = new PointF(); + private final Rect mResizeTaskBounds = new Rect(); + + private int mCtrlType; + + TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration) { + mTaskOrganizer = taskOrganizer; + mWindowDecoration = windowDecoration; + } + + @Override + public void onDragResizeStart(int ctrlType, float x, float y) { + mCtrlType = ctrlType; + + mTaskBoundsAtDragStart.set( + mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds()); + mResizeStartPoint.set(x, y); + } + + @Override + public void onDragResizeMove(float x, float y) { + changeBounds(x, y); + } + + @Override + public void onDragResizeEnd(float x, float y) { + changeBounds(x, y); + + mCtrlType = 0; + mTaskBoundsAtDragStart.setEmpty(); + mResizeStartPoint.set(0, 0); + } + + private void changeBounds(float x, float y) { + float deltaX = x - mResizeStartPoint.x; + mResizeTaskBounds.set(mTaskBoundsAtDragStart); + if ((mCtrlType & CTRL_TYPE_LEFT) != 0) { + mResizeTaskBounds.left += deltaX; + } + if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) { + mResizeTaskBounds.right += deltaX; + } + float deltaY = y - mResizeStartPoint.y; + if ((mCtrlType & CTRL_TYPE_TOP) != 0) { + mResizeTaskBounds.top += deltaY; + } + if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) { + mResizeTaskBounds.bottom += deltaY; + } + if (mCtrlType == 0) { + mResizeTaskBounds.offset((int) deltaX, (int) deltaY); + } + + if (!mResizeTaskBounds.isEmpty()) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds); + mTaskOrganizer.applyTransaction(wct); + } + } +} diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 974340ee2510..fb1450d0aa61 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4880,9 +4880,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return true; } - // TODO(b/165794880): Freeform task organizer doesn't support drag-resize yet. Remove - // the special case when it does. - if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FREEFORM) { + if (task.isOrganized()) { return true; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 7e06b8823260..8389dbde3e14 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -288,10 +288,7 @@ final class InputMonitor { SurfaceControl touchableRegionCrop = null; final Task task = w.getTask(); if (task != null) { - // TODO(b/165794636): Remove the special case for freeform window once drag resizing is - // handled by WM shell. - if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN - && !task.inFreeformWindowingMode()) { + if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { // If the window is in a TaskManaged by a TaskOrganizer then most cropping will // be applied using the SurfaceControl hierarchy from the Organizer. This means // we need to make sure that these changes in crop are reflected in the input |