blob: c1b7cb59e87ede3c02503d811f4ef2eee398daac [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
23import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080024import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
25import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080026import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
27import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070028import static com.android.server.wm.WindowManagerService.dipToPixel;
Wale Ogunwale231b06e2015-09-16 12:03:09 -070029import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
30import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
Chong Zhang8e89b312015-09-09 15:09:30 -070031
32import android.annotation.IntDef;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070033import android.app.IActivityTaskManager;
Chong Zhang8e89b312015-09-09 15:09:30 -070034import android.graphics.Point;
35import android.graphics.Rect;
36import android.os.Looper;
37import android.os.Process;
38import android.os.RemoteException;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070039import android.os.Trace;
Chong Zhang8e89b312015-09-09 15:09:30 -070040import android.util.DisplayMetrics;
41import android.util.Slog;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080042import android.view.BatchedInputEventReceiver;
Wale Ogunwale4f52bc62015-09-24 13:47:31 -070043import android.view.Choreographer;
Chong Zhang8e89b312015-09-09 15:09:30 -070044import android.view.Display;
45import android.view.InputChannel;
46import android.view.InputDevice;
47import android.view.InputEvent;
Chong Zhang8e89b312015-09-09 15:09:30 -070048import android.view.MotionEvent;
49import android.view.WindowManager;
50
skuhne@google.com322347b2016-12-02 12:54:03 -080051import com.android.internal.annotations.VisibleForTesting;
Robert Carr788f5742018-07-30 17:46:45 -070052import android.view.InputApplicationHandle;
53import android.view.InputWindowHandle;
54import com.android.server.wm.WindowManagerService.H;
Wale Ogunwale228d4042015-09-13 10:17:34 -070055
56import java.lang.annotation.Retention;
57import java.lang.annotation.RetentionPolicy;
58
Robert Carrf59b8dd2017-10-02 18:58:36 -070059class TaskPositioner {
skuhne@google.com322347b2016-12-02 12:54:03 -080060 private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
Jorim Jaggibc5425c2016-03-01 13:51:16 +010061 private static final String TAG_LOCAL = "TaskPositioner";
62 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
Chong Zhang8e89b312015-09-09 15:09:30 -070063
Garfield Tan6caf1d8c2018-01-18 12:37:50 -080064 private static Factory sFactory;
65
Wale Ogunwale228d4042015-09-13 10:17:34 -070066 // The margin the pointer position has to be within the side of the screen to be
67 // considered at the side of the screen.
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070068 static final int SIDE_MARGIN_DIP = 100;
Wale Ogunwale228d4042015-09-13 10:17:34 -070069
Chong Zhang8e89b312015-09-09 15:09:30 -070070 @IntDef(flag = true,
71 value = {
72 CTRL_NONE,
73 CTRL_LEFT,
74 CTRL_RIGHT,
75 CTRL_TOP,
76 CTRL_BOTTOM
77 })
78 @Retention(RetentionPolicy.SOURCE)
79 @interface CtrlType {}
80
81 private static final int CTRL_NONE = 0x0;
82 private static final int CTRL_LEFT = 0x1;
83 private static final int CTRL_RIGHT = 0x2;
84 private static final int CTRL_TOP = 0x4;
85 private static final int CTRL_BOTTOM = 0x8;
86
Filip Gruszczynski64b6b442016-01-18 13:20:58 -080087 public static final float RESIZING_HINT_ALPHA = 0.5f;
88
89 public static final int RESIZING_HINT_DURATION_MS = 0;
90
skuhne@google.com322347b2016-12-02 12:54:03 -080091 // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
92 // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
93 // aspect he desires.
94 @VisibleForTesting
95 static final float MIN_ASPECT = 1.2f;
96
Chong Zhang8e89b312015-09-09 15:09:30 -070097 private final WindowManagerService mService;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070098 private final IActivityTaskManager mActivityManager;
Chong Zhang8e89b312015-09-09 15:09:30 -070099 private WindowPositionerEventReceiver mInputEventReceiver;
Riddle Hsu654a6f92018-07-13 22:59:36 +0800100 private DisplayContent mDisplayContent;
Chong Zhang8e89b312015-09-09 15:09:30 -0700101 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700102 private Rect mTmpRect = new Rect();
103 private int mSideMargin;
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700104 private int mMinVisibleWidth;
105 private int mMinVisibleHeight;
Chong Zhang8e89b312015-09-09 15:09:30 -0700106
Chong Zhang3005e752015-09-18 18:46:28 -0700107 private Task mTask;
Chong Zhang09b21ef2015-09-14 10:20:21 -0700108 private boolean mResizing;
skuhne@google.com322347b2016-12-02 12:54:03 -0800109 private boolean mPreserveOrientation;
110 private boolean mStartOrientationWasLandscape;
Chong Zhang8e89b312015-09-09 15:09:30 -0700111 private final Rect mWindowOriginalBounds = new Rect();
112 private final Rect mWindowDragBounds = new Rect();
skuhne@google.com322347b2016-12-02 12:54:03 -0800113 private final Point mMaxVisibleSize = new Point();
Chong Zhang8e89b312015-09-09 15:09:30 -0700114 private float mStartDragX;
115 private float mStartDragY;
116 @CtrlType
117 private int mCtrlType = CTRL_NONE;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700118 private boolean mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700119
120 InputChannel mServerChannel;
121 InputChannel mClientChannel;
122 InputApplicationHandle mDragApplicationHandle;
123 InputWindowHandle mDragWindowHandle;
124
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700125 private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
126 public WindowPositionerEventReceiver(
127 InputChannel inputChannel, Looper looper, Choreographer choreographer) {
128 super(inputChannel, looper, choreographer);
Chong Zhang8e89b312015-09-09 15:09:30 -0700129 }
130
131 @Override
Siarhei Vishniakou85ddfff2018-01-31 16:49:36 -0800132 public void onInputEvent(InputEvent event) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700133 if (!(event instanceof MotionEvent)
134 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
135 return;
136 }
137 final MotionEvent motionEvent = (MotionEvent) event;
138 boolean handled = false;
139
140 try {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700141 if (mDragEnded) {
142 // The drag has ended but the clean-up message has not been processed by
143 // window manager. Drop events that occur after this until window manager
144 // has a chance to clean-up the input handle.
145 handled = true;
146 return;
147 }
148
Chong Zhang8e89b312015-09-09 15:09:30 -0700149 final float newX = motionEvent.getRawX();
150 final float newY = motionEvent.getRawY();
151
152 switch (motionEvent.getAction()) {
153 case MotionEvent.ACTION_DOWN: {
154 if (DEBUG_TASK_POSITIONING) {
155 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
156 }
157 } break;
158
159 case MotionEvent.ACTION_MOVE: {
160 if (DEBUG_TASK_POSITIONING){
161 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
162 }
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700163 synchronized (mService.mGlobalLock) {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700164 mDragEnded = notifyMoveLocked(newX, newY);
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800165 mTask.getDimBounds(mTmpRect);
Chong Zhang8e89b312015-09-09 15:09:30 -0700166 }
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700167 if (!mTmpRect.equals(mWindowDragBounds)) {
168 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
169 "wm.TaskPositioner.resizeTask");
170 try {
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900171 mActivityManager.resizeTask(
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700172 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
173 } catch (RemoteException e) {
174 }
175 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
176 }
Chong Zhang8e89b312015-09-09 15:09:30 -0700177 } break;
178
179 case MotionEvent.ACTION_UP: {
180 if (DEBUG_TASK_POSITIONING) {
181 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
182 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700183 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700184 } break;
185
186 case MotionEvent.ACTION_CANCEL: {
187 if (DEBUG_TASK_POSITIONING) {
188 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
189 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700190 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700191 } break;
192 }
193
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700194 if (mDragEnded) {
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700195 final boolean wasResizing = mResizing;
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700196 synchronized (mService.mGlobalLock) {
Chong Zhang3005e752015-09-18 18:46:28 -0700197 endDragLocked();
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700198 mTask.getDimBounds(mTmpRect);
Chong Zhang3005e752015-09-18 18:46:28 -0700199 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700200 try {
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700201 if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
Chong Zhang3005e752015-09-18 18:46:28 -0700202 // We were using fullscreen surface during resizing. Request
203 // resizeTask() one last time to restore surface to window size.
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900204 mActivityManager.resizeTask(
Chong Zhang6de2ae82015-09-30 18:25:21 -0700205 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
Chong Zhang3005e752015-09-18 18:46:28 -0700206 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700207 } catch(RemoteException e) {}
Chong Zhang3005e752015-09-18 18:46:28 -0700208
Chong Zhang8e89b312015-09-09 15:09:30 -0700209 // Post back to WM to handle clean-ups. We still need the input
210 // event handler for the last finishInputEvent()!
Daichi Hironoce2f97a2017-11-30 16:44:15 +0900211 mService.mTaskPositioningController.finishTaskPositioning();
Chong Zhang8e89b312015-09-09 15:09:30 -0700212 }
213 handled = true;
214 } catch (Exception e) {
215 Slog.e(TAG, "Exception caught by drag handleMotion", e);
216 } finally {
217 finishInputEvent(event, handled);
218 }
219 }
220 }
221
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900222 @VisibleForTesting
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700223 TaskPositioner(WindowManagerService service, IActivityTaskManager activityManager) {
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900224 mService = service;
225 mActivityManager = activityManager;
226 }
227
Garfield Tan6caf1d8c2018-01-18 12:37:50 -0800228 /** Use {@link #create(WindowManagerService)} instead **/
Chong Zhang8e89b312015-09-09 15:09:30 -0700229 TaskPositioner(WindowManagerService service) {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700230 this(service, service.mActivityTaskManager);
Chong Zhang8e89b312015-09-09 15:09:30 -0700231 }
232
skuhne@google.com322347b2016-12-02 12:54:03 -0800233 @VisibleForTesting
234 Rect getWindowDragBounds() {
235 return mWindowDragBounds;
236 }
237
Chong Zhang8e89b312015-09-09 15:09:30 -0700238 /**
Garfield Tan07544cd2018-09-12 16:16:54 -0700239 * @param displayContent The Display that the window being dragged is on.
Chong Zhang8e89b312015-09-09 15:09:30 -0700240 */
Robert Carrb1579c82017-09-05 14:54:47 -0700241 void register(DisplayContent displayContent) {
242 final Display display = displayContent.getDisplay();
243
Chong Zhang8e89b312015-09-09 15:09:30 -0700244 if (DEBUG_TASK_POSITIONING) {
245 Slog.d(TAG, "Registering task positioner");
246 }
247
248 if (mClientChannel != null) {
249 Slog.e(TAG, "Task positioner already registered");
250 return;
251 }
252
Riddle Hsu654a6f92018-07-13 22:59:36 +0800253 mDisplayContent = displayContent;
254 display.getMetrics(mDisplayMetrics);
Chong Zhang8e89b312015-09-09 15:09:30 -0700255 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
256 mServerChannel = channels[0];
257 mClientChannel = channels[1];
258 mService.mInputManager.registerInputChannel(mServerChannel, null);
259
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700260 mInputEventReceiver = new WindowPositionerEventReceiver(
Jorim Jaggied7993b2017-03-28 18:50:01 +0100261 mClientChannel, mService.mAnimationHandler.getLooper(),
262 mService.mAnimator.getChoreographer());
Chong Zhang8e89b312015-09-09 15:09:30 -0700263
264 mDragApplicationHandle = new InputApplicationHandle(null);
265 mDragApplicationHandle.name = TAG;
266 mDragApplicationHandle.dispatchingTimeoutNanos =
267 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
268
Robert Carre0a353c2018-08-02 16:38:04 -0700269 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
Riddle Hsu654a6f92018-07-13 22:59:36 +0800270 display.getDisplayId());
Chong Zhang8e89b312015-09-09 15:09:30 -0700271 mDragWindowHandle.name = TAG;
Robert Carreadae822018-10-11 19:07:03 -0700272 mDragWindowHandle.token = mServerChannel.getToken();
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700273 mDragWindowHandle.layer = mService.getDragLayerLocked();
Chong Zhang8e89b312015-09-09 15:09:30 -0700274 mDragWindowHandle.layoutParamsFlags = 0;
275 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
276 mDragWindowHandle.dispatchingTimeoutNanos =
277 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
278 mDragWindowHandle.visible = true;
279 mDragWindowHandle.canReceiveKeys = false;
280 mDragWindowHandle.hasFocus = true;
281 mDragWindowHandle.hasWallpaper = false;
282 mDragWindowHandle.paused = false;
283 mDragWindowHandle.ownerPid = Process.myPid();
284 mDragWindowHandle.ownerUid = Process.myUid();
285 mDragWindowHandle.inputFeatures = 0;
286 mDragWindowHandle.scaleFactor = 1.0f;
287
288 // The drag window cannot receive new touches.
289 mDragWindowHandle.touchableRegion.setEmpty();
290
291 // The drag window covers the entire display
292 mDragWindowHandle.frameLeft = 0;
293 mDragWindowHandle.frameTop = 0;
294 final Point p = new Point();
Riddle Hsu654a6f92018-07-13 22:59:36 +0800295 display.getRealSize(p);
Chong Zhang8e89b312015-09-09 15:09:30 -0700296 mDragWindowHandle.frameRight = p.x;
297 mDragWindowHandle.frameBottom = p.y;
298
299 // Pause rotations before a drag.
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800300 if (DEBUG_ORIENTATION) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700301 Slog.d(TAG, "Pausing rotation during re-position");
302 }
Riddle Hsu654a6f92018-07-13 22:59:36 +0800303 mDisplayContent.pauseRotationLocked();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700304
Garfield Tan07544cd2018-09-12 16:16:54 -0700305 // Notify InputMonitor to take mDragWindowHandle.
306 mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
307
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700308 mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
309 mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
310 mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
Riddle Hsu654a6f92018-07-13 22:59:36 +0800311 display.getRealSize(mMaxVisibleSize);
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700312
313 mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700314 }
315
316 void unregister() {
317 if (DEBUG_TASK_POSITIONING) {
318 Slog.d(TAG, "Unregistering task positioner");
319 }
320
321 if (mClientChannel == null) {
322 Slog.e(TAG, "Task positioner not registered");
323 return;
324 }
325
326 mService.mInputManager.unregisterInputChannel(mServerChannel);
327
328 mInputEventReceiver.dispose();
329 mInputEventReceiver = null;
330 mClientChannel.dispose();
331 mServerChannel.dispose();
332 mClientChannel = null;
333 mServerChannel = null;
334
335 mDragWindowHandle = null;
336 mDragApplicationHandle = null;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700337 mDragEnded = true;
Wale Ogunwale228d4042015-09-13 10:17:34 -0700338
Garfield Tan07544cd2018-09-12 16:16:54 -0700339 // Notify InputMonitor to remove mDragWindowHandle.
340 mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
341
Chong Zhang8e89b312015-09-09 15:09:30 -0700342 // Resume rotations after a drag.
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800343 if (DEBUG_ORIENTATION) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700344 Slog.d(TAG, "Resuming rotation after re-position");
345 }
Riddle Hsu654a6f92018-07-13 22:59:36 +0800346 mDisplayContent.resumeRotationLocked();
347 mDisplayContent = null;
Chong Zhang8e89b312015-09-09 15:09:30 -0700348 }
349
skuhne@google.com322347b2016-12-02 12:54:03 -0800350 void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX,
351 float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700352 if (DEBUG_TASK_POSITIONING) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800353 Slog.d(TAG, "startDrag: win=" + win + ", resize=" + resize
354 + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
355 + startY + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700356 }
Chong Zhangd8ceb852015-11-11 14:53:41 -0800357 mTask = win.getTask();
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700358 // Use the dim bounds, not the original task bounds. The cursor
359 // movement should be calculated relative to the visible bounds.
360 // Also, use the dim bounds of the task which accounts for
361 // multiple app windows. Don't use any bounds from win itself as it
362 // may not be the same size as the task.
363 mTask.getDimBounds(mTmpRect);
skuhne@google.com322347b2016-12-02 12:54:03 -0800364 startDrag(resize, preserveOrientation, startX, startY, mTmpRect);
365 }
366
Daichi Hirono43094a12018-05-31 12:32:23 +0900367 protected void startDrag(boolean resize, boolean preserveOrientation,
skuhne@google.com322347b2016-12-02 12:54:03 -0800368 float startX, float startY, Rect startBounds) {
369 mCtrlType = CTRL_NONE;
370 mStartDragX = startX;
371 mStartDragY = startY;
372 mPreserveOrientation = preserveOrientation;
Chong Zhangd8ceb852015-11-11 14:53:41 -0800373
Chong Zhang8e89b312015-09-09 15:09:30 -0700374 if (resize) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800375 if (startX < startBounds.left) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700376 mCtrlType |= CTRL_LEFT;
377 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800378 if (startX > startBounds.right) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700379 mCtrlType |= CTRL_RIGHT;
380 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800381 if (startY < startBounds.top) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700382 mCtrlType |= CTRL_TOP;
383 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800384 if (startY > startBounds.bottom) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700385 mCtrlType |= CTRL_BOTTOM;
386 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800387 mResizing = mCtrlType != CTRL_NONE;
Chong Zhang8e89b312015-09-09 15:09:30 -0700388 }
389
skuhne@google.com322347b2016-12-02 12:54:03 -0800390 // In case of !isDockedInEffect we are using the union of all task bounds. These might be
391 // made up out of multiple windows which are only partially overlapping. When that happens,
392 // the orientation from the window of interest to the entire stack might diverge. However
393 // for now we treat them as the same.
394 mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
395 mWindowOriginalBounds.set(startBounds);
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700396
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900397 // Notify the app that resizing has started, even though we haven't received any new
398 // bounds yet. This will guarantee that the app starts the backdrop renderer before
399 // configuration changes which could cause an activity restart.
400 if (mResizing) {
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700401 synchronized (mService.mGlobalLock) {
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900402 notifyMoveLocked(startX, startY);
403 }
404
405 // Perform the resize on the WMS handler thread when we don't have the WMS lock held
406 // to ensure that we don't deadlock WMS and AMS. Note that WindowPositionerEventReceiver
407 // callbacks are delivered on the same handler so this initial resize is always
408 // guaranteed to happen before subsequent drag resizes.
409 mService.mH.post(() -> {
410 try {
Daichi Hirono67f2f4b2018-05-25 16:07:30 +0900411 mActivityManager.resizeTask(
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900412 mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
413 } catch (RemoteException e) {
414 }
415 });
416 }
417
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700418 // Make sure we always have valid drag bounds even if the drag ends before any move events
419 // have been handled.
skuhne@google.com322347b2016-12-02 12:54:03 -0800420 mWindowDragBounds.set(startBounds);
Chong Zhang3005e752015-09-18 18:46:28 -0700421 }
422
423 private void endDragLocked() {
424 mResizing = false;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100425 mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM);
Chong Zhang8e89b312015-09-09 15:09:30 -0700426 }
427
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700428 /** Returns true if the move operation should be ended. */
429 private boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700430 if (DEBUG_TASK_POSITIONING) {
Chong Zhangb15758a2015-11-17 12:12:03 -0800431 Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700432 }
433
434 if (mCtrlType != CTRL_NONE) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800435 resizeDrag(x, y);
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100436 mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700437 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700438 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700439
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700440 // This is a moving or scrolling operation.
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800441 mTask.mStack.getDimBounds(mTmpRect);
Chong Zhangb15758a2015-11-17 12:12:03 -0800442
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700443 int nX = (int) x;
444 int nY = (int) y;
Chong Zhangb15758a2015-11-17 12:12:03 -0800445 if (!mTmpRect.contains(nX, nY)) {
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700446 // For a moving operation we allow the pointer to go out of the stack bounds, but
447 // use the clamped pointer position for the drag bounds computation.
448 nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
449 nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
Chong Zhangb15758a2015-11-17 12:12:03 -0800450 }
451
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700452 updateWindowDragBounds(nX, nY, mTmpRect);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700453 return false;
Chong Zhangb15758a2015-11-17 12:12:03 -0800454 }
455
skuhne@google.com322347b2016-12-02 12:54:03 -0800456 /**
457 * The user is drag - resizing the window.
458 *
459 * @param x The x coordinate of the current drag coordinate.
460 * @param y the y coordinate of the current drag coordinate.
461 */
462 @VisibleForTesting
463 void resizeDrag(float x, float y) {
464 // This is a resizing operation.
465 // We need to keep various constraints:
466 // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
467 // 2. The orientation is kept - if required.
468 final int deltaX = Math.round(x - mStartDragX);
469 final int deltaY = Math.round(y - mStartDragY);
470 int left = mWindowOriginalBounds.left;
471 int top = mWindowOriginalBounds.top;
472 int right = mWindowOriginalBounds.right;
473 int bottom = mWindowOriginalBounds.bottom;
474
475 // The aspect which we have to respect. Note that if the orientation does not need to be
476 // preserved the aspect will be calculated as 1.0 which neutralizes the following
477 // computations.
478 final float minAspect = !mPreserveOrientation
479 ? 1.0f
480 : (mStartOrientationWasLandscape ? MIN_ASPECT : (1.0f / MIN_ASPECT));
481 // Calculate the resulting width and height of the drag operation.
482 int width = right - left;
483 int height = bottom - top;
484 if ((mCtrlType & CTRL_LEFT) != 0) {
485 width = Math.max(mMinVisibleWidth, width - deltaX);
486 } else if ((mCtrlType & CTRL_RIGHT) != 0) {
487 width = Math.max(mMinVisibleWidth, width + deltaX);
488 }
489 if ((mCtrlType & CTRL_TOP) != 0) {
490 height = Math.max(mMinVisibleHeight, height - deltaY);
491 } else if ((mCtrlType & CTRL_BOTTOM) != 0) {
492 height = Math.max(mMinVisibleHeight, height + deltaY);
493 }
494
495 // If we have to preserve the orientation - check that we are doing so.
496 final float aspect = (float) width / (float) height;
497 if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT)
498 || (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
499 // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
500 // drag axis. What ever is producing the bigger rectangle will be chosen.
501 int width1;
502 int width2;
503 int height1;
504 int height2;
505 if (mStartOrientationWasLandscape) {
506 // Assuming that the width is our target we calculate the height.
507 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
508 height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT));
509 if (height1 < mMinVisibleHeight) {
510 // If the resulting height is too small we adjust to the minimal size.
511 height1 = mMinVisibleHeight;
512 width1 = Math.max(mMinVisibleWidth,
513 Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT)));
514 }
515 // Assuming that the height is our target we calculate the width.
516 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
517 width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT));
518 if (width2 < mMinVisibleWidth) {
519 // If the resulting width is too small we adjust to the minimal size.
520 width2 = mMinVisibleWidth;
521 height2 = Math.max(mMinVisibleHeight,
522 Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT)));
523 }
524 } else {
525 // Assuming that the width is our target we calculate the height.
526 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
527 height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT));
528 if (height1 < mMinVisibleHeight) {
529 // If the resulting height is too small we adjust to the minimal size.
530 height1 = mMinVisibleHeight;
531 width1 = Math.max(mMinVisibleWidth,
532 Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT)));
533 }
534 // Assuming that the height is our target we calculate the width.
535 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
536 width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT));
537 if (width2 < mMinVisibleWidth) {
538 // If the resulting width is too small we adjust to the minimal size.
539 width2 = mMinVisibleWidth;
540 height2 = Math.max(mMinVisibleHeight,
541 Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT)));
542 }
543 }
544
545 // Use the bigger of the two rectangles if the major change was positive, otherwise
546 // do the opposite.
547 final boolean grows = width > (right - left) || height > (bottom - top);
548 if (grows == (width1 * height1 > width2 * height2)) {
549 width = width1;
550 height = height1;
551 } else {
552 width = width2;
553 height = height2;
554 }
555 }
556
557 // Update mWindowDragBounds to the new drag size.
558 updateDraggedBounds(left, top, right, bottom, width, height);
559 }
560
561 /**
562 * Given the old coordinates and the new width and height, update the mWindowDragBounds.
563 *
564 * @param left The original left bound before the user started dragging.
565 * @param top The original top bound before the user started dragging.
566 * @param right The original right bound before the user started dragging.
567 * @param bottom The original bottom bound before the user started dragging.
568 * @param newWidth The new dragged width.
569 * @param newHeight The new dragged height.
570 */
571 void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth,
572 int newHeight) {
573 // Generate the final bounds by keeping the opposite drag edge constant.
574 if ((mCtrlType & CTRL_LEFT) != 0) {
575 left = right - newWidth;
576 } else { // Note: The right might have changed - if we pulled at the right or not.
577 right = left + newWidth;
578 }
579 if ((mCtrlType & CTRL_TOP) != 0) {
580 top = bottom - newHeight;
581 } else { // Note: The height might have changed - if we pulled at the bottom or not.
582 bottom = top + newHeight;
583 }
584
585 mWindowDragBounds.set(left, top, right, bottom);
586
587 checkBoundsForOrientationViolations(mWindowDragBounds);
588 }
589
590 /**
591 * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
592 *
593 * @param bounds The bounds to be checked.
594 */
595 private void checkBoundsForOrientationViolations(Rect bounds) {
596 // When using debug check that we are not violating the given constraints.
597 if (DEBUG_ORIENTATION_VIOLATIONS) {
598 if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
599 Slog.e(TAG, "Orientation violation detected! should be "
600 + (mStartOrientationWasLandscape ? "landscape" : "portrait")
601 + " but is the other");
602 } else {
603 Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
604 }
605 if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
606 Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
607 + ", " + bounds.width() + ") Height(min,is)=("
608 + mMinVisibleHeight + ", " + bounds.height() + ")");
609 }
610 if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
611 Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
612 + ", " + bounds.width() + ") Height(min,is)=("
613 + mMaxVisibleSize.y + ", " + bounds.height() + ")");
614 }
615 }
616 }
617
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700618 private void updateWindowDragBounds(int x, int y, Rect stackBounds) {
619 final int offsetX = Math.round(x - mStartDragX);
620 final int offsetY = Math.round(y - mStartDragY);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700621 mWindowDragBounds.set(mWindowOriginalBounds);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700622 // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
623 final int maxLeft = stackBounds.right - mMinVisibleWidth;
624 final int minLeft = stackBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700625
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700626 // Vertically, the top mMinVisibleHeight of the window should remain visible.
627 // (This assumes that the window caption bar is at the top of the window).
628 final int minTop = stackBounds.top;
629 final int maxTop = stackBounds.bottom - mMinVisibleHeight;
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700630
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700631 mWindowDragBounds.offsetTo(
632 Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
633 Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
634
Chong Zhangb15758a2015-11-17 12:12:03 -0800635 if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
636 "updateWindowDragBounds: " + mWindowDragBounds);
Chong Zhang8e89b312015-09-09 15:09:30 -0700637 }
638
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700639 public String toShortString() {
640 return TAG;
641 }
Garfield Tan6caf1d8c2018-01-18 12:37:50 -0800642
643 static void setFactory(Factory factory) {
644 sFactory = factory;
645 }
646
647 static TaskPositioner create(WindowManagerService service) {
648 if (sFactory == null) {
649 sFactory = new Factory() {};
650 }
651
652 return sFactory.create(service);
653 }
654
655 interface Factory {
656 default TaskPositioner create(WindowManagerService service) {
657 return new TaskPositioner(service);
658 }
659 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700660}