blob: 650d6a4653b56e66b16511b286f695c9435604d4 [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
Matthew Ngbf155872017-10-27 15:24:39 -070019import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
20import static android.app.ActivityManager.SPLIT_SCREEN_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;
Wale Ogunwale228d4042015-09-13 10:17:34 -070023import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070024import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080025import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
26import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
27import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
28import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
29import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070030import static com.android.server.wm.WindowManagerService.dipToPixel;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +010031import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
Wale Ogunwale231b06e2015-09-16 12:03:09 -070032import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
33import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
Chong Zhang8e89b312015-09-09 15:09:30 -070034
35import android.annotation.IntDef;
36import android.graphics.Point;
37import android.graphics.Rect;
38import android.os.Looper;
39import android.os.Process;
40import android.os.RemoteException;
Wale Ogunwalecad05a02015-09-25 10:41:44 -070041import android.os.Trace;
Chong Zhang8e89b312015-09-09 15:09:30 -070042import android.util.DisplayMetrics;
43import android.util.Slog;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080044import android.view.BatchedInputEventReceiver;
Wale Ogunwale4f52bc62015-09-24 13:47:31 -070045import android.view.Choreographer;
Chong Zhang8e89b312015-09-09 15:09:30 -070046import android.view.Display;
Wale Ogunwale228d4042015-09-13 10:17:34 -070047import android.view.DisplayInfo;
Chong Zhang8e89b312015-09-09 15:09:30 -070048import android.view.InputChannel;
49import android.view.InputDevice;
50import android.view.InputEvent;
Chong Zhang8e89b312015-09-09 15:09:30 -070051import android.view.MotionEvent;
52import android.view.WindowManager;
53
skuhne@google.com322347b2016-12-02 12:54:03 -080054import com.android.internal.annotations.VisibleForTesting;
Wale Ogunwale228d4042015-09-13 10:17:34 -070055import com.android.server.input.InputApplicationHandle;
56import com.android.server.input.InputWindowHandle;
57import com.android.server.wm.WindowManagerService.H;
58
59import java.lang.annotation.Retention;
60import java.lang.annotation.RetentionPolicy;
61
62class TaskPositioner implements DimLayer.DimLayerUser {
skuhne@google.com322347b2016-12-02 12:54:03 -080063 private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
Jorim Jaggibc5425c2016-03-01 13:51:16 +010064 private static final String TAG_LOCAL = "TaskPositioner";
65 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
Chong Zhang8e89b312015-09-09 15:09:30 -070066
Wale Ogunwale228d4042015-09-13 10:17:34 -070067 // The margin the pointer position has to be within the side of the screen to be
68 // considered at the side of the screen.
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -070069 static final int SIDE_MARGIN_DIP = 100;
Wale Ogunwale228d4042015-09-13 10:17:34 -070070
Chong Zhang8e89b312015-09-09 15:09:30 -070071 @IntDef(flag = true,
72 value = {
73 CTRL_NONE,
74 CTRL_LEFT,
75 CTRL_RIGHT,
76 CTRL_TOP,
77 CTRL_BOTTOM
78 })
79 @Retention(RetentionPolicy.SOURCE)
80 @interface CtrlType {}
81
82 private static final int CTRL_NONE = 0x0;
83 private static final int CTRL_LEFT = 0x1;
84 private static final int CTRL_RIGHT = 0x2;
85 private static final int CTRL_TOP = 0x4;
86 private static final int CTRL_BOTTOM = 0x8;
87
Filip Gruszczynski64b6b442016-01-18 13:20:58 -080088 public static final float RESIZING_HINT_ALPHA = 0.5f;
89
90 public static final int RESIZING_HINT_DURATION_MS = 0;
91
skuhne@google.com322347b2016-12-02 12:54:03 -080092 // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
93 // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
94 // aspect he desires.
95 @VisibleForTesting
96 static final float MIN_ASPECT = 1.2f;
97
Chong Zhang8e89b312015-09-09 15:09:30 -070098 private final WindowManagerService mService;
99 private WindowPositionerEventReceiver mInputEventReceiver;
100 private Display mDisplay;
101 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700102 private DimLayer mDimLayer;
103 @CtrlType
104 private int mCurrentDimSide;
105 private Rect mTmpRect = new Rect();
106 private int mSideMargin;
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700107 private int mMinVisibleWidth;
108 private int mMinVisibleHeight;
Chong Zhang8e89b312015-09-09 15:09:30 -0700109
Chong Zhang3005e752015-09-18 18:46:28 -0700110 private Task mTask;
Chong Zhang09b21ef2015-09-14 10:20:21 -0700111 private boolean mResizing;
skuhne@google.com322347b2016-12-02 12:54:03 -0800112 private boolean mPreserveOrientation;
113 private boolean mStartOrientationWasLandscape;
Chong Zhang8e89b312015-09-09 15:09:30 -0700114 private final Rect mWindowOriginalBounds = new Rect();
115 private final Rect mWindowDragBounds = new Rect();
skuhne@google.com322347b2016-12-02 12:54:03 -0800116 private final Point mMaxVisibleSize = new Point();
Chong Zhang8e89b312015-09-09 15:09:30 -0700117 private float mStartDragX;
118 private float mStartDragY;
119 @CtrlType
120 private int mCtrlType = CTRL_NONE;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700121 private boolean mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700122
123 InputChannel mServerChannel;
124 InputChannel mClientChannel;
125 InputApplicationHandle mDragApplicationHandle;
126 InputWindowHandle mDragWindowHandle;
127
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700128 private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
129 public WindowPositionerEventReceiver(
130 InputChannel inputChannel, Looper looper, Choreographer choreographer) {
131 super(inputChannel, looper, choreographer);
Chong Zhang8e89b312015-09-09 15:09:30 -0700132 }
133
134 @Override
Tarandeep Singhe1cfcf42017-07-10 18:50:00 -0700135 public void onInputEvent(InputEvent event, int displayId) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700136 if (!(event instanceof MotionEvent)
137 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
138 return;
139 }
140 final MotionEvent motionEvent = (MotionEvent) event;
141 boolean handled = false;
142
143 try {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700144 if (mDragEnded) {
145 // The drag has ended but the clean-up message has not been processed by
146 // window manager. Drop events that occur after this until window manager
147 // has a chance to clean-up the input handle.
148 handled = true;
149 return;
150 }
151
Chong Zhang8e89b312015-09-09 15:09:30 -0700152 final float newX = motionEvent.getRawX();
153 final float newY = motionEvent.getRawY();
154
155 switch (motionEvent.getAction()) {
156 case MotionEvent.ACTION_DOWN: {
157 if (DEBUG_TASK_POSITIONING) {
158 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
159 }
160 } break;
161
162 case MotionEvent.ACTION_MOVE: {
163 if (DEBUG_TASK_POSITIONING){
164 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
165 }
166 synchronized (mService.mWindowMap) {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700167 mDragEnded = notifyMoveLocked(newX, newY);
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800168 mTask.getDimBounds(mTmpRect);
Chong Zhang8e89b312015-09-09 15:09:30 -0700169 }
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700170 if (!mTmpRect.equals(mWindowDragBounds)) {
171 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
172 "wm.TaskPositioner.resizeTask");
173 try {
174 mService.mActivityManager.resizeTask(
175 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
176 } catch (RemoteException e) {
177 }
178 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
179 }
Chong Zhang8e89b312015-09-09 15:09:30 -0700180 } break;
181
182 case MotionEvent.ACTION_UP: {
183 if (DEBUG_TASK_POSITIONING) {
184 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
185 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700186 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700187 } break;
188
189 case MotionEvent.ACTION_CANCEL: {
190 if (DEBUG_TASK_POSITIONING) {
191 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
192 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700193 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700194 } break;
195 }
196
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700197 if (mDragEnded) {
Wale Ogunwale04ad7b12015-10-02 12:43:27 -0700198 final boolean wasResizing = mResizing;
Chong Zhang3005e752015-09-18 18:46:28 -0700199 synchronized (mService.mWindowMap) {
200 endDragLocked();
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700201 mTask.getDimBounds(mTmpRect);
Chong Zhang3005e752015-09-18 18:46:28 -0700202 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700203 try {
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700204 if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
Chong Zhang3005e752015-09-18 18:46:28 -0700205 // We were using fullscreen surface during resizing. Request
206 // resizeTask() one last time to restore surface to window size.
207 mService.mActivityManager.resizeTask(
Chong Zhang6de2ae82015-09-30 18:25:21 -0700208 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
Chong Zhang3005e752015-09-18 18:46:28 -0700209 }
210
211 if (mCurrentDimSide != CTRL_NONE) {
212 final int createMode = mCurrentDimSide == CTRL_LEFT
Matthew Ngbf155872017-10-27 15:24:39 -0700213 ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
214 : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
215 mService.mActivityManager.setTaskWindowingModeSplitScreenPrimary(
Jorim Jaggi9ea2f7b2015-11-23 18:08:28 -0800216 mTask.mTaskId, createMode, true /*toTop*/, true /* animate */,
Matthew Ng330757d2017-02-28 14:19:17 -0800217 null /* initialBounds */);
Chong Zhang3005e752015-09-18 18:46:28 -0700218 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700219 } catch(RemoteException e) {}
Chong Zhang3005e752015-09-18 18:46:28 -0700220
Chong Zhang8e89b312015-09-09 15:09:30 -0700221 // Post back to WM to handle clean-ups. We still need the input
222 // event handler for the last finishInputEvent()!
223 mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
224 }
225 handled = true;
226 } catch (Exception e) {
227 Slog.e(TAG, "Exception caught by drag handleMotion", e);
228 } finally {
229 finishInputEvent(event, handled);
230 }
231 }
232 }
233
234 TaskPositioner(WindowManagerService service) {
235 mService = service;
236 }
237
skuhne@google.com322347b2016-12-02 12:54:03 -0800238 @VisibleForTesting
239 Rect getWindowDragBounds() {
240 return mWindowDragBounds;
241 }
242
Chong Zhang8e89b312015-09-09 15:09:30 -0700243 /**
244 * @param display The Display that the window being dragged is on.
245 */
246 void register(Display display) {
247 if (DEBUG_TASK_POSITIONING) {
248 Slog.d(TAG, "Registering task positioner");
249 }
250
251 if (mClientChannel != null) {
252 Slog.e(TAG, "Task positioner already registered");
253 return;
254 }
255
256 mDisplay = display;
257 mDisplay.getMetrics(mDisplayMetrics);
258 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
259 mServerChannel = channels[0];
260 mClientChannel = channels[1];
261 mService.mInputManager.registerInputChannel(mServerChannel, null);
262
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700263 mInputEventReceiver = new WindowPositionerEventReceiver(
Jorim Jaggied7993b2017-03-28 18:50:01 +0100264 mClientChannel, mService.mAnimationHandler.getLooper(),
265 mService.mAnimator.getChoreographer());
Chong Zhang8e89b312015-09-09 15:09:30 -0700266
267 mDragApplicationHandle = new InputApplicationHandle(null);
268 mDragApplicationHandle.name = TAG;
269 mDragApplicationHandle.dispatchingTimeoutNanos =
270 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
271
Vladislav Kaznacheev3787de12016-12-21 10:36:35 -0800272 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
Chong Zhang8e89b312015-09-09 15:09:30 -0700273 mDisplay.getDisplayId());
274 mDragWindowHandle.name = TAG;
275 mDragWindowHandle.inputChannel = mServerChannel;
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700276 mDragWindowHandle.layer = mService.getDragLayerLocked();
Chong Zhang8e89b312015-09-09 15:09:30 -0700277 mDragWindowHandle.layoutParamsFlags = 0;
278 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
279 mDragWindowHandle.dispatchingTimeoutNanos =
280 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
281 mDragWindowHandle.visible = true;
282 mDragWindowHandle.canReceiveKeys = false;
283 mDragWindowHandle.hasFocus = true;
284 mDragWindowHandle.hasWallpaper = false;
285 mDragWindowHandle.paused = false;
286 mDragWindowHandle.ownerPid = Process.myPid();
287 mDragWindowHandle.ownerUid = Process.myUid();
288 mDragWindowHandle.inputFeatures = 0;
289 mDragWindowHandle.scaleFactor = 1.0f;
290
291 // The drag window cannot receive new touches.
292 mDragWindowHandle.touchableRegion.setEmpty();
293
294 // The drag window covers the entire display
295 mDragWindowHandle.frameLeft = 0;
296 mDragWindowHandle.frameTop = 0;
297 final Point p = new Point();
298 mDisplay.getRealSize(p);
299 mDragWindowHandle.frameRight = p.x;
300 mDragWindowHandle.frameBottom = p.y;
301
302 // Pause rotations before a drag.
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800303 if (DEBUG_ORIENTATION) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700304 Slog.d(TAG, "Pausing rotation during re-position");
305 }
306 mService.pauseRotationLocked();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700307
Jorim Jaggibc5425c2016-03-01 13:51:16 +0100308 mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700309 mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
310 mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
311 mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
skuhne@google.com322347b2016-12-02 12:54:03 -0800312 mDisplay.getRealSize(mMaxVisibleSize);
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700313
314 mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700315 }
316
317 void unregister() {
318 if (DEBUG_TASK_POSITIONING) {
319 Slog.d(TAG, "Unregistering task positioner");
320 }
321
322 if (mClientChannel == null) {
323 Slog.e(TAG, "Task positioner not registered");
324 return;
325 }
326
327 mService.mInputManager.unregisterInputChannel(mServerChannel);
328
329 mInputEventReceiver.dispose();
330 mInputEventReceiver = null;
331 mClientChannel.dispose();
332 mServerChannel.dispose();
333 mClientChannel = null;
334 mServerChannel = null;
335
336 mDragWindowHandle = null;
337 mDragApplicationHandle = null;
338 mDisplay = null;
339
Wale Ogunwale228d4042015-09-13 10:17:34 -0700340 if (mDimLayer != null) {
341 mDimLayer.destroySurface();
342 mDimLayer = null;
343 }
344 mCurrentDimSide = CTRL_NONE;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700345 mDragEnded = true;
Wale Ogunwale228d4042015-09-13 10:17:34 -0700346
Chong Zhang8e89b312015-09-09 15:09:30 -0700347 // Resume rotations after a drag.
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800348 if (DEBUG_ORIENTATION) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700349 Slog.d(TAG, "Resuming rotation after re-position");
350 }
351 mService.resumeRotationLocked();
352 }
353
skuhne@google.com322347b2016-12-02 12:54:03 -0800354 void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX,
355 float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700356 if (DEBUG_TASK_POSITIONING) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800357 Slog.d(TAG, "startDrag: win=" + win + ", resize=" + resize
358 + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
359 + startY + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700360 }
Chong Zhangd8ceb852015-11-11 14:53:41 -0800361 mTask = win.getTask();
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700362 // Use the dim bounds, not the original task bounds. The cursor
363 // movement should be calculated relative to the visible bounds.
364 // Also, use the dim bounds of the task which accounts for
365 // multiple app windows. Don't use any bounds from win itself as it
366 // may not be the same size as the task.
367 mTask.getDimBounds(mTmpRect);
skuhne@google.com322347b2016-12-02 12:54:03 -0800368 startDrag(resize, preserveOrientation, startX, startY, mTmpRect);
369 }
370
371 @VisibleForTesting
372 void startDrag(boolean resize, boolean preserveOrientation,
373 float startX, float startY, Rect startBounds) {
374 mCtrlType = CTRL_NONE;
375 mStartDragX = startX;
376 mStartDragY = startY;
377 mPreserveOrientation = preserveOrientation;
Chong Zhangd8ceb852015-11-11 14:53:41 -0800378
Chong Zhang8e89b312015-09-09 15:09:30 -0700379 if (resize) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800380 if (startX < startBounds.left) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700381 mCtrlType |= CTRL_LEFT;
382 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800383 if (startX > startBounds.right) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700384 mCtrlType |= CTRL_RIGHT;
385 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800386 if (startY < startBounds.top) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700387 mCtrlType |= CTRL_TOP;
388 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800389 if (startY > startBounds.bottom) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700390 mCtrlType |= CTRL_BOTTOM;
391 }
skuhne@google.com322347b2016-12-02 12:54:03 -0800392 mResizing = mCtrlType != CTRL_NONE;
Chong Zhang8e89b312015-09-09 15:09:30 -0700393 }
394
skuhne@google.com322347b2016-12-02 12:54:03 -0800395 // In case of !isDockedInEffect we are using the union of all task bounds. These might be
396 // made up out of multiple windows which are only partially overlapping. When that happens,
397 // the orientation from the window of interest to the entire stack might diverge. However
398 // for now we treat them as the same.
399 mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
400 mWindowOriginalBounds.set(startBounds);
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700401
Tomasz Mikolajewski79e8d172017-11-21 11:21:42 +0900402 // Notify the app that resizing has started, even though we haven't received any new
403 // bounds yet. This will guarantee that the app starts the backdrop renderer before
404 // configuration changes which could cause an activity restart.
405 if (mResizing) {
406 synchronized (mService.mWindowMap) {
407 notifyMoveLocked(startX, startY);
408 }
409
410 // Perform the resize on the WMS handler thread when we don't have the WMS lock held
411 // to ensure that we don't deadlock WMS and AMS. Note that WindowPositionerEventReceiver
412 // callbacks are delivered on the same handler so this initial resize is always
413 // guaranteed to happen before subsequent drag resizes.
414 mService.mH.post(() -> {
415 try {
416 mService.mActivityManager.resizeTask(
417 mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
418 } catch (RemoteException e) {
419 }
420 });
421 }
422
Vladislav Kaznacheev824bc5d2016-04-22 17:48:35 -0700423 // Make sure we always have valid drag bounds even if the drag ends before any move events
424 // have been handled.
skuhne@google.com322347b2016-12-02 12:54:03 -0800425 mWindowDragBounds.set(startBounds);
Chong Zhang3005e752015-09-18 18:46:28 -0700426 }
427
428 private void endDragLocked() {
429 mResizing = false;
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100430 mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM);
Chong Zhang8e89b312015-09-09 15:09:30 -0700431 }
432
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700433 /** Returns true if the move operation should be ended. */
434 private boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700435 if (DEBUG_TASK_POSITIONING) {
Chong Zhangb15758a2015-11-17 12:12:03 -0800436 Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
Chong Zhang8e89b312015-09-09 15:09:30 -0700437 }
438
439 if (mCtrlType != CTRL_NONE) {
skuhne@google.com322347b2016-12-02 12:54:03 -0800440 resizeDrag(x, y);
Jorim Jaggi0b46f3c2016-03-14 12:21:37 +0100441 mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700442 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700443 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700444
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700445 // This is a moving or scrolling operation.
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800446 mTask.mStack.getDimBounds(mTmpRect);
Chong Zhangb15758a2015-11-17 12:12:03 -0800447
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700448 int nX = (int) x;
449 int nY = (int) y;
Chong Zhangb15758a2015-11-17 12:12:03 -0800450 if (!mTmpRect.contains(nX, nY)) {
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700451 // For a moving operation we allow the pointer to go out of the stack bounds, but
452 // use the clamped pointer position for the drag bounds computation.
453 nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
454 nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
Chong Zhangb15758a2015-11-17 12:12:03 -0800455 }
456
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700457 updateWindowDragBounds(nX, nY, mTmpRect);
Chong Zhangb15758a2015-11-17 12:12:03 -0800458 updateDimLayerVisibility(nX);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700459 return false;
Chong Zhangb15758a2015-11-17 12:12:03 -0800460 }
461
skuhne@google.com322347b2016-12-02 12:54:03 -0800462 /**
463 * The user is drag - resizing the window.
464 *
465 * @param x The x coordinate of the current drag coordinate.
466 * @param y the y coordinate of the current drag coordinate.
467 */
468 @VisibleForTesting
469 void resizeDrag(float x, float y) {
470 // This is a resizing operation.
471 // We need to keep various constraints:
472 // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
473 // 2. The orientation is kept - if required.
474 final int deltaX = Math.round(x - mStartDragX);
475 final int deltaY = Math.round(y - mStartDragY);
476 int left = mWindowOriginalBounds.left;
477 int top = mWindowOriginalBounds.top;
478 int right = mWindowOriginalBounds.right;
479 int bottom = mWindowOriginalBounds.bottom;
480
481 // The aspect which we have to respect. Note that if the orientation does not need to be
482 // preserved the aspect will be calculated as 1.0 which neutralizes the following
483 // computations.
484 final float minAspect = !mPreserveOrientation
485 ? 1.0f
486 : (mStartOrientationWasLandscape ? MIN_ASPECT : (1.0f / MIN_ASPECT));
487 // Calculate the resulting width and height of the drag operation.
488 int width = right - left;
489 int height = bottom - top;
490 if ((mCtrlType & CTRL_LEFT) != 0) {
491 width = Math.max(mMinVisibleWidth, width - deltaX);
492 } else if ((mCtrlType & CTRL_RIGHT) != 0) {
493 width = Math.max(mMinVisibleWidth, width + deltaX);
494 }
495 if ((mCtrlType & CTRL_TOP) != 0) {
496 height = Math.max(mMinVisibleHeight, height - deltaY);
497 } else if ((mCtrlType & CTRL_BOTTOM) != 0) {
498 height = Math.max(mMinVisibleHeight, height + deltaY);
499 }
500
501 // If we have to preserve the orientation - check that we are doing so.
502 final float aspect = (float) width / (float) height;
503 if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT)
504 || (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
505 // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
506 // drag axis. What ever is producing the bigger rectangle will be chosen.
507 int width1;
508 int width2;
509 int height1;
510 int height2;
511 if (mStartOrientationWasLandscape) {
512 // Assuming that the width is our target we calculate the height.
513 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
514 height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT));
515 if (height1 < mMinVisibleHeight) {
516 // If the resulting height is too small we adjust to the minimal size.
517 height1 = mMinVisibleHeight;
518 width1 = Math.max(mMinVisibleWidth,
519 Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT)));
520 }
521 // Assuming that the height is our target we calculate the width.
522 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
523 width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT));
524 if (width2 < mMinVisibleWidth) {
525 // If the resulting width is too small we adjust to the minimal size.
526 width2 = mMinVisibleWidth;
527 height2 = Math.max(mMinVisibleHeight,
528 Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT)));
529 }
530 } else {
531 // Assuming that the width is our target we calculate the height.
532 width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
533 height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT));
534 if (height1 < mMinVisibleHeight) {
535 // If the resulting height is too small we adjust to the minimal size.
536 height1 = mMinVisibleHeight;
537 width1 = Math.max(mMinVisibleWidth,
538 Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT)));
539 }
540 // Assuming that the height is our target we calculate the width.
541 height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
542 width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT));
543 if (width2 < mMinVisibleWidth) {
544 // If the resulting width is too small we adjust to the minimal size.
545 width2 = mMinVisibleWidth;
546 height2 = Math.max(mMinVisibleHeight,
547 Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT)));
548 }
549 }
550
551 // Use the bigger of the two rectangles if the major change was positive, otherwise
552 // do the opposite.
553 final boolean grows = width > (right - left) || height > (bottom - top);
554 if (grows == (width1 * height1 > width2 * height2)) {
555 width = width1;
556 height = height1;
557 } else {
558 width = width2;
559 height = height2;
560 }
561 }
562
563 // Update mWindowDragBounds to the new drag size.
564 updateDraggedBounds(left, top, right, bottom, width, height);
565 }
566
567 /**
568 * Given the old coordinates and the new width and height, update the mWindowDragBounds.
569 *
570 * @param left The original left bound before the user started dragging.
571 * @param top The original top bound before the user started dragging.
572 * @param right The original right bound before the user started dragging.
573 * @param bottom The original bottom bound before the user started dragging.
574 * @param newWidth The new dragged width.
575 * @param newHeight The new dragged height.
576 */
577 void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth,
578 int newHeight) {
579 // Generate the final bounds by keeping the opposite drag edge constant.
580 if ((mCtrlType & CTRL_LEFT) != 0) {
581 left = right - newWidth;
582 } else { // Note: The right might have changed - if we pulled at the right or not.
583 right = left + newWidth;
584 }
585 if ((mCtrlType & CTRL_TOP) != 0) {
586 top = bottom - newHeight;
587 } else { // Note: The height might have changed - if we pulled at the bottom or not.
588 bottom = top + newHeight;
589 }
590
591 mWindowDragBounds.set(left, top, right, bottom);
592
593 checkBoundsForOrientationViolations(mWindowDragBounds);
594 }
595
596 /**
597 * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
598 *
599 * @param bounds The bounds to be checked.
600 */
601 private void checkBoundsForOrientationViolations(Rect bounds) {
602 // When using debug check that we are not violating the given constraints.
603 if (DEBUG_ORIENTATION_VIOLATIONS) {
604 if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
605 Slog.e(TAG, "Orientation violation detected! should be "
606 + (mStartOrientationWasLandscape ? "landscape" : "portrait")
607 + " but is the other");
608 } else {
609 Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
610 }
611 if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
612 Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
613 + ", " + bounds.width() + ") Height(min,is)=("
614 + mMinVisibleHeight + ", " + bounds.height() + ")");
615 }
616 if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
617 Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
618 + ", " + bounds.width() + ") Height(min,is)=("
619 + mMaxVisibleSize.y + ", " + bounds.height() + ")");
620 }
621 }
622 }
623
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700624 private void updateWindowDragBounds(int x, int y, Rect stackBounds) {
625 final int offsetX = Math.round(x - mStartDragX);
626 final int offsetY = Math.round(y - mStartDragY);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700627 mWindowDragBounds.set(mWindowOriginalBounds);
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700628 // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
629 final int maxLeft = stackBounds.right - mMinVisibleWidth;
630 final int minLeft = stackBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700631
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700632 // Vertically, the top mMinVisibleHeight of the window should remain visible.
633 // (This assumes that the window caption bar is at the top of the window).
634 final int minTop = stackBounds.top;
635 final int maxTop = stackBounds.bottom - mMinVisibleHeight;
Vladislav Kaznacheeva90a3b82016-04-27 14:54:53 -0700636
Chong Zhang2e2c81a2016-07-15 11:28:17 -0700637 mWindowDragBounds.offsetTo(
638 Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
639 Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
640
Chong Zhangb15758a2015-11-17 12:12:03 -0800641 if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
642 "updateWindowDragBounds: " + mWindowDragBounds);
Chong Zhang8e89b312015-09-09 15:09:30 -0700643 }
644
Wale Ogunwale228d4042015-09-13 10:17:34 -0700645 private void updateDimLayerVisibility(int x) {
646 @CtrlType
647 int dimSide = getDimSide(x);
648 if (dimSide == mCurrentDimSide) {
649 return;
650 }
651
652 mCurrentDimSide = dimSide;
653
654 if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
Robert Carr68e5c9e2016-09-14 10:50:09 -0700655 mService.openSurfaceTransaction();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700656 if (mCurrentDimSide == CTRL_NONE) {
657 mDimLayer.hide();
658 } else {
659 showDimLayer();
660 }
Adrian Roos111aff92017-09-27 18:11:46 +0200661 mService.closeSurfaceTransaction("updateDimLayerVisibility");
Wale Ogunwale228d4042015-09-13 10:17:34 -0700662 }
663
664 /**
665 * Returns the side of the screen the dim layer should be shown.
666 * @param x horizontal coordinate used to determine if the dim layer should be shown
667 * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
668 * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
669 * shouldn't be shown.
670 */
671 private int getDimSide(int x) {
Wale Ogunwale44f036f2017-09-29 05:09:09 -0700672 if (!mTask.mStack.inFreeformWindowingMode()
Wale Ogunwale14a3fb92016-09-11 15:19:05 -0700673 || !mTask.mStack.fillsParent()
Andrii Kulian441e4492016-09-29 15:25:00 -0700674 || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700675 return CTRL_NONE;
676 }
677
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800678 mTask.mStack.getDimBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700679 if (x - mSideMargin <= mTmpRect.left) {
680 return CTRL_LEFT;
681 }
682 if (x + mSideMargin >= mTmpRect.right) {
683 return CTRL_RIGHT;
684 }
685
686 return CTRL_NONE;
687 }
688
689 private void showDimLayer() {
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800690 mTask.mStack.getDimBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700691 if (mCurrentDimSide == CTRL_LEFT) {
692 mTmpRect.right = mTmpRect.centerX();
693 } else if (mCurrentDimSide == CTRL_RIGHT) {
694 mTmpRect.left = mTmpRect.centerX();
695 }
696
697 mDimLayer.setBounds(mTmpRect);
Filip Gruszczynski57b6cce2015-10-06 09:50:51 -0700698 mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
699 RESIZING_HINT_DURATION_MS);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700700 }
701
702 @Override /** {@link DimLayer.DimLayerUser} */
Wale Ogunwale29bfbb82016-05-12 15:13:52 -0700703 public boolean dimFullscreen() {
704 return isFullscreen();
705 }
706
707 boolean isFullscreen() {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700708 return false;
709 }
710
711 @Override /** {@link DimLayer.DimLayerUser} */
712 public DisplayInfo getDisplayInfo() {
Chong Zhang3005e752015-09-18 18:46:28 -0700713 return mTask.mStack.getDisplayInfo();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700714 }
715
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700716 @Override
Wale Ogunwalef0a60a92017-01-19 09:44:40 -0800717 public boolean isAttachedToDisplay() {
718 return mTask != null && mTask.getDisplayContent() != null;
719 }
720
721 @Override
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800722 public void getDimBounds(Rect out) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700723 // This dim layer user doesn't need this.
724 }
725
726 @Override
727 public String toShortString() {
728 return TAG;
729 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700730}