blob: c68b660bb76fd0ffda5a9cbc90ecff58540348f6 [file] [log] [blame]
Chong Zhang8e89b312015-09-09 15:09:30 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
Wale Ogunwale65ebd952018-04-25 15:41:44 -070019import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
20import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070021import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
Garfield Tan07544cd2018-09-12 16:16:54 -070022
Ben Lin2e306af2020-02-03 14:23:57 -080023import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
24import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
25import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
26import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
27import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
Garfield Tan07544cd2018-09-12 16:16:54 -070028import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
Adrian Roosb125e0b2019-10-02 14:55:14 +020029import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080030import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080031import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
32import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070033import static com.android.server.wm.WindowManagerService.dipToPixel;
Wale Ogunwale231b06e2015-09-16 12:03:09 -070034import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
35import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
Chong Zhang8e89b312015-09-09 15:09:30 -070036
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +090037import android.annotation.NonNull;
Chong Zhang8e89b312015-09-09 15:09:30 -070038import android.graphics.Point;
39import android.graphics.Rect;
Robert Carr0bcbe642018-10-11 19:07:43 -070040import android.os.Binder;
yj81.kwon79c97672019-04-16 19:44:48 -070041import android.os.IBinder;
Chong Zhang8e89b312015-09-09 15:09:30 -070042import android.os.Looper;
43import android.os.Process;
44import android.os.RemoteException;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070045import android.os.Trace;
Chong Zhang8e89b312015-09-09 15:09:30 -070046import android.util.DisplayMetrics;
47import android.util.Slog;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080048import android.view.BatchedInputEventReceiver;
Wale Ogunwale4f52bc62015-09-24 13:47:31 -070049import android.view.Choreographer;
Evan Rosky0d654cb2019-02-26 10:59:10 -080050import android.view.InputApplicationHandle;
Chong Zhang8e89b312015-09-09 15:09:30 -070051import android.view.InputChannel;
52import android.view.InputDevice;
53import android.view.InputEvent;
Evan Rosky0d654cb2019-02-26 10:59:10 -080054import android.view.InputWindowHandle;
Chong Zhang8e89b312015-09-09 15:09:30 -070055import android.view.MotionEvent;
56import android.view.WindowManager;
57
skuhne@google.com322347b2016-12-02 12:54:03 -080058import com.android.internal.annotations.VisibleForTesting;
Ben Lin2e306af2020-02-03 14:23:57 -080059import com.android.internal.policy.TaskResizingAlgorithm;
60import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
Adrian Roosb125e0b2019-10-02 14:55:14 +020061import com.android.server.protolog.common.ProtoLog;
Wale Ogunwale228d4042015-09-13 10:17:34 -070062
yj81.kwon79c97672019-04-16 19:44:48 -070063class TaskPositioner implements IBinder.DeathRecipient {
skuhne@google.com322347b2016-12-02 12:54:03 -080064 private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
Jorim Jaggibc5425c2016-03-01 13:51:16 +010065 private static final String TAG_LOCAL = "TaskPositioner";
66 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
Chong Zhang8e89b312015-09-09 15:09:30 -070067
Garfield Tan6caf1d8c2018-01-18 12:37:50 -080068 private static Factory sFactory;
69
Filip Gruszczynski64b6b442016-01-18 13:20:58 -080070 public static final float RESIZING_HINT_ALPHA = 0.5f;
71
72 public static final int RESIZING_HINT_DURATION_MS = 0;
73
Chong Zhang8e89b312015-09-09 15:09:30 -070074 private final WindowManagerService mService;
75 private WindowPositionerEventReceiver mInputEventReceiver;
Riddle Hsu654a6f92018-07-13 22:59:36 +080076 private DisplayContent mDisplayContent;
Wale Ogunwale228d4042015-09-13 10:17:34 -070077 private Rect mTmpRect = new Rect();
Wale Ogunwaleb8051b82015-09-17 15:41:52 -070078 private int mMinVisibleWidth;
79 private int mMinVisibleHeight;
Chong Zhang8e89b312015-09-09 15:09:30 -070080
Charles Chen2cb5a0f2019-03-22 20:58:00 +080081 @VisibleForTesting
82 Task mTask;
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +090083 WindowState mWindow;
Chong Zhang09b21ef2015-09-14 10:20:21 -070084 private boolean mResizing;
skuhne@google.com322347b2016-12-02 12:54:03 -080085 private boolean mPreserveOrientation;
86 private boolean mStartOrientationWasLandscape;
Chong Zhang8e89b312015-09-09 15:09:30 -070087 private final Rect mWindowOriginalBounds = new Rect();
88 private final Rect mWindowDragBounds = new Rect();
skuhne@google.com322347b2016-12-02 12:54:03 -080089 private final Point mMaxVisibleSize = new Point();
Chong Zhang8e89b312015-09-09 15:09:30 -070090 private float mStartDragX;
91 private float mStartDragY;
92 @CtrlType
93 private int mCtrlType = CTRL_NONE;
yj81.kwon79c97672019-04-16 19:44:48 -070094 @VisibleForTesting
95 boolean mDragEnded;
yj81.kwon19585ff2019-04-23 18:53:57 -070096 IBinder mClientCallback;
Chong Zhang8e89b312015-09-09 15:09:30 -070097
98 InputChannel mServerChannel;
99 InputChannel mClientChannel;
100 InputApplicationHandle mDragApplicationHandle;
101 InputWindowHandle mDragWindowHandle;
102
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700103 private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
104 public WindowPositionerEventReceiver(
105 InputChannel inputChannel, Looper looper, Choreographer choreographer) {
106 super(inputChannel, looper, choreographer);
Chong Zhang8e89b312015-09-09 15:09:30 -0700107 }
108
109 @Override
Siarhei Vishniakou85ddfff2018-01-31 16:49:36 -0800110 public void onInputEvent(InputEvent event) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700111 boolean handled = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700112 try {
Yunfan Chen54872c32019-11-14 19:41:47 -0800113 // All returns need to be in the try block to make sure the finishInputEvent is
114 // called correctly.
115 if (!(event instanceof MotionEvent)
116 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
117 return;
118 }
119 final MotionEvent motionEvent = (MotionEvent) event;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700120 if (mDragEnded) {
121 // The drag has ended but the clean-up message has not been processed by
122 // window manager. Drop events that occur after this until window manager
123 // has a chance to clean-up the input handle.
124 handled = true;
125 return;
126 }
127
Chong Zhang8e89b312015-09-09 15:09:30 -0700128 final float newX = motionEvent.getRawX();
129 final float newY = motionEvent.getRawY();
130
131 switch (motionEvent.getAction()) {
132 case MotionEvent.ACTION_DOWN: {
133 if (DEBUG_TASK_POSITIONING) {
134 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
135 }
136 } break;
137
138 case MotionEvent.ACTION_MOVE: {
139 if (DEBUG_TASK_POSITIONING){
140 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
141 }
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700142 synchronized (mService.mGlobalLock) {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700143 mDragEnded = notifyMoveLocked(newX, newY);
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800144 mTask.getDimBounds(mTmpRect);
Chong Zhang8e89b312015-09-09 15:09:30 -0700145 }
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700146 if (!mTmpRect.equals(mWindowDragBounds)) {
147 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
148 "wm.TaskPositioner.resizeTask");
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800149 mService.mAtmService.resizeTask(
150 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700151 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
152 }
Chong Zhang8e89b312015-09-09 15:09:30 -0700153 } break;
154
155 case MotionEvent.ACTION_UP: {
156 if (DEBUG_TASK_POSITIONING) {
157 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
158 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700159 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700160 } break;
161
162 case MotionEvent.ACTION_CANCEL: {
163 if (DEBUG_TASK_POSITIONING) {
164 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
165 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700166 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700167 } break;
168 }
169
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700170 if (mDragEnded) {
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700171 final boolean wasResizing = mResizing;
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700172 synchronized (mService.mGlobalLock) {
Chong Zhang3005e752015-09-18 18:46:28 -0700173 endDragLocked();
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700174 mTask.getDimBounds(mTmpRect);
Chong Zhang3005e752015-09-18 18:46:28 -0700175 }
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800176 if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
177 // We were using fullscreen surface during resizing. Request
178 // resizeTask() one last time to restore surface to window size.
179 mService.mAtmService.resizeTask(
180 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
181 }
Chong Zhang3005e752015-09-18 18:46:28 -0700182
Chong Zhang8e89b312015-09-09 15:09:30 -0700183 // Post back to WM to handle clean-ups. We still need the input
184 // event handler for the last finishInputEvent()!
Daichi Hironoce2f97a2017-11-30 16:44:15 +0900185 mService.mTaskPositioningController.finishTaskPositioning();
Chong Zhang8e89b312015-09-09 15:09:30 -0700186 }
187 handled = true;
188 } catch (Exception e) {
189 Slog.e(TAG, "Exception caught by drag handleMotion", e);
190 } finally {
191 finishInputEvent(event, handled);
192 }
193 }
194 }
195
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800196 /** Use {@link #create(WindowManagerService)} instead. */
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900197 @VisibleForTesting
Chong Zhang8e89b312015-09-09 15:09:30 -0700198 TaskPositioner(WindowManagerService service) {
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800199 mService = service;
Chong Zhang8e89b312015-09-09 15:09:30 -0700200 }
201
skuhne@google.com322347b2016-12-02 12:54:03 -0800202 @VisibleForTesting
203 Rect getWindowDragBounds() {
204 return mWindowDragBounds;
205 }
206
Chong Zhang8e89b312015-09-09 15:09:30 -0700207 /**
Garfield Tan07544cd2018-09-12 16:16:54 -0700208 * @param displayContent The Display that the window being dragged is on.
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900209 * @param win The window which will be dragged.
Chong Zhang8e89b312015-09-09 15:09:30 -0700210 */
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900211 void register(DisplayContent displayContent, @NonNull WindowState win) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700212 if (DEBUG_TASK_POSITIONING) {
213 Slog.d(TAG, "Registering task positioner");
214 }
215
216 if (mClientChannel != null) {
217 Slog.e(TAG, "Task positioner already registered");
218 return;
219 }
220
Riddle Hsu654a6f92018-07-13 22:59:36 +0800221 mDisplayContent = displayContent;
Chong Zhang8e89b312015-09-09 15:09:30 -0700222 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
223 mServerChannel = channels[0];
224 mClientChannel = channels[1];
Vishnu Nair18782162019-10-08 14:57:16 -0700225 mService.mInputManager.registerInputChannel(mServerChannel);
Chong Zhang8e89b312015-09-09 15:09:30 -0700226
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700227 mInputEventReceiver = new WindowPositionerEventReceiver(
Jorim Jaggied7993b2017-03-28 18:50:01 +0100228 mClientChannel, mService.mAnimationHandler.getLooper(),
229 mService.mAnimator.getChoreographer());
Chong Zhang8e89b312015-09-09 15:09:30 -0700230
Robert Carr0bcbe642018-10-11 19:07:43 -0700231 mDragApplicationHandle = new InputApplicationHandle(new Binder());
Chong Zhang8e89b312015-09-09 15:09:30 -0700232 mDragApplicationHandle.name = TAG;
233 mDragApplicationHandle.dispatchingTimeoutNanos =
234 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
235
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800236 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
237 displayContent.getDisplayId());
Chong Zhang8e89b312015-09-09 15:09:30 -0700238 mDragWindowHandle.name = TAG;
Robert Carreadae822018-10-11 19:07:03 -0700239 mDragWindowHandle.token = mServerChannel.getToken();
Chong Zhang8e89b312015-09-09 15:09:30 -0700240 mDragWindowHandle.layoutParamsFlags = 0;
241 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
242 mDragWindowHandle.dispatchingTimeoutNanos =
243 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
244 mDragWindowHandle.visible = true;
245 mDragWindowHandle.canReceiveKeys = false;
246 mDragWindowHandle.hasFocus = true;
247 mDragWindowHandle.hasWallpaper = false;
248 mDragWindowHandle.paused = false;
249 mDragWindowHandle.ownerPid = Process.myPid();
250 mDragWindowHandle.ownerUid = Process.myUid();
251 mDragWindowHandle.inputFeatures = 0;
252 mDragWindowHandle.scaleFactor = 1.0f;
253
254 // The drag window cannot receive new touches.
255 mDragWindowHandle.touchableRegion.setEmpty();
256
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800257 // The drag window covers the entire display.
258 final Rect displayBounds = mTmpRect;
259 displayContent.getBounds(mTmpRect);
260 mDragWindowHandle.frameLeft = displayBounds.left;
261 mDragWindowHandle.frameTop = displayBounds.top;
262 mDragWindowHandle.frameRight = displayBounds.right;
263 mDragWindowHandle.frameBottom = displayBounds.bottom;
Chong Zhang8e89b312015-09-09 15:09:30 -0700264
265 // Pause rotations before a drag.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200266 ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during re-position");
Riddle Hsuccf09402019-08-13 00:33:06 +0800267 mDisplayContent.getDisplayRotation().pause();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700268
Garfield Tan07544cd2018-09-12 16:16:54 -0700269 // Notify InputMonitor to take mDragWindowHandle.
arthurhungda4c2d62020-04-21 14:38:54 +0800270 mService.mTaskPositioningController.showInputSurface(win.getDisplayId());
Garfield Tan07544cd2018-09-12 16:16:54 -0700271
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800272 final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
273 mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
274 mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
275 mMaxVisibleSize.set(displayBounds.width(), displayBounds.height());
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700276
277 mDragEnded = false;
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900278
279 try {
280 mClientCallback = win.mClient.asBinder();
281 mClientCallback.linkToDeath(this, 0 /* flags */);
282 } catch (RemoteException e) {
283 // The caller has died, so clean up TaskPositioningController.
284 mService.mTaskPositioningController.finishTaskPositioning();
285 return;
286 }
287 mWindow = win;
288 mTask = win.getTask();
Chong Zhang8e89b312015-09-09 15:09:30 -0700289 }
290
291 void unregister() {
292 if (DEBUG_TASK_POSITIONING) {
293 Slog.d(TAG, "Unregistering task positioner");
294 }
295
296 if (mClientChannel == null) {
297 Slog.e(TAG, "Task positioner not registered");
298 return;
299 }
300
arthurhungda4c2d62020-04-21 14:38:54 +0800301 mService.mTaskPositioningController.hideInputSurface(mDisplayContent.getDisplayId());
Chong Zhang8e89b312015-09-09 15:09:30 -0700302 mService.mInputManager.unregisterInputChannel(mServerChannel);
303
304 mInputEventReceiver.dispose();
305 mInputEventReceiver = null;
306 mClientChannel.dispose();
307 mServerChannel.dispose();
308 mClientChannel = null;
309 mServerChannel = null;
310
311 mDragWindowHandle = null;
312 mDragApplicationHandle = null;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700313 mDragEnded = true;
Wale Ogunwale228d4042015-09-13 10:17:34 -0700314
Garfield Tan07544cd2018-09-12 16:16:54 -0700315 // Notify InputMonitor to remove mDragWindowHandle.
316 mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
317
Chong Zhang8e89b312015-09-09 15:09:30 -0700318 // Resume rotations after a drag.
Adrian Roosb125e0b2019-10-02 14:55:14 +0200319 ProtoLog.d(WM_DEBUG_ORIENTATION, "Resuming rotation after re-position");
Riddle Hsuccf09402019-08-13 00:33:06 +0800320 mDisplayContent.getDisplayRotation().resume();
Riddle Hsu654a6f92018-07-13 22:59:36 +0800321 mDisplayContent = null;
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900322 if (mClientCallback != null) {
323 mClientCallback.unlinkToDeath(this, 0 /* flags */);
324 }
325 mWindow = null;
Chong Zhang8e89b312015-09-09 15:09:30 -0700326 }
327
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800328 /**
329 * Starts moving or resizing the task. This method should be only called from
330 * {@link TaskPositioningController#startPositioningLocked} or unit tests.
331 */
332 void startDrag(boolean resize, boolean preserveOrientation, float startX, float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700333 if (DEBUG_TASK_POSITIONING) {
Yongjin Kwon9c5f0aa2019-11-06 17:36:24 +0900334 Slog.d(TAG, "startDrag: win=" + mWindow + ", resize=" + resize
skuhne@google.com322347b2016-12-02 12:54:03 -0800335 + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
336 + startY + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700337 }
Evan Rosky0d654cb2019-02-26 10:59:10 -0800338 // Use the bounds of the task which accounts for
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700339 // multiple app windows. Don't use any bounds from win itself as it
340 // may not be the same size as the task.
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800341 final Rect startBounds = mTmpRect;
342 mTask.getBounds(startBounds);
skuhne@google.com322347b2016-12-02 12:54:03 -0800343
skuhne@google.com322347b2016-12-02 12:54:03 -0800344 mCtrlType = CTRL_NONE;
345 mStartDragX = startX;
346 mStartDragY = startY;
347 mPreserveOrientation = preserveOrientation;
Chong Zhangd8ceb852015-11-11 14:53:41 -0800348
Chong Zhang8e89b312015-09-09 15:09:30 -0700349 if (resize) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800350 if (startX < startBounds.left) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700351 mCtrlType |= CTRL_LEFT;
352 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800353 if (startX > startBounds.right) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700354 mCtrlType |= CTRL_RIGHT;
355 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800356 if (startY < startBounds.top) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700357 mCtrlType |= CTRL_TOP;
358 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800359 if (startY > startBounds.bottom) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700360 mCtrlType |= CTRL_BOTTOM;
361 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800362 mResizing = mCtrlType != CTRL_NONE;
Chong Zhang8e89b312015-09-09 15:09:30 -0700363 }
364
skuhne@google.com322347b2016-12-02 12:54:03 -0800365 // In case of !isDockedInEffect we are using the union of all task bounds. These might be
366 // made up out of multiple windows which are only partially overlapping. When that happens,
367 // the orientation from the window of interest to the entire stack might diverge. However
368 // for now we treat them as the same.
369 mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
370 mWindowOriginalBounds.set(startBounds);
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700371
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900372 // Notify the app that resizing has started, even though we haven't received any new
373 // bounds yet. This will guarantee that the app starts the backdrop renderer before
374 // configuration changes which could cause an activity restart.
375 if (mResizing) {
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800376 notifyMoveLocked(startX, startY);
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900377
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800378 // The WindowPositionerEventReceiver callbacks are delivered on the same handler so this
379 // initial resize is always guaranteed to happen before subsequent drag resizes.
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900380 mService.mH.post(() -> {
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800381 mService.mAtmService.resizeTask(
382 mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900383 });
384 }
385
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700386 // Make sure we always have valid drag bounds even if the drag ends before any move events
387 // have been handled.
skuhne@google.com322347b2016-12-02 12:54:03 -0800388 mWindowDragBounds.set(startBounds);
Chong Zhang3005e752015-09-18 18:46:28 -0700389 }
390
391 private void endDragLocked() {
392 mResizing = false;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100393 mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM);
Chong Zhang8e89b312015-09-09 15:09:30 -0700394 }
395
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700396 /** Returns true if the move operation should be ended. */
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800397 @VisibleForTesting
398 boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700399 if (DEBUG_TASK_POSITIONING) {
Chong Zhangb15758a2015-11-17 12:12:03 -0800400 Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700401 }
402
403 if (mCtrlType != CTRL_NONE) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800404 resizeDrag(x, y);
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100405 mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700406 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700407 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700408
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700409 // This is a moving or scrolling operation.
Riddle Hsu3ae6ebb2020-03-06 15:15:42 +0800410 // Only allow to move in stable area so the target window won't be covered by system bar.
411 // Though {@link Task#resolveOverrideConfiguration} should also avoid the case.
412 mDisplayContent.getStableRect(mTmpRect);
413 // The task may be put in a limited display area.
414 mTmpRect.intersect(mTask.getRootTask().getParent().getBounds());
Chong Zhangb15758a2015-11-17 12:12:03 -0800415
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700416 int nX = (int) x;
417 int nY = (int) y;
Chong Zhangb15758a2015-11-17 12:12:03 -0800418 if (!mTmpRect.contains(nX, nY)) {
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700419 // For a moving operation we allow the pointer to go out of the stack bounds, but
420 // use the clamped pointer position for the drag bounds computation.
421 nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
422 nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
Chong Zhangb15758a2015-11-17 12:12:03 -0800423 }
424
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700425 updateWindowDragBounds(nX, nY, mTmpRect);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700426 return false;
Chong Zhangb15758a2015-11-17 12:12:03 -0800427 }
428
skuhne@google.com322347b2016-12-02 12:54:03 -0800429 /**
430 * The user is drag - resizing the window.
431 *
432 * @param x The x coordinate of the current drag coordinate.
433 * @param y the y coordinate of the current drag coordinate.
434 */
435 @VisibleForTesting
436 void resizeDrag(float x, float y) {
Ben Lin2e306af2020-02-03 14:23:57 -0800437 updateDraggedBounds(TaskResizingAlgorithm.resizeDrag(x, y, mStartDragX, mStartDragY,
438 mWindowOriginalBounds, mCtrlType, mMinVisibleWidth, mMinVisibleHeight,
439 mMaxVisibleSize, mPreserveOrientation, mStartOrientationWasLandscape));
skuhne@google.com322347b2016-12-02 12:54:03 -0800440 }
441
Ben Lin2e306af2020-02-03 14:23:57 -0800442 private void updateDraggedBounds(Rect newBounds) {
443 mWindowDragBounds.set(newBounds);
skuhne@google.com322347b2016-12-02 12:54:03 -0800444
445 checkBoundsForOrientationViolations(mWindowDragBounds);
446 }
447
448 /**
449 * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
450 *
451 * @param bounds The bounds to be checked.
452 */
453 private void checkBoundsForOrientationViolations(Rect bounds) {
454 // When using debug check that we are not violating the given constraints.
455 if (DEBUG_ORIENTATION_VIOLATIONS) {
456 if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
457 Slog.e(TAG, "Orientation violation detected! should be "
458 + (mStartOrientationWasLandscape ? "landscape" : "portrait")
459 + " but is the other");
460 } else {
461 Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
462 }
463 if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
464 Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
465 + ", " + bounds.width() + ") Height(min,is)=("
466 + mMinVisibleHeight + ", " + bounds.height() + ")");
467 }
468 if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
469 Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
470 + ", " + bounds.width() + ") Height(min,is)=("
471 + mMaxVisibleSize.y + ", " + bounds.height() + ")");
472 }
473 }
474 }
475
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700476 private void updateWindowDragBounds(int x, int y, Rect stackBounds) {
477 final int offsetX = Math.round(x - mStartDragX);
478 final int offsetY = Math.round(y - mStartDragY);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700479 mWindowDragBounds.set(mWindowOriginalBounds);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700480 // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
481 final int maxLeft = stackBounds.right - mMinVisibleWidth;
482 final int minLeft = stackBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700483
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700484 // Vertically, the top mMinVisibleHeight of the window should remain visible.
485 // (This assumes that the window caption bar is at the top of the window).
486 final int minTop = stackBounds.top;
487 final int maxTop = stackBounds.bottom - mMinVisibleHeight;
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700488
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700489 mWindowDragBounds.offsetTo(
490 Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
491 Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
492
Chong Zhangb15758a2015-11-17 12:12:03 -0800493 if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
494 "updateWindowDragBounds: " + mWindowDragBounds);
Chong Zhang8e89b312015-09-09 15:09:30 -0700495 }
496
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700497 public String toShortString() {
498 return TAG;
499 }
Garfield Tan6caf1d8c2018-01-18 12:37:50 -0800500
501 static void setFactory(Factory factory) {
502 sFactory = factory;
503 }
504
505 static TaskPositioner create(WindowManagerService service) {
506 if (sFactory == null) {
507 sFactory = new Factory() {};
508 }
509
510 return sFactory.create(service);
511 }
512
yj81.kwon79c97672019-04-16 19:44:48 -0700513 @Override
514 public void binderDied() {
515 mService.mTaskPositioningController.finishTaskPositioning();
516 }
517
Garfield Tan6caf1d8c2018-01-18 12:37:50 -0800518 interface Factory {
519 default TaskPositioner create(WindowManagerService service) {
520 return new TaskPositioner(service);
521 }
522 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700523}