blob: fa7ea2ffdd171bc2862b2eada41287474f50938e [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
Chong Zhang87b21722015-09-21 15:39:51 -070019import static android.app.ActivityManager.RESIZE_MODE_USER;
Chong Zhang6de2ae82015-09-30 18:25:21 -070020import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070021import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080022import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
23import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080024import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
25import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070026import static com.android.server.wm.WindowManagerService.dipToPixel;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +010027import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
Wale Ogunwale231b06e2015-09-16 12:03:09 -070028import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
29import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
Chong Zhang8e89b312015-09-09 15:09:30 -070030
31import android.annotation.IntDef;
32import android.graphics.Point;
33import android.graphics.Rect;
34import android.os.Looper;
35import android.os.Process;
36import android.os.RemoteException;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070037import android.os.Trace;
Chong Zhang8e89b312015-09-09 15:09:30 -070038import android.util.DisplayMetrics;
39import android.util.Slog;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080040import android.view.BatchedInputEventReceiver;
Wale Ogunwale4f52bc62015-09-24 13:47:31 -070041import android.view.Choreographer;
Chong Zhang8e89b312015-09-09 15:09:30 -070042import android.view.Display;
43import android.view.InputChannel;
44import android.view.InputDevice;
45import android.view.InputEvent;
Chong Zhang8e89b312015-09-09 15:09:30 -070046import android.view.MotionEvent;
47import android.view.WindowManager;
48
skuhne@google.com322347b2016-12-02 12:54:03 -080049import com.android.internal.annotations.VisibleForTesting;
Wale Ogunwale228d4042015-09-13 10:17:34 -070050import com.android.server.input.InputApplicationHandle;
51import com.android.server.input.InputWindowHandle;
52import com.android.server.wm.WindowManagerService.H;
53
54import java.lang.annotation.Retention;
55import java.lang.annotation.RetentionPolicy;
56
Robert Carrf59b8dd2017-10-02 18:58:36 -070057class TaskPositioner {
skuhne@google.com322347b2016-12-02 12:54:03 -080058 private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
Jorim Jaggibc5425c2016-03-01 13:51:16 +010059 private static final String TAG_LOCAL = "TaskPositioner";
60 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
Chong Zhang8e89b312015-09-09 15:09:30 -070061
Wale Ogunwale228d4042015-09-13 10:17:34 -070062 // The margin the pointer position has to be within the side of the screen to be
63 // considered at the side of the screen.
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070064 static final int SIDE_MARGIN_DIP = 100;
Wale Ogunwale228d4042015-09-13 10:17:34 -070065
Chong Zhang8e89b312015-09-09 15:09:30 -070066 @IntDef(flag = true,
67 value = {
68 CTRL_NONE,
69 CTRL_LEFT,
70 CTRL_RIGHT,
71 CTRL_TOP,
72 CTRL_BOTTOM
73 })
74 @Retention(RetentionPolicy.SOURCE)
75 @interface CtrlType {}
76
77 private static final int CTRL_NONE = 0x0;
78 private static final int CTRL_LEFT = 0x1;
79 private static final int CTRL_RIGHT = 0x2;
80 private static final int CTRL_TOP = 0x4;
81 private static final int CTRL_BOTTOM = 0x8;
82
Filip Gruszczynski64b6b442016-01-18 13:20:58 -080083 public static final float RESIZING_HINT_ALPHA = 0.5f;
84
85 public static final int RESIZING_HINT_DURATION_MS = 0;
86
skuhne@google.com322347b2016-12-02 12:54:03 -080087 // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
88 // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
89 // aspect he desires.
90 @VisibleForTesting
91 static final float MIN_ASPECT = 1.2f;
92
Chong Zhang8e89b312015-09-09 15:09:30 -070093 private final WindowManagerService mService;
94 private WindowPositionerEventReceiver mInputEventReceiver;
95 private Display mDisplay;
96 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Wale Ogunwale228d4042015-09-13 10:17:34 -070097 private Rect mTmpRect = new Rect();
98 private int mSideMargin;
Wale Ogunwaleb8051b82015-09-17 15:41:52 -070099 private int mMinVisibleWidth;
100 private int mMinVisibleHeight;
Chong Zhang8e89b312015-09-09 15:09:30 -0700101
Chong Zhang3005e752015-09-18 18:46:28 -0700102 private Task mTask;
Chong Zhang09b21ef2015-09-14 10:20:21 -0700103 private boolean mResizing;
skuhne@google.com322347b2016-12-02 12:54:03 -0800104 private boolean mPreserveOrientation;
105 private boolean mStartOrientationWasLandscape;
Chong Zhang8e89b312015-09-09 15:09:30 -0700106 private final Rect mWindowOriginalBounds = new Rect();
107 private final Rect mWindowDragBounds = new Rect();
skuhne@google.com322347b2016-12-02 12:54:03 -0800108 private final Point mMaxVisibleSize = new Point();
Chong Zhang8e89b312015-09-09 15:09:30 -0700109 private float mStartDragX;
110 private float mStartDragY;
111 @CtrlType
112 private int mCtrlType = CTRL_NONE;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700113 private boolean mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700114
115 InputChannel mServerChannel;
116 InputChannel mClientChannel;
117 InputApplicationHandle mDragApplicationHandle;
118 InputWindowHandle mDragWindowHandle;
119
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700120 private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
121 public WindowPositionerEventReceiver(
122 InputChannel inputChannel, Looper looper, Choreographer choreographer) {
123 super(inputChannel, looper, choreographer);
Chong Zhang8e89b312015-09-09 15:09:30 -0700124 }
125
126 @Override
Tarandeep Singhe1cfcf42017-07-10 18:50:00 -0700127 public void onInputEvent(InputEvent event, int displayId) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700128 if (!(event instanceof MotionEvent)
129 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
130 return;
131 }
132 final MotionEvent motionEvent = (MotionEvent) event;
133 boolean handled = false;
134
135 try {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700136 if (mDragEnded) {
137 // The drag has ended but the clean-up message has not been processed by
138 // window manager. Drop events that occur after this until window manager
139 // has a chance to clean-up the input handle.
140 handled = true;
141 return;
142 }
143
Chong Zhang8e89b312015-09-09 15:09:30 -0700144 final float newX = motionEvent.getRawX();
145 final float newY = motionEvent.getRawY();
146
147 switch (motionEvent.getAction()) {
148 case MotionEvent.ACTION_DOWN: {
149 if (DEBUG_TASK_POSITIONING) {
150 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
151 }
152 } break;
153
154 case MotionEvent.ACTION_MOVE: {
155 if (DEBUG_TASK_POSITIONING){
156 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
157 }
158 synchronized (mService.mWindowMap) {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700159 mDragEnded = notifyMoveLocked(newX, newY);
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800160 mTask.getDimBounds(mTmpRect);
Chong Zhang8e89b312015-09-09 15:09:30 -0700161 }
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700162 if (!mTmpRect.equals(mWindowDragBounds)) {
163 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
164 "wm.TaskPositioner.resizeTask");
165 try {
166 mService.mActivityManager.resizeTask(
167 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
168 } catch (RemoteException e) {
169 }
170 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
171 }
Chong Zhang8e89b312015-09-09 15:09:30 -0700172 } break;
173
174 case MotionEvent.ACTION_UP: {
175 if (DEBUG_TASK_POSITIONING) {
176 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
177 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700178 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700179 } break;
180
181 case MotionEvent.ACTION_CANCEL: {
182 if (DEBUG_TASK_POSITIONING) {
183 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
184 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700185 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700186 } break;
187 }
188
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700189 if (mDragEnded) {
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700190 final boolean wasResizing = mResizing;
Chong Zhang3005e752015-09-18 18:46:28 -0700191 synchronized (mService.mWindowMap) {
192 endDragLocked();
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700193 mTask.getDimBounds(mTmpRect);
Chong Zhang3005e752015-09-18 18:46:28 -0700194 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700195 try {
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700196 if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
Chong Zhang3005e752015-09-18 18:46:28 -0700197 // We were using fullscreen surface during resizing. Request
198 // resizeTask() one last time to restore surface to window size.
199 mService.mActivityManager.resizeTask(
Chong Zhang6de2ae82015-09-30 18:25:21 -0700200 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
Chong Zhang3005e752015-09-18 18:46:28 -0700201 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700202 } catch(RemoteException e) {}
Chong Zhang3005e752015-09-18 18:46:28 -0700203
Chong Zhang8e89b312015-09-09 15:09:30 -0700204 // Post back to WM to handle clean-ups. We still need the input
205 // event handler for the last finishInputEvent()!
Daichi Hironoce2f97a2017-11-30 16:44:15 +0900206 mService.mTaskPositioningController.finishTaskPositioning();
Chong Zhang8e89b312015-09-09 15:09:30 -0700207 }
208 handled = true;
209 } catch (Exception e) {
210 Slog.e(TAG, "Exception caught by drag handleMotion", e);
211 } finally {
212 finishInputEvent(event, handled);
213 }
214 }
215 }
216
217 TaskPositioner(WindowManagerService service) {
218 mService = service;
219 }
220
skuhne@google.com322347b2016-12-02 12:54:03 -0800221 @VisibleForTesting
222 Rect getWindowDragBounds() {
223 return mWindowDragBounds;
224 }
225
Chong Zhang8e89b312015-09-09 15:09:30 -0700226 /**
227 * @param display The Display that the window being dragged is on.
228 */
Robert Carrb1579c82017-09-05 14:54:47 -0700229 void register(DisplayContent displayContent) {
230 final Display display = displayContent.getDisplay();
231
Chong Zhang8e89b312015-09-09 15:09:30 -0700232 if (DEBUG_TASK_POSITIONING) {
233 Slog.d(TAG, "Registering task positioner");
234 }
235
236 if (mClientChannel != null) {
237 Slog.e(TAG, "Task positioner already registered");
238 return;
239 }
240
241 mDisplay = display;
242 mDisplay.getMetrics(mDisplayMetrics);
243 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
244 mServerChannel = channels[0];
245 mClientChannel = channels[1];
246 mService.mInputManager.registerInputChannel(mServerChannel, null);
247
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700248 mInputEventReceiver = new WindowPositionerEventReceiver(
Jorim Jaggied7993b2017-03-28 18:50:01 +0100249 mClientChannel, mService.mAnimationHandler.getLooper(),
250 mService.mAnimator.getChoreographer());
Chong Zhang8e89b312015-09-09 15:09:30 -0700251
252 mDragApplicationHandle = new InputApplicationHandle(null);
253 mDragApplicationHandle.name = TAG;
254 mDragApplicationHandle.dispatchingTimeoutNanos =
255 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
256
Vladislav Kaznacheev3787de12016-12-21 10:36:35 -0800257 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
Chong Zhang8e89b312015-09-09 15:09:30 -0700258 mDisplay.getDisplayId());
259 mDragWindowHandle.name = TAG;
260 mDragWindowHandle.inputChannel = mServerChannel;
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700261 mDragWindowHandle.layer = mService.getDragLayerLocked();
Chong Zhang8e89b312015-09-09 15:09:30 -0700262 mDragWindowHandle.layoutParamsFlags = 0;
263 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
264 mDragWindowHandle.dispatchingTimeoutNanos =
265 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
266 mDragWindowHandle.visible = true;
267 mDragWindowHandle.canReceiveKeys = false;
268 mDragWindowHandle.hasFocus = true;
269 mDragWindowHandle.hasWallpaper = false;
270 mDragWindowHandle.paused = false;
271 mDragWindowHandle.ownerPid = Process.myPid();
272 mDragWindowHandle.ownerUid = Process.myUid();
273 mDragWindowHandle.inputFeatures = 0;
274 mDragWindowHandle.scaleFactor = 1.0f;
275
276 // The drag window cannot receive new touches.
277 mDragWindowHandle.touchableRegion.setEmpty();
278
279 // The drag window covers the entire display
280 mDragWindowHandle.frameLeft = 0;
281 mDragWindowHandle.frameTop = 0;
282 final Point p = new Point();
283 mDisplay.getRealSize(p);
284 mDragWindowHandle.frameRight = p.x;
285 mDragWindowHandle.frameBottom = p.y;
286
287 // Pause rotations before a drag.
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800288 if (DEBUG_ORIENTATION) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700289 Slog.d(TAG, "Pausing rotation during re-position");
290 }
291 mService.pauseRotationLocked();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700292
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700293 mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
294 mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
295 mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
skuhne@google.com322347b2016-12-02 12:54:03 -0800296 mDisplay.getRealSize(mMaxVisibleSize);
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700297
298 mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700299 }
300
301 void unregister() {
302 if (DEBUG_TASK_POSITIONING) {
303 Slog.d(TAG, "Unregistering task positioner");
304 }
305
306 if (mClientChannel == null) {
307 Slog.e(TAG, "Task positioner not registered");
308 return;
309 }
310
311 mService.mInputManager.unregisterInputChannel(mServerChannel);
312
313 mInputEventReceiver.dispose();
314 mInputEventReceiver = null;
315 mClientChannel.dispose();
316 mServerChannel.dispose();
317 mClientChannel = null;
318 mServerChannel = null;
319
320 mDragWindowHandle = null;
321 mDragApplicationHandle = null;
322 mDisplay = null;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700323 mDragEnded = true;
Wale Ogunwale228d4042015-09-13 10:17:34 -0700324
Chong Zhang8e89b312015-09-09 15:09:30 -0700325 // Resume rotations after a drag.
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800326 if (DEBUG_ORIENTATION) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700327 Slog.d(TAG, "Resuming rotation after re-position");
328 }
329 mService.resumeRotationLocked();
330 }
331
skuhne@google.com322347b2016-12-02 12:54:03 -0800332 void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX,
333 float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700334 if (DEBUG_TASK_POSITIONING) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800335 Slog.d(TAG, "startDrag: win=" + win + ", resize=" + resize
336 + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
337 + startY + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700338 }
Chong Zhangd8ceb852015-11-11 14:53:41 -0800339 mTask = win.getTask();
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700340 // Use the dim bounds, not the original task bounds. The cursor
341 // movement should be calculated relative to the visible bounds.
342 // Also, use the dim bounds of the task which accounts for
343 // multiple app windows. Don't use any bounds from win itself as it
344 // may not be the same size as the task.
345 mTask.getDimBounds(mTmpRect);
skuhne@google.com322347b2016-12-02 12:54:03 -0800346 startDrag(resize, preserveOrientation, startX, startY, mTmpRect);
347 }
348
349 @VisibleForTesting
350 void startDrag(boolean resize, boolean preserveOrientation,
351 float startX, float startY, Rect startBounds) {
352 mCtrlType = CTRL_NONE;
353 mStartDragX = startX;
354 mStartDragY = startY;
355 mPreserveOrientation = preserveOrientation;
Chong Zhangd8ceb852015-11-11 14:53:41 -0800356
Chong Zhang8e89b312015-09-09 15:09:30 -0700357 if (resize) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800358 if (startX < startBounds.left) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700359 mCtrlType |= CTRL_LEFT;
360 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800361 if (startX > startBounds.right) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700362 mCtrlType |= CTRL_RIGHT;
363 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800364 if (startY < startBounds.top) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700365 mCtrlType |= CTRL_TOP;
366 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800367 if (startY > startBounds.bottom) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700368 mCtrlType |= CTRL_BOTTOM;
369 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800370 mResizing = mCtrlType != CTRL_NONE;
Chong Zhang8e89b312015-09-09 15:09:30 -0700371 }
372
skuhne@google.com322347b2016-12-02 12:54:03 -0800373 // In case of !isDockedInEffect we are using the union of all task bounds. These might be
374 // made up out of multiple windows which are only partially overlapping. When that happens,
375 // the orientation from the window of interest to the entire stack might diverge. However
376 // for now we treat them as the same.
377 mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
378 mWindowOriginalBounds.set(startBounds);
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700379
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900380 // Notify the app that resizing has started, even though we haven't received any new
381 // bounds yet. This will guarantee that the app starts the backdrop renderer before
382 // configuration changes which could cause an activity restart.
383 if (mResizing) {
384 synchronized (mService.mWindowMap) {
385 notifyMoveLocked(startX, startY);
386 }
387
388 // Perform the resize on the WMS handler thread when we don't have the WMS lock held
389 // to ensure that we don't deadlock WMS and AMS. Note that WindowPositionerEventReceiver
390 // callbacks are delivered on the same handler so this initial resize is always
391 // guaranteed to happen before subsequent drag resizes.
392 mService.mH.post(() -> {
393 try {
394 mService.mActivityManager.resizeTask(
395 mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
396 } catch (RemoteException e) {
397 }
398 });
399 }
400
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700401 // Make sure we always have valid drag bounds even if the drag ends before any move events
402 // have been handled.
skuhne@google.com322347b2016-12-02 12:54:03 -0800403 mWindowDragBounds.set(startBounds);
Chong Zhang3005e752015-09-18 18:46:28 -0700404 }
405
406 private void endDragLocked() {
407 mResizing = false;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100408 mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM);
Chong Zhang8e89b312015-09-09 15:09:30 -0700409 }
410
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700411 /** Returns true if the move operation should be ended. */
412 private boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700413 if (DEBUG_TASK_POSITIONING) {
Chong Zhangb15758a2015-11-17 12:12:03 -0800414 Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700415 }
416
417 if (mCtrlType != CTRL_NONE) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800418 resizeDrag(x, y);
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100419 mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700420 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700421 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700422
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700423 // This is a moving or scrolling operation.
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800424 mTask.mStack.getDimBounds(mTmpRect);
Chong Zhangb15758a2015-11-17 12:12:03 -0800425
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700426 int nX = (int) x;
427 int nY = (int) y;
Chong Zhangb15758a2015-11-17 12:12:03 -0800428 if (!mTmpRect.contains(nX, nY)) {
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700429 // For a moving operation we allow the pointer to go out of the stack bounds, but
430 // use the clamped pointer position for the drag bounds computation.
431 nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
432 nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
Chong Zhangb15758a2015-11-17 12:12:03 -0800433 }
434
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700435 updateWindowDragBounds(nX, nY, mTmpRect);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700436 return false;
Chong Zhangb15758a2015-11-17 12:12:03 -0800437 }
438
skuhne@google.com322347b2016-12-02 12:54:03 -0800439 /**
440 * The user is drag - resizing the window.
441 *
442 * @param x The x coordinate of the current drag coordinate.
443 * @param y the y coordinate of the current drag coordinate.
444 */
445 @VisibleForTesting
446 void resizeDrag(float x, float y) {
447 // This is a resizing operation.
448 // We need to keep various constraints:
449 // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
450 // 2. The orientation is kept - if required.
451 final int deltaX = Math.round(x - mStartDragX);
452 final int deltaY = Math.round(y - mStartDragY);
453 int left = mWindowOriginalBounds.left;
454 int top = mWindowOriginalBounds.top;
455 int right = mWindowOriginalBounds.right;
456 int bottom = mWindowOriginalBounds.bottom;
457
458 // The aspect which we have to respect. Note that if the orientation does not need to be
459 // preserved the aspect will be calculated as 1.0 which neutralizes the following
460 // computations.
461 final float minAspect = !mPreserveOrientation
462 ? 1.0f
463 : (mStartOrientationWasLandscape ? MIN_ASPECT : (1.0f / MIN_ASPECT));
464 // Calculate the resulting width and height of the drag operation.
465 int width = right - left;
466 int height = bottom - top;
467 if ((mCtrlType & CTRL_LEFT) != 0) {
468 width = Math.max(mMinVisibleWidth, width - deltaX);
469 } else if ((mCtrlType & CTRL_RIGHT) != 0) {
470 width = Math.max(mMinVisibleWidth, width + deltaX);
471 }
472 if ((mCtrlType & CTRL_TOP) != 0) {
473 height = Math.max(mMinVisibleHeight, height - deltaY);
474 } else if ((mCtrlType & CTRL_BOTTOM) != 0) {
475 height = Math.max(mMinVisibleHeight, height + deltaY);
476 }
477
478 // If we have to preserve the orientation - check that we are doing so.
479 final float aspect = (float) width / (float) height;
480 if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT)
481 || (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
482 // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
483 // drag axis. What ever is producing the bigger rectangle will be chosen.
484 int width1;
485 int width2;
486 int height1;
487 int height2;
488 if (mStartOrientationWasLandscape) {
489 // Assuming that the width is our target we calculate the height.
490 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
491 height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT));
492 if (height1 < mMinVisibleHeight) {
493 // If the resulting height is too small we adjust to the minimal size.
494 height1 = mMinVisibleHeight;
495 width1 = Math.max(mMinVisibleWidth,
496 Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT)));
497 }
498 // Assuming that the height is our target we calculate the width.
499 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
500 width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT));
501 if (width2 < mMinVisibleWidth) {
502 // If the resulting width is too small we adjust to the minimal size.
503 width2 = mMinVisibleWidth;
504 height2 = Math.max(mMinVisibleHeight,
505 Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT)));
506 }
507 } else {
508 // Assuming that the width is our target we calculate the height.
509 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
510 height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT));
511 if (height1 < mMinVisibleHeight) {
512 // If the resulting height is too small we adjust to the minimal size.
513 height1 = mMinVisibleHeight;
514 width1 = Math.max(mMinVisibleWidth,
515 Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT)));
516 }
517 // Assuming that the height is our target we calculate the width.
518 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
519 width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT));
520 if (width2 < mMinVisibleWidth) {
521 // If the resulting width is too small we adjust to the minimal size.
522 width2 = mMinVisibleWidth;
523 height2 = Math.max(mMinVisibleHeight,
524 Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT)));
525 }
526 }
527
528 // Use the bigger of the two rectangles if the major change was positive, otherwise
529 // do the opposite.
530 final boolean grows = width > (right - left) || height > (bottom - top);
531 if (grows == (width1 * height1 > width2 * height2)) {
532 width = width1;
533 height = height1;
534 } else {
535 width = width2;
536 height = height2;
537 }
538 }
539
540 // Update mWindowDragBounds to the new drag size.
541 updateDraggedBounds(left, top, right, bottom, width, height);
542 }
543
544 /**
545 * Given the old coordinates and the new width and height, update the mWindowDragBounds.
546 *
547 * @param left The original left bound before the user started dragging.
548 * @param top The original top bound before the user started dragging.
549 * @param right The original right bound before the user started dragging.
550 * @param bottom The original bottom bound before the user started dragging.
551 * @param newWidth The new dragged width.
552 * @param newHeight The new dragged height.
553 */
554 void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth,
555 int newHeight) {
556 // Generate the final bounds by keeping the opposite drag edge constant.
557 if ((mCtrlType & CTRL_LEFT) != 0) {
558 left = right - newWidth;
559 } else { // Note: The right might have changed - if we pulled at the right or not.
560 right = left + newWidth;
561 }
562 if ((mCtrlType & CTRL_TOP) != 0) {
563 top = bottom - newHeight;
564 } else { // Note: The height might have changed - if we pulled at the bottom or not.
565 bottom = top + newHeight;
566 }
567
568 mWindowDragBounds.set(left, top, right, bottom);
569
570 checkBoundsForOrientationViolations(mWindowDragBounds);
571 }
572
573 /**
574 * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
575 *
576 * @param bounds The bounds to be checked.
577 */
578 private void checkBoundsForOrientationViolations(Rect bounds) {
579 // When using debug check that we are not violating the given constraints.
580 if (DEBUG_ORIENTATION_VIOLATIONS) {
581 if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
582 Slog.e(TAG, "Orientation violation detected! should be "
583 + (mStartOrientationWasLandscape ? "landscape" : "portrait")
584 + " but is the other");
585 } else {
586 Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
587 }
588 if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
589 Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
590 + ", " + bounds.width() + ") Height(min,is)=("
591 + mMinVisibleHeight + ", " + bounds.height() + ")");
592 }
593 if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
594 Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
595 + ", " + bounds.width() + ") Height(min,is)=("
596 + mMaxVisibleSize.y + ", " + bounds.height() + ")");
597 }
598 }
599 }
600
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700601 private void updateWindowDragBounds(int x, int y, Rect stackBounds) {
602 final int offsetX = Math.round(x - mStartDragX);
603 final int offsetY = Math.round(y - mStartDragY);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700604 mWindowDragBounds.set(mWindowOriginalBounds);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700605 // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
606 final int maxLeft = stackBounds.right - mMinVisibleWidth;
607 final int minLeft = stackBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700608
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700609 // Vertically, the top mMinVisibleHeight of the window should remain visible.
610 // (This assumes that the window caption bar is at the top of the window).
611 final int minTop = stackBounds.top;
612 final int maxTop = stackBounds.bottom - mMinVisibleHeight;
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700613
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700614 mWindowDragBounds.offsetTo(
615 Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
616 Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
617
Chong Zhangb15758a2015-11-17 12:12:03 -0800618 if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
619 "updateWindowDragBounds: " + mWindowDragBounds);
Chong Zhang8e89b312015-09-09 15:09:30 -0700620 }
621
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700622 public String toShortString() {
623 return TAG;
624 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700625}