blob: 0c68e2ca6d7728da931350b05285dbfb0c09c087 [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 Ogunwale59a73ca2015-09-14 12:54:50 -070019import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
20import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
Chong Zhang87b21722015-09-21 15:39:51 -070021import static android.app.ActivityManager.RESIZE_MODE_USER;
Chong Zhang6de2ae82015-09-30 18:25:21 -070022import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080023import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
Wale Ogunwale228d4042015-09-13 10:17:34 -070024import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070025import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080026import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
27import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
28import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
29import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
30import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070031import static com.android.server.wm.WindowManagerService.dipToPixel;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +010032import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
Wale Ogunwale231b06e2015-09-16 12:03:09 -070033import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
34import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
Chong Zhang8e89b312015-09-09 15:09:30 -070035
36import android.annotation.IntDef;
37import android.graphics.Point;
38import android.graphics.Rect;
39import android.os.Looper;
40import android.os.Process;
41import android.os.RemoteException;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070042import android.os.Trace;
Chong Zhang8e89b312015-09-09 15:09:30 -070043import android.util.DisplayMetrics;
44import android.util.Slog;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080045import android.view.BatchedInputEventReceiver;
Wale Ogunwale4f52bc62015-09-24 13:47:31 -070046import android.view.Choreographer;
Chong Zhang8e89b312015-09-09 15:09:30 -070047import android.view.Display;
Wale Ogunwale228d4042015-09-13 10:17:34 -070048import android.view.DisplayInfo;
Chong Zhang8e89b312015-09-09 15:09:30 -070049import android.view.InputChannel;
50import android.view.InputDevice;
51import android.view.InputEvent;
Chong Zhang8e89b312015-09-09 15:09:30 -070052import android.view.MotionEvent;
53import android.view.WindowManager;
54
skuhne@google.com322347b2016-12-02 12:54:03 -080055import com.android.internal.annotations.VisibleForTesting;
Wale Ogunwale228d4042015-09-13 10:17:34 -070056import com.android.server.input.InputApplicationHandle;
57import com.android.server.input.InputWindowHandle;
58import com.android.server.wm.WindowManagerService.H;
59
60import java.lang.annotation.Retention;
61import java.lang.annotation.RetentionPolicy;
62
63class TaskPositioner implements DimLayer.DimLayerUser {
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
Wale Ogunwale228d4042015-09-13 10:17:34 -070068 // The margin the pointer position has to be within the side of the screen to be
69 // considered at the side of the screen.
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070070 static final int SIDE_MARGIN_DIP = 100;
Wale Ogunwale228d4042015-09-13 10:17:34 -070071
Chong Zhang8e89b312015-09-09 15:09:30 -070072 @IntDef(flag = true,
73 value = {
74 CTRL_NONE,
75 CTRL_LEFT,
76 CTRL_RIGHT,
77 CTRL_TOP,
78 CTRL_BOTTOM
79 })
80 @Retention(RetentionPolicy.SOURCE)
81 @interface CtrlType {}
82
83 private static final int CTRL_NONE = 0x0;
84 private static final int CTRL_LEFT = 0x1;
85 private static final int CTRL_RIGHT = 0x2;
86 private static final int CTRL_TOP = 0x4;
87 private static final int CTRL_BOTTOM = 0x8;
88
Filip Gruszczynski64b6b442016-01-18 13:20:58 -080089 public static final float RESIZING_HINT_ALPHA = 0.5f;
90
91 public static final int RESIZING_HINT_DURATION_MS = 0;
92
skuhne@google.com322347b2016-12-02 12:54:03 -080093 // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
94 // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
95 // aspect he desires.
96 @VisibleForTesting
97 static final float MIN_ASPECT = 1.2f;
98
Chong Zhang8e89b312015-09-09 15:09:30 -070099 private final WindowManagerService mService;
100 private WindowPositionerEventReceiver mInputEventReceiver;
101 private Display mDisplay;
102 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700103 private DimLayer mDimLayer;
104 @CtrlType
105 private int mCurrentDimSide;
106 private Rect mTmpRect = new Rect();
107 private int mSideMargin;
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700108 private int mMinVisibleWidth;
109 private int mMinVisibleHeight;
Chong Zhang8e89b312015-09-09 15:09:30 -0700110
Chong Zhang3005e752015-09-18 18:46:28 -0700111 private Task mTask;
Chong Zhang09b21ef2015-09-14 10:20:21 -0700112 private boolean mResizing;
skuhne@google.com322347b2016-12-02 12:54:03 -0800113 private boolean mPreserveOrientation;
114 private boolean mStartOrientationWasLandscape;
Chong Zhang8e89b312015-09-09 15:09:30 -0700115 private final Rect mWindowOriginalBounds = new Rect();
116 private final Rect mWindowDragBounds = new Rect();
skuhne@google.com322347b2016-12-02 12:54:03 -0800117 private final Point mMaxVisibleSize = new Point();
Chong Zhang8e89b312015-09-09 15:09:30 -0700118 private float mStartDragX;
119 private float mStartDragY;
120 @CtrlType
121 private int mCtrlType = CTRL_NONE;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700122 private boolean mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700123
124 InputChannel mServerChannel;
125 InputChannel mClientChannel;
126 InputApplicationHandle mDragApplicationHandle;
127 InputWindowHandle mDragWindowHandle;
128
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700129 private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
130 public WindowPositionerEventReceiver(
131 InputChannel inputChannel, Looper looper, Choreographer choreographer) {
132 super(inputChannel, looper, choreographer);
Chong Zhang8e89b312015-09-09 15:09:30 -0700133 }
134
135 @Override
136 public void onInputEvent(InputEvent event) {
137 if (!(event instanceof MotionEvent)
138 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
139 return;
140 }
141 final MotionEvent motionEvent = (MotionEvent) event;
142 boolean handled = false;
143
144 try {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700145 if (mDragEnded) {
146 // The drag has ended but the clean-up message has not been processed by
147 // window manager. Drop events that occur after this until window manager
148 // has a chance to clean-up the input handle.
149 handled = true;
150 return;
151 }
152
Chong Zhang8e89b312015-09-09 15:09:30 -0700153 final float newX = motionEvent.getRawX();
154 final float newY = motionEvent.getRawY();
155
156 switch (motionEvent.getAction()) {
157 case MotionEvent.ACTION_DOWN: {
158 if (DEBUG_TASK_POSITIONING) {
159 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
160 }
161 } break;
162
163 case MotionEvent.ACTION_MOVE: {
164 if (DEBUG_TASK_POSITIONING){
165 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
166 }
167 synchronized (mService.mWindowMap) {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700168 mDragEnded = notifyMoveLocked(newX, newY);
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800169 mTask.getDimBounds(mTmpRect);
Chong Zhang8e89b312015-09-09 15:09:30 -0700170 }
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700171 if (!mTmpRect.equals(mWindowDragBounds)) {
172 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
173 "wm.TaskPositioner.resizeTask");
174 try {
175 mService.mActivityManager.resizeTask(
176 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
177 } catch (RemoteException e) {
178 }
179 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
180 }
Chong Zhang8e89b312015-09-09 15:09:30 -0700181 } break;
182
183 case MotionEvent.ACTION_UP: {
184 if (DEBUG_TASK_POSITIONING) {
185 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
186 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700187 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700188 } break;
189
190 case MotionEvent.ACTION_CANCEL: {
191 if (DEBUG_TASK_POSITIONING) {
192 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
193 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700194 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700195 } break;
196 }
197
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700198 if (mDragEnded) {
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700199 final boolean wasResizing = mResizing;
Chong Zhang3005e752015-09-18 18:46:28 -0700200 synchronized (mService.mWindowMap) {
201 endDragLocked();
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700202 mTask.getDimBounds(mTmpRect);
Chong Zhang3005e752015-09-18 18:46:28 -0700203 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700204 try {
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700205 if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
Chong Zhang3005e752015-09-18 18:46:28 -0700206 // We were using fullscreen surface during resizing. Request
207 // resizeTask() one last time to restore surface to window size.
208 mService.mActivityManager.resizeTask(
Chong Zhang6de2ae82015-09-30 18:25:21 -0700209 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
Chong Zhang3005e752015-09-18 18:46:28 -0700210 }
211
212 if (mCurrentDimSide != CTRL_NONE) {
213 final int createMode = mCurrentDimSide == CTRL_LEFT
214 ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
215 : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
216 mService.mActivityManager.moveTaskToDockedStack(
Jorim Jaggi9ea2f7b2015-11-23 18:08:28 -0800217 mTask.mTaskId, createMode, true /*toTop*/, true /* animate */,
Matthew Ng330757d2017-02-28 14:19:17 -0800218 null /* initialBounds */);
Chong Zhang3005e752015-09-18 18:46:28 -0700219 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700220 } catch(RemoteException e) {}
Chong Zhang3005e752015-09-18 18:46:28 -0700221
Chong Zhang8e89b312015-09-09 15:09:30 -0700222 // Post back to WM to handle clean-ups. We still need the input
223 // event handler for the last finishInputEvent()!
224 mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
225 }
226 handled = true;
227 } catch (Exception e) {
228 Slog.e(TAG, "Exception caught by drag handleMotion", e);
229 } finally {
230 finishInputEvent(event, handled);
231 }
232 }
233 }
234
235 TaskPositioner(WindowManagerService service) {
236 mService = service;
237 }
238
skuhne@google.com322347b2016-12-02 12:54:03 -0800239 @VisibleForTesting
240 Rect getWindowDragBounds() {
241 return mWindowDragBounds;
242 }
243
Chong Zhang8e89b312015-09-09 15:09:30 -0700244 /**
245 * @param display The Display that the window being dragged is on.
246 */
247 void register(Display display) {
248 if (DEBUG_TASK_POSITIONING) {
249 Slog.d(TAG, "Registering task positioner");
250 }
251
252 if (mClientChannel != null) {
253 Slog.e(TAG, "Task positioner already registered");
254 return;
255 }
256
257 mDisplay = display;
258 mDisplay.getMetrics(mDisplayMetrics);
259 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
260 mServerChannel = channels[0];
261 mClientChannel = channels[1];
262 mService.mInputManager.registerInputChannel(mServerChannel, null);
263
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700264 mInputEventReceiver = new WindowPositionerEventReceiver(
Jorim Jaggied7993b2017-03-28 18:50:01 +0100265 mClientChannel, mService.mAnimationHandler.getLooper(),
266 mService.mAnimator.getChoreographer());
Chong Zhang8e89b312015-09-09 15:09:30 -0700267
268 mDragApplicationHandle = new InputApplicationHandle(null);
269 mDragApplicationHandle.name = TAG;
270 mDragApplicationHandle.dispatchingTimeoutNanos =
271 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
272
Vladislav Kaznacheev3787de12016-12-21 10:36:35 -0800273 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
Chong Zhang8e89b312015-09-09 15:09:30 -0700274 mDisplay.getDisplayId());
275 mDragWindowHandle.name = TAG;
276 mDragWindowHandle.inputChannel = mServerChannel;
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700277 mDragWindowHandle.layer = mService.getDragLayerLocked();
Chong Zhang8e89b312015-09-09 15:09:30 -0700278 mDragWindowHandle.layoutParamsFlags = 0;
279 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
280 mDragWindowHandle.dispatchingTimeoutNanos =
281 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
282 mDragWindowHandle.visible = true;
283 mDragWindowHandle.canReceiveKeys = false;
284 mDragWindowHandle.hasFocus = true;
285 mDragWindowHandle.hasWallpaper = false;
286 mDragWindowHandle.paused = false;
287 mDragWindowHandle.ownerPid = Process.myPid();
288 mDragWindowHandle.ownerUid = Process.myUid();
289 mDragWindowHandle.inputFeatures = 0;
290 mDragWindowHandle.scaleFactor = 1.0f;
291
292 // The drag window cannot receive new touches.
293 mDragWindowHandle.touchableRegion.setEmpty();
294
295 // The drag window covers the entire display
296 mDragWindowHandle.frameLeft = 0;
297 mDragWindowHandle.frameTop = 0;
298 final Point p = new Point();
299 mDisplay.getRealSize(p);
300 mDragWindowHandle.frameRight = p.x;
301 mDragWindowHandle.frameBottom = p.y;
302
303 // Pause rotations before a drag.
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800304 if (DEBUG_ORIENTATION) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700305 Slog.d(TAG, "Pausing rotation during re-position");
306 }
307 mService.pauseRotationLocked();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700308
Jorim Jaggibc5425c2016-03-01 13:51:16 +0100309 mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700310 mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
311 mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
312 mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
skuhne@google.com322347b2016-12-02 12:54:03 -0800313 mDisplay.getRealSize(mMaxVisibleSize);
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700314
315 mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700316 }
317
318 void unregister() {
319 if (DEBUG_TASK_POSITIONING) {
320 Slog.d(TAG, "Unregistering task positioner");
321 }
322
323 if (mClientChannel == null) {
324 Slog.e(TAG, "Task positioner not registered");
325 return;
326 }
327
328 mService.mInputManager.unregisterInputChannel(mServerChannel);
329
330 mInputEventReceiver.dispose();
331 mInputEventReceiver = null;
332 mClientChannel.dispose();
333 mServerChannel.dispose();
334 mClientChannel = null;
335 mServerChannel = null;
336
337 mDragWindowHandle = null;
338 mDragApplicationHandle = null;
339 mDisplay = null;
340
Wale Ogunwale228d4042015-09-13 10:17:34 -0700341 if (mDimLayer != null) {
342 mDimLayer.destroySurface();
343 mDimLayer = null;
344 }
345 mCurrentDimSide = CTRL_NONE;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700346 mDragEnded = true;
Wale Ogunwale228d4042015-09-13 10:17:34 -0700347
Chong Zhang8e89b312015-09-09 15:09:30 -0700348 // Resume rotations after a drag.
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800349 if (DEBUG_ORIENTATION) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700350 Slog.d(TAG, "Resuming rotation after re-position");
351 }
352 mService.resumeRotationLocked();
353 }
354
skuhne@google.com322347b2016-12-02 12:54:03 -0800355 void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX,
356 float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700357 if (DEBUG_TASK_POSITIONING) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800358 Slog.d(TAG, "startDrag: win=" + win + ", resize=" + resize
359 + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
360 + startY + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700361 }
Chong Zhangd8ceb852015-11-11 14:53:41 -0800362 mTask = win.getTask();
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700363 // Use the dim bounds, not the original task bounds. The cursor
364 // movement should be calculated relative to the visible bounds.
365 // Also, use the dim bounds of the task which accounts for
366 // multiple app windows. Don't use any bounds from win itself as it
367 // may not be the same size as the task.
368 mTask.getDimBounds(mTmpRect);
skuhne@google.com322347b2016-12-02 12:54:03 -0800369 startDrag(resize, preserveOrientation, startX, startY, mTmpRect);
370 }
371
372 @VisibleForTesting
373 void startDrag(boolean resize, boolean preserveOrientation,
374 float startX, float startY, Rect startBounds) {
375 mCtrlType = CTRL_NONE;
376 mStartDragX = startX;
377 mStartDragY = startY;
378 mPreserveOrientation = preserveOrientation;
Chong Zhangd8ceb852015-11-11 14:53:41 -0800379
Chong Zhang8e89b312015-09-09 15:09:30 -0700380 if (resize) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800381 if (startX < startBounds.left) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700382 mCtrlType |= CTRL_LEFT;
383 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800384 if (startX > startBounds.right) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700385 mCtrlType |= CTRL_RIGHT;
386 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800387 if (startY < startBounds.top) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700388 mCtrlType |= CTRL_TOP;
389 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800390 if (startY > startBounds.bottom) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700391 mCtrlType |= CTRL_BOTTOM;
392 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800393 mResizing = mCtrlType != CTRL_NONE;
Chong Zhang8e89b312015-09-09 15:09:30 -0700394 }
395
skuhne@google.com322347b2016-12-02 12:54:03 -0800396 // In case of !isDockedInEffect we are using the union of all task bounds. These might be
397 // made up out of multiple windows which are only partially overlapping. When that happens,
398 // the orientation from the window of interest to the entire stack might diverge. However
399 // for now we treat them as the same.
400 mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
401 mWindowOriginalBounds.set(startBounds);
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700402
403 // Make sure we always have valid drag bounds even if the drag ends before any move events
404 // have been handled.
skuhne@google.com322347b2016-12-02 12:54:03 -0800405 mWindowDragBounds.set(startBounds);
Chong Zhang3005e752015-09-18 18:46:28 -0700406 }
407
408 private void endDragLocked() {
409 mResizing = false;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100410 mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM);
Chong Zhang8e89b312015-09-09 15:09:30 -0700411 }
412
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700413 /** Returns true if the move operation should be ended. */
414 private boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700415 if (DEBUG_TASK_POSITIONING) {
Chong Zhangb15758a2015-11-17 12:12:03 -0800416 Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700417 }
418
419 if (mCtrlType != CTRL_NONE) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800420 resizeDrag(x, y);
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100421 mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700422 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700423 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700424
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700425 // This is a moving or scrolling operation.
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800426 mTask.mStack.getDimBounds(mTmpRect);
Chong Zhangb15758a2015-11-17 12:12:03 -0800427
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700428 int nX = (int) x;
429 int nY = (int) y;
Chong Zhangb15758a2015-11-17 12:12:03 -0800430 if (!mTmpRect.contains(nX, nY)) {
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700431 // For a moving operation we allow the pointer to go out of the stack bounds, but
432 // use the clamped pointer position for the drag bounds computation.
433 nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
434 nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
Chong Zhangb15758a2015-11-17 12:12:03 -0800435 }
436
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700437 updateWindowDragBounds(nX, nY, mTmpRect);
Chong Zhangb15758a2015-11-17 12:12:03 -0800438 updateDimLayerVisibility(nX);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700439 return false;
Chong Zhangb15758a2015-11-17 12:12:03 -0800440 }
441
skuhne@google.com322347b2016-12-02 12:54:03 -0800442 /**
443 * The user is drag - resizing the window.
444 *
445 * @param x The x coordinate of the current drag coordinate.
446 * @param y the y coordinate of the current drag coordinate.
447 */
448 @VisibleForTesting
449 void resizeDrag(float x, float y) {
450 // This is a resizing operation.
451 // We need to keep various constraints:
452 // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
453 // 2. The orientation is kept - if required.
454 final int deltaX = Math.round(x - mStartDragX);
455 final int deltaY = Math.round(y - mStartDragY);
456 int left = mWindowOriginalBounds.left;
457 int top = mWindowOriginalBounds.top;
458 int right = mWindowOriginalBounds.right;
459 int bottom = mWindowOriginalBounds.bottom;
460
461 // The aspect which we have to respect. Note that if the orientation does not need to be
462 // preserved the aspect will be calculated as 1.0 which neutralizes the following
463 // computations.
464 final float minAspect = !mPreserveOrientation
465 ? 1.0f
466 : (mStartOrientationWasLandscape ? MIN_ASPECT : (1.0f / MIN_ASPECT));
467 // Calculate the resulting width and height of the drag operation.
468 int width = right - left;
469 int height = bottom - top;
470 if ((mCtrlType & CTRL_LEFT) != 0) {
471 width = Math.max(mMinVisibleWidth, width - deltaX);
472 } else if ((mCtrlType & CTRL_RIGHT) != 0) {
473 width = Math.max(mMinVisibleWidth, width + deltaX);
474 }
475 if ((mCtrlType & CTRL_TOP) != 0) {
476 height = Math.max(mMinVisibleHeight, height - deltaY);
477 } else if ((mCtrlType & CTRL_BOTTOM) != 0) {
478 height = Math.max(mMinVisibleHeight, height + deltaY);
479 }
480
481 // If we have to preserve the orientation - check that we are doing so.
482 final float aspect = (float) width / (float) height;
483 if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT)
484 || (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
485 // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
486 // drag axis. What ever is producing the bigger rectangle will be chosen.
487 int width1;
488 int width2;
489 int height1;
490 int height2;
491 if (mStartOrientationWasLandscape) {
492 // Assuming that the width is our target we calculate the height.
493 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
494 height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT));
495 if (height1 < mMinVisibleHeight) {
496 // If the resulting height is too small we adjust to the minimal size.
497 height1 = mMinVisibleHeight;
498 width1 = Math.max(mMinVisibleWidth,
499 Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT)));
500 }
501 // Assuming that the height is our target we calculate the width.
502 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
503 width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT));
504 if (width2 < mMinVisibleWidth) {
505 // If the resulting width is too small we adjust to the minimal size.
506 width2 = mMinVisibleWidth;
507 height2 = Math.max(mMinVisibleHeight,
508 Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT)));
509 }
510 } else {
511 // Assuming that the width is our target we calculate the height.
512 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
513 height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT));
514 if (height1 < mMinVisibleHeight) {
515 // If the resulting height is too small we adjust to the minimal size.
516 height1 = mMinVisibleHeight;
517 width1 = Math.max(mMinVisibleWidth,
518 Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT)));
519 }
520 // Assuming that the height is our target we calculate the width.
521 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
522 width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT));
523 if (width2 < mMinVisibleWidth) {
524 // If the resulting width is too small we adjust to the minimal size.
525 width2 = mMinVisibleWidth;
526 height2 = Math.max(mMinVisibleHeight,
527 Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT)));
528 }
529 }
530
531 // Use the bigger of the two rectangles if the major change was positive, otherwise
532 // do the opposite.
533 final boolean grows = width > (right - left) || height > (bottom - top);
534 if (grows == (width1 * height1 > width2 * height2)) {
535 width = width1;
536 height = height1;
537 } else {
538 width = width2;
539 height = height2;
540 }
541 }
542
543 // Update mWindowDragBounds to the new drag size.
544 updateDraggedBounds(left, top, right, bottom, width, height);
545 }
546
547 /**
548 * Given the old coordinates and the new width and height, update the mWindowDragBounds.
549 *
550 * @param left The original left bound before the user started dragging.
551 * @param top The original top bound before the user started dragging.
552 * @param right The original right bound before the user started dragging.
553 * @param bottom The original bottom bound before the user started dragging.
554 * @param newWidth The new dragged width.
555 * @param newHeight The new dragged height.
556 */
557 void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth,
558 int newHeight) {
559 // Generate the final bounds by keeping the opposite drag edge constant.
560 if ((mCtrlType & CTRL_LEFT) != 0) {
561 left = right - newWidth;
562 } else { // Note: The right might have changed - if we pulled at the right or not.
563 right = left + newWidth;
564 }
565 if ((mCtrlType & CTRL_TOP) != 0) {
566 top = bottom - newHeight;
567 } else { // Note: The height might have changed - if we pulled at the bottom or not.
568 bottom = top + newHeight;
569 }
570
571 mWindowDragBounds.set(left, top, right, bottom);
572
573 checkBoundsForOrientationViolations(mWindowDragBounds);
574 }
575
576 /**
577 * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
578 *
579 * @param bounds The bounds to be checked.
580 */
581 private void checkBoundsForOrientationViolations(Rect bounds) {
582 // When using debug check that we are not violating the given constraints.
583 if (DEBUG_ORIENTATION_VIOLATIONS) {
584 if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
585 Slog.e(TAG, "Orientation violation detected! should be "
586 + (mStartOrientationWasLandscape ? "landscape" : "portrait")
587 + " but is the other");
588 } else {
589 Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
590 }
591 if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
592 Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
593 + ", " + bounds.width() + ") Height(min,is)=("
594 + mMinVisibleHeight + ", " + bounds.height() + ")");
595 }
596 if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
597 Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
598 + ", " + bounds.width() + ") Height(min,is)=("
599 + mMaxVisibleSize.y + ", " + bounds.height() + ")");
600 }
601 }
602 }
603
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700604 private void updateWindowDragBounds(int x, int y, Rect stackBounds) {
605 final int offsetX = Math.round(x - mStartDragX);
606 final int offsetY = Math.round(y - mStartDragY);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700607 mWindowDragBounds.set(mWindowOriginalBounds);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700608 // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
609 final int maxLeft = stackBounds.right - mMinVisibleWidth;
610 final int minLeft = stackBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700611
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700612 // Vertically, the top mMinVisibleHeight of the window should remain visible.
613 // (This assumes that the window caption bar is at the top of the window).
614 final int minTop = stackBounds.top;
615 final int maxTop = stackBounds.bottom - mMinVisibleHeight;
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700616
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700617 mWindowDragBounds.offsetTo(
618 Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
619 Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
620
Chong Zhangb15758a2015-11-17 12:12:03 -0800621 if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
622 "updateWindowDragBounds: " + mWindowDragBounds);
Chong Zhang8e89b312015-09-09 15:09:30 -0700623 }
624
Wale Ogunwale228d4042015-09-13 10:17:34 -0700625 private void updateDimLayerVisibility(int x) {
626 @CtrlType
627 int dimSide = getDimSide(x);
628 if (dimSide == mCurrentDimSide) {
629 return;
630 }
631
632 mCurrentDimSide = dimSide;
633
634 if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
Robert Carr68e5c9e2016-09-14 10:50:09 -0700635 mService.openSurfaceTransaction();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700636 if (mCurrentDimSide == CTRL_NONE) {
637 mDimLayer.hide();
638 } else {
639 showDimLayer();
640 }
Robert Carr68e5c9e2016-09-14 10:50:09 -0700641 mService.closeSurfaceTransaction();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700642 }
643
644 /**
645 * Returns the side of the screen the dim layer should be shown.
646 * @param x horizontal coordinate used to determine if the dim layer should be shown
647 * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
648 * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
649 * shouldn't be shown.
650 */
651 private int getDimSide(int x) {
Chong Zhang3005e752015-09-18 18:46:28 -0700652 if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
Wale Ogunwale14a3fb92016-09-11 15:19:05 -0700653 || !mTask.mStack.fillsParent()
Andrii Kulian441e4492016-09-29 15:25:00 -0700654 || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700655 return CTRL_NONE;
656 }
657
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800658 mTask.mStack.getDimBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700659 if (x - mSideMargin <= mTmpRect.left) {
660 return CTRL_LEFT;
661 }
662 if (x + mSideMargin >= mTmpRect.right) {
663 return CTRL_RIGHT;
664 }
665
666 return CTRL_NONE;
667 }
668
669 private void showDimLayer() {
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800670 mTask.mStack.getDimBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700671 if (mCurrentDimSide == CTRL_LEFT) {
672 mTmpRect.right = mTmpRect.centerX();
673 } else if (mCurrentDimSide == CTRL_RIGHT) {
674 mTmpRect.left = mTmpRect.centerX();
675 }
676
677 mDimLayer.setBounds(mTmpRect);
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700678 mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
679 RESIZING_HINT_DURATION_MS);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700680 }
681
682 @Override /** {@link DimLayer.DimLayerUser} */
Wale Ogunwale29bfbb82016-05-12 15:13:52 -0700683 public boolean dimFullscreen() {
684 return isFullscreen();
685 }
686
687 boolean isFullscreen() {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700688 return false;
689 }
690
691 @Override /** {@link DimLayer.DimLayerUser} */
692 public DisplayInfo getDisplayInfo() {
Chong Zhang3005e752015-09-18 18:46:28 -0700693 return mTask.mStack.getDisplayInfo();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700694 }
695
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700696 @Override
Wale Ogunwalef0a60a92017-01-19 09:44:40 -0800697 public boolean isAttachedToDisplay() {
698 return mTask != null && mTask.getDisplayContent() != null;
699 }
700
701 @Override
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800702 public void getDimBounds(Rect out) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700703 // This dim layer user doesn't need this.
704 }
705
706 @Override
707 public String toShortString() {
708 return TAG;
709 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700710}