blob: 9557d121830d0e78a0a5ace09e72ac8115a81e3f [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;
Wale Ogunwale228d4042015-09-13 10:17:34 -070021import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
Chong Zhang87b21722015-09-21 15:39:51 -070022import static android.app.ActivityManager.RESIZE_MODE_FORCED;
23import static android.app.ActivityManager.RESIZE_MODE_USER;
Wale Ogunwale228d4042015-09-13 10:17:34 -070024import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
Chong Zhang8e89b312015-09-09 15:09:30 -070025import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
Wale Ogunwale228d4042015-09-13 10:17:34 -070026import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
Wale Ogunwale231b06e2015-09-16 12:03:09 -070027import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
28import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
Chong Zhang8e89b312015-09-09 15:09:30 -070029
30import android.annotation.IntDef;
31import android.graphics.Point;
32import android.graphics.Rect;
33import android.os.Looper;
34import android.os.Process;
35import android.os.RemoteException;
36import android.util.DisplayMetrics;
37import android.util.Slog;
Chong Zhang8e89b312015-09-09 15:09:30 -070038import android.view.Display;
Wale Ogunwale228d4042015-09-13 10:17:34 -070039import android.view.DisplayInfo;
Chong Zhang8e89b312015-09-09 15:09:30 -070040import android.view.InputChannel;
41import android.view.InputDevice;
42import android.view.InputEvent;
43import android.view.InputEventReceiver;
44import android.view.MotionEvent;
Wale Ogunwale228d4042015-09-13 10:17:34 -070045import android.view.SurfaceControl;
Chong Zhang8e89b312015-09-09 15:09:30 -070046import android.view.WindowManager;
47
Wale Ogunwale228d4042015-09-13 10:17:34 -070048import com.android.server.input.InputApplicationHandle;
49import com.android.server.input.InputWindowHandle;
50import com.android.server.wm.WindowManagerService.H;
51
52import java.lang.annotation.Retention;
53import java.lang.annotation.RetentionPolicy;
54
55class TaskPositioner implements DimLayer.DimLayerUser {
Chong Zhang8e89b312015-09-09 15:09:30 -070056 private static final String TAG = "TaskPositioner";
57
Wale Ogunwale228d4042015-09-13 10:17:34 -070058 // The margin the pointer position has to be within the side of the screen to be
59 // considered at the side of the screen.
Wale Ogunwalef5a67f52015-09-15 09:54:33 -070060 private static final int SIDE_MARGIN_DIP = 100;
Wale Ogunwale228d4042015-09-13 10:17:34 -070061
Chong Zhang8e89b312015-09-09 15:09:30 -070062 @IntDef(flag = true,
63 value = {
64 CTRL_NONE,
65 CTRL_LEFT,
66 CTRL_RIGHT,
67 CTRL_TOP,
68 CTRL_BOTTOM
69 })
70 @Retention(RetentionPolicy.SOURCE)
71 @interface CtrlType {}
72
73 private static final int CTRL_NONE = 0x0;
74 private static final int CTRL_LEFT = 0x1;
75 private static final int CTRL_RIGHT = 0x2;
76 private static final int CTRL_TOP = 0x4;
77 private static final int CTRL_BOTTOM = 0x8;
78
79 private final WindowManagerService mService;
80 private WindowPositionerEventReceiver mInputEventReceiver;
81 private Display mDisplay;
82 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Wale Ogunwale228d4042015-09-13 10:17:34 -070083 private DimLayer mDimLayer;
84 @CtrlType
85 private int mCurrentDimSide;
86 private Rect mTmpRect = new Rect();
87 private int mSideMargin;
Wale Ogunwaleb8051b82015-09-17 15:41:52 -070088 private int mMinVisibleWidth;
89 private int mMinVisibleHeight;
Chong Zhang8e89b312015-09-09 15:09:30 -070090
Chong Zhang3005e752015-09-18 18:46:28 -070091 private Task mTask;
Chong Zhang09b21ef2015-09-14 10:20:21 -070092 private boolean mResizing;
Chong Zhang8e89b312015-09-09 15:09:30 -070093 private final Rect mWindowOriginalBounds = new Rect();
94 private final Rect mWindowDragBounds = new Rect();
95 private float mStartDragX;
96 private float mStartDragY;
97 @CtrlType
98 private int mCtrlType = CTRL_NONE;
Wale Ogunwale6a804b82015-09-23 21:04:21 -070099 private boolean mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700100
101 InputChannel mServerChannel;
102 InputChannel mClientChannel;
103 InputApplicationHandle mDragApplicationHandle;
104 InputWindowHandle mDragWindowHandle;
105
106 private final class WindowPositionerEventReceiver extends InputEventReceiver {
107 public WindowPositionerEventReceiver(InputChannel inputChannel, Looper looper) {
108 super(inputChannel, looper);
109 }
110
111 @Override
112 public void onInputEvent(InputEvent event) {
113 if (!(event instanceof MotionEvent)
114 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
115 return;
116 }
117 final MotionEvent motionEvent = (MotionEvent) event;
118 boolean handled = false;
119
120 try {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700121 if (mDragEnded) {
122 // The drag has ended but the clean-up message has not been processed by
123 // window manager. Drop events that occur after this until window manager
124 // has a chance to clean-up the input handle.
125 handled = true;
126 return;
127 }
128
Chong Zhang8e89b312015-09-09 15:09:30 -0700129 final float newX = motionEvent.getRawX();
130 final float newY = motionEvent.getRawY();
131
132 switch (motionEvent.getAction()) {
133 case MotionEvent.ACTION_DOWN: {
134 if (DEBUG_TASK_POSITIONING) {
135 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
136 }
137 } break;
138
139 case MotionEvent.ACTION_MOVE: {
140 if (DEBUG_TASK_POSITIONING){
141 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
142 }
143 synchronized (mService.mWindowMap) {
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700144 mDragEnded = notifyMoveLocked(newX, newY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700145 }
146 try {
Chong Zhang09b21ef2015-09-14 10:20:21 -0700147 mService.mActivityManager.resizeTask(
Chong Zhang87b21722015-09-21 15:39:51 -0700148 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
Chong Zhang8e89b312015-09-09 15:09:30 -0700149 } catch(RemoteException e) {}
150 } break;
151
152 case MotionEvent.ACTION_UP: {
153 if (DEBUG_TASK_POSITIONING) {
154 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
155 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700156 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700157 } break;
158
159 case MotionEvent.ACTION_CANCEL: {
160 if (DEBUG_TASK_POSITIONING) {
161 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
162 }
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700163 mDragEnded = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700164 } break;
165 }
166
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700167 if (mDragEnded) {
Chong Zhang3005e752015-09-18 18:46:28 -0700168 synchronized (mService.mWindowMap) {
169 endDragLocked();
170 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700171 try {
Chong Zhang3005e752015-09-18 18:46:28 -0700172 if (mResizing) {
173 // We were using fullscreen surface during resizing. Request
174 // resizeTask() one last time to restore surface to window size.
175 mService.mActivityManager.resizeTask(
Chong Zhang87b21722015-09-21 15:39:51 -0700176 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_FORCED);
Chong Zhang3005e752015-09-18 18:46:28 -0700177 }
178
179 if (mCurrentDimSide != CTRL_NONE) {
180 final int createMode = mCurrentDimSide == CTRL_LEFT
181 ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
182 : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
183 mService.mActivityManager.moveTaskToDockedStack(
184 mTask.mTaskId, createMode, true /*toTop*/);
185 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700186 } catch(RemoteException e) {}
Chong Zhang3005e752015-09-18 18:46:28 -0700187
Chong Zhang8e89b312015-09-09 15:09:30 -0700188 // Post back to WM to handle clean-ups. We still need the input
189 // event handler for the last finishInputEvent()!
190 mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
191 }
192 handled = true;
193 } catch (Exception e) {
194 Slog.e(TAG, "Exception caught by drag handleMotion", e);
195 } finally {
196 finishInputEvent(event, handled);
197 }
198 }
199 }
200
201 TaskPositioner(WindowManagerService service) {
202 mService = service;
203 }
204
205 /**
206 * @param display The Display that the window being dragged is on.
207 */
208 void register(Display display) {
209 if (DEBUG_TASK_POSITIONING) {
210 Slog.d(TAG, "Registering task positioner");
211 }
212
213 if (mClientChannel != null) {
214 Slog.e(TAG, "Task positioner already registered");
215 return;
216 }
217
218 mDisplay = display;
219 mDisplay.getMetrics(mDisplayMetrics);
220 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
221 mServerChannel = channels[0];
222 mClientChannel = channels[1];
223 mService.mInputManager.registerInputChannel(mServerChannel, null);
224
225 mInputEventReceiver = new WindowPositionerEventReceiver(mClientChannel,
226 mService.mH.getLooper());
227
228 mDragApplicationHandle = new InputApplicationHandle(null);
229 mDragApplicationHandle.name = TAG;
230 mDragApplicationHandle.dispatchingTimeoutNanos =
231 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
232
233 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
234 mDisplay.getDisplayId());
235 mDragWindowHandle.name = TAG;
236 mDragWindowHandle.inputChannel = mServerChannel;
237 mDragWindowHandle.layer = getDragLayerLocked();
238 mDragWindowHandle.layoutParamsFlags = 0;
239 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
240 mDragWindowHandle.dispatchingTimeoutNanos =
241 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
242 mDragWindowHandle.visible = true;
243 mDragWindowHandle.canReceiveKeys = false;
244 mDragWindowHandle.hasFocus = true;
245 mDragWindowHandle.hasWallpaper = false;
246 mDragWindowHandle.paused = false;
247 mDragWindowHandle.ownerPid = Process.myPid();
248 mDragWindowHandle.ownerUid = Process.myUid();
249 mDragWindowHandle.inputFeatures = 0;
250 mDragWindowHandle.scaleFactor = 1.0f;
251
252 // The drag window cannot receive new touches.
253 mDragWindowHandle.touchableRegion.setEmpty();
254
255 // The drag window covers the entire display
256 mDragWindowHandle.frameLeft = 0;
257 mDragWindowHandle.frameTop = 0;
258 final Point p = new Point();
259 mDisplay.getRealSize(p);
260 mDragWindowHandle.frameRight = p.x;
261 mDragWindowHandle.frameBottom = p.y;
262
263 // Pause rotations before a drag.
264 if (WindowManagerService.DEBUG_ORIENTATION) {
265 Slog.d(TAG, "Pausing rotation during re-position");
266 }
267 mService.pauseRotationLocked();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700268
269 mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
Wale Ogunwale231b06e2015-09-16 12:03:09 -0700270 mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700271 mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
272 mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700273
274 mDragEnded = false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700275 }
276
277 void unregister() {
278 if (DEBUG_TASK_POSITIONING) {
279 Slog.d(TAG, "Unregistering task positioner");
280 }
281
282 if (mClientChannel == null) {
283 Slog.e(TAG, "Task positioner not registered");
284 return;
285 }
286
287 mService.mInputManager.unregisterInputChannel(mServerChannel);
288
289 mInputEventReceiver.dispose();
290 mInputEventReceiver = null;
291 mClientChannel.dispose();
292 mServerChannel.dispose();
293 mClientChannel = null;
294 mServerChannel = null;
295
296 mDragWindowHandle = null;
297 mDragApplicationHandle = null;
298 mDisplay = null;
299
Wale Ogunwale228d4042015-09-13 10:17:34 -0700300 if (mDimLayer != null) {
301 mDimLayer.destroySurface();
302 mDimLayer = null;
303 }
304 mCurrentDimSide = CTRL_NONE;
Wale Ogunwale6a804b82015-09-23 21:04:21 -0700305 mDragEnded = true;
Wale Ogunwale228d4042015-09-13 10:17:34 -0700306
Chong Zhang8e89b312015-09-09 15:09:30 -0700307 // Resume rotations after a drag.
308 if (WindowManagerService.DEBUG_ORIENTATION) {
309 Slog.d(TAG, "Resuming rotation after re-position");
310 }
311 mService.resumeRotationLocked();
312 }
313
314 void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700315 if (DEBUG_TASK_POSITIONING) {
316 Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
Chong Zhang8e89b312015-09-09 15:09:30 -0700317 + ", {" + startX + ", " + startY + "}");
318 }
319 mCtrlType = CTRL_NONE;
320 if (resize) {
321 final Rect visibleFrame = win.mVisibleFrame;
322 if (startX < visibleFrame.left) {
323 mCtrlType |= CTRL_LEFT;
324 }
325 if (startX > visibleFrame.right) {
326 mCtrlType |= CTRL_RIGHT;
327 }
328 if (startY < visibleFrame.top) {
329 mCtrlType |= CTRL_TOP;
330 }
331 if (startY > visibleFrame.bottom) {
332 mCtrlType |= CTRL_BOTTOM;
333 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700334 mResizing = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700335 }
336
Chong Zhang3005e752015-09-18 18:46:28 -0700337 mTask = win.getTask();
Chong Zhang8e89b312015-09-09 15:09:30 -0700338 mStartDragX = startX;
339 mStartDragY = startY;
340
Chong Zhang3005e752015-09-18 18:46:28 -0700341 mService.getTaskBounds(mTask.mTaskId, mWindowOriginalBounds);
342 }
343
344 private void endDragLocked() {
345 mResizing = false;
346 mTask.setDragResizing(false);
Chong Zhang8e89b312015-09-09 15:09:30 -0700347 }
348
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700349 /** Returns true if the move operation should be ended. */
350 private boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700351 if (DEBUG_TASK_POSITIONING) {
352 Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
353 }
354
355 if (mCtrlType != CTRL_NONE) {
356 // This is a resizing operation.
357 final int deltaX = Math.round(x - mStartDragX);
358 final int deltaY = Math.round(y - mStartDragY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700359 int left = mWindowOriginalBounds.left;
360 int top = mWindowOriginalBounds.top;
361 int right = mWindowOriginalBounds.right;
362 int bottom = mWindowOriginalBounds.bottom;
363 if ((mCtrlType & CTRL_LEFT) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700364 left = Math.min(left + deltaX, right - mMinVisibleWidth);
Chong Zhang8e89b312015-09-09 15:09:30 -0700365 }
366 if ((mCtrlType & CTRL_TOP) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700367 top = Math.min(top + deltaY, bottom - mMinVisibleHeight);
Chong Zhang8e89b312015-09-09 15:09:30 -0700368 }
369 if ((mCtrlType & CTRL_RIGHT) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700370 right = Math.max(left + mMinVisibleWidth, right + deltaX);
Chong Zhang8e89b312015-09-09 15:09:30 -0700371 }
372 if ((mCtrlType & CTRL_BOTTOM) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700373 bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700374 }
375 mWindowDragBounds.set(left, top, right, bottom);
Chong Zhang3005e752015-09-18 18:46:28 -0700376 mTask.setDragResizing(true);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700377 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700378 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700379
380 // This is a moving operation.
Chong Zhang3005e752015-09-18 18:46:28 -0700381 mTask.mStack.getBounds(mTmpRect);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700382 mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
383 if (!mTmpRect.contains((int) x, (int) y)) {
384 // We end the moving operation if position is outside the stack bounds.
385 return true;
386 }
387 mWindowDragBounds.set(mWindowOriginalBounds);
388 mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
389 updateDimLayerVisibility((int) x);
390 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700391 }
392
Wale Ogunwale228d4042015-09-13 10:17:34 -0700393 private void updateDimLayerVisibility(int x) {
394 @CtrlType
395 int dimSide = getDimSide(x);
396 if (dimSide == mCurrentDimSide) {
397 return;
398 }
399
400 mCurrentDimSide = dimSide;
401
402 if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
403 SurfaceControl.openTransaction();
404 if (mCurrentDimSide == CTRL_NONE) {
405 mDimLayer.hide();
406 } else {
407 showDimLayer();
408 }
409 SurfaceControl.closeTransaction();
410 }
411
412 /**
413 * Returns the side of the screen the dim layer should be shown.
414 * @param x horizontal coordinate used to determine if the dim layer should be shown
415 * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
416 * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
417 * shouldn't be shown.
418 */
419 private int getDimSide(int x) {
Chong Zhang3005e752015-09-18 18:46:28 -0700420 if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
421 || !mTask.mStack.isFullscreen()
Wale Ogunwale228d4042015-09-13 10:17:34 -0700422 || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
423 return CTRL_NONE;
424 }
425
Chong Zhang3005e752015-09-18 18:46:28 -0700426 mTask.mStack.getBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700427 if (x - mSideMargin <= mTmpRect.left) {
428 return CTRL_LEFT;
429 }
430 if (x + mSideMargin >= mTmpRect.right) {
431 return CTRL_RIGHT;
432 }
433
434 return CTRL_NONE;
435 }
436
437 private void showDimLayer() {
Chong Zhang3005e752015-09-18 18:46:28 -0700438 mTask.mStack.getBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700439 if (mCurrentDimSide == CTRL_LEFT) {
440 mTmpRect.right = mTmpRect.centerX();
441 } else if (mCurrentDimSide == CTRL_RIGHT) {
442 mTmpRect.left = mTmpRect.centerX();
443 }
444
445 mDimLayer.setBounds(mTmpRect);
446 mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
447 }
448
449 @Override /** {@link DimLayer.DimLayerUser} */
450 public boolean isFullscreen() {
451 return false;
452 }
453
454 @Override /** {@link DimLayer.DimLayerUser} */
455 public DisplayInfo getDisplayInfo() {
Chong Zhang3005e752015-09-18 18:46:28 -0700456 return mTask.mStack.getDisplayInfo();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700457 }
458
Chong Zhang8e89b312015-09-09 15:09:30 -0700459 private int getDragLayerLocked() {
460 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
461 * WindowManagerService.TYPE_LAYER_MULTIPLIER
462 + WindowManagerService.TYPE_LAYER_OFFSET;
463 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700464}