blob: c4ff886109c41e7f5df6d3071d266b026bb48176 [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;
Wale Ogunwale4f52bc62015-09-24 13:47:31 -070038import android.view.Choreographer;
Chong Zhang8e89b312015-09-09 15:09:30 -070039import android.view.Display;
Wale Ogunwale228d4042015-09-13 10:17:34 -070040import android.view.DisplayInfo;
Chong Zhang8e89b312015-09-09 15:09:30 -070041import android.view.InputChannel;
42import android.view.InputDevice;
43import android.view.InputEvent;
Wale Ogunwale4f52bc62015-09-24 13:47:31 -070044import android.view.BatchedInputEventReceiver;
Chong Zhang8e89b312015-09-09 15:09:30 -070045import android.view.MotionEvent;
Wale Ogunwale228d4042015-09-13 10:17:34 -070046import android.view.SurfaceControl;
Chong Zhang8e89b312015-09-09 15:09:30 -070047import android.view.WindowManager;
48
Wale Ogunwale228d4042015-09-13 10:17:34 -070049import com.android.server.input.InputApplicationHandle;
50import com.android.server.input.InputWindowHandle;
51import com.android.server.wm.WindowManagerService.H;
52
53import java.lang.annotation.Retention;
54import java.lang.annotation.RetentionPolicy;
55
56class TaskPositioner implements DimLayer.DimLayerUser {
Chong Zhang8e89b312015-09-09 15:09:30 -070057 private static final String TAG = "TaskPositioner";
58
Wale Ogunwale228d4042015-09-13 10:17:34 -070059 // The margin the pointer position has to be within the side of the screen to be
60 // considered at the side of the screen.
Wale Ogunwalef5a67f52015-09-15 09:54:33 -070061 private static final int SIDE_MARGIN_DIP = 100;
Wale Ogunwale228d4042015-09-13 10:17:34 -070062
Chong Zhang8e89b312015-09-09 15:09:30 -070063 @IntDef(flag = true,
64 value = {
65 CTRL_NONE,
66 CTRL_LEFT,
67 CTRL_RIGHT,
68 CTRL_TOP,
69 CTRL_BOTTOM
70 })
71 @Retention(RetentionPolicy.SOURCE)
72 @interface CtrlType {}
73
74 private static final int CTRL_NONE = 0x0;
75 private static final int CTRL_LEFT = 0x1;
76 private static final int CTRL_RIGHT = 0x2;
77 private static final int CTRL_TOP = 0x4;
78 private static final int CTRL_BOTTOM = 0x8;
79
80 private final WindowManagerService mService;
81 private WindowPositionerEventReceiver mInputEventReceiver;
82 private Display mDisplay;
83 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Wale Ogunwale228d4042015-09-13 10:17:34 -070084 private DimLayer mDimLayer;
85 @CtrlType
86 private int mCurrentDimSide;
87 private Rect mTmpRect = new Rect();
88 private int mSideMargin;
Wale Ogunwaleb8051b82015-09-17 15:41:52 -070089 private int mMinVisibleWidth;
90 private int mMinVisibleHeight;
Chong Zhang8e89b312015-09-09 15:09:30 -070091
Chong Zhang3005e752015-09-18 18:46:28 -070092 private Task mTask;
Chong Zhang09b21ef2015-09-14 10:20:21 -070093 private boolean mResizing;
Chong Zhang8e89b312015-09-09 15:09:30 -070094 private final Rect mWindowOriginalBounds = new Rect();
95 private final Rect mWindowDragBounds = new Rect();
96 private float mStartDragX;
97 private float mStartDragY;
98 @CtrlType
99 private int mCtrlType = CTRL_NONE;
100
101 InputChannel mServerChannel;
102 InputChannel mClientChannel;
103 InputApplicationHandle mDragApplicationHandle;
104 InputWindowHandle mDragWindowHandle;
105
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700106 private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
107 public WindowPositionerEventReceiver(
108 InputChannel inputChannel, Looper looper, Choreographer choreographer) {
109 super(inputChannel, looper, choreographer);
Chong Zhang8e89b312015-09-09 15:09:30 -0700110 }
111
112 @Override
113 public void onInputEvent(InputEvent event) {
114 if (!(event instanceof MotionEvent)
115 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
116 return;
117 }
118 final MotionEvent motionEvent = (MotionEvent) event;
119 boolean handled = false;
120
121 try {
122 boolean endDrag = false;
123 final float newX = motionEvent.getRawX();
124 final float newY = motionEvent.getRawY();
125
126 switch (motionEvent.getAction()) {
127 case MotionEvent.ACTION_DOWN: {
128 if (DEBUG_TASK_POSITIONING) {
129 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
130 }
131 } break;
132
133 case MotionEvent.ACTION_MOVE: {
134 if (DEBUG_TASK_POSITIONING){
135 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
136 }
137 synchronized (mService.mWindowMap) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700138 endDrag = notifyMoveLocked(newX, newY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700139 }
140 try {
Chong Zhang09b21ef2015-09-14 10:20:21 -0700141 mService.mActivityManager.resizeTask(
Chong Zhang87b21722015-09-21 15:39:51 -0700142 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
Chong Zhang8e89b312015-09-09 15:09:30 -0700143 } catch(RemoteException e) {}
144 } break;
145
146 case MotionEvent.ACTION_UP: {
147 if (DEBUG_TASK_POSITIONING) {
148 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
149 }
150 endDrag = true;
151 } break;
152
153 case MotionEvent.ACTION_CANCEL: {
154 if (DEBUG_TASK_POSITIONING) {
155 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
156 }
157 endDrag = true;
158 } break;
159 }
160
161 if (endDrag) {
Chong Zhang3005e752015-09-18 18:46:28 -0700162 synchronized (mService.mWindowMap) {
163 endDragLocked();
164 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700165 try {
Chong Zhang3005e752015-09-18 18:46:28 -0700166 if (mResizing) {
167 // We were using fullscreen surface during resizing. Request
168 // resizeTask() one last time to restore surface to window size.
169 mService.mActivityManager.resizeTask(
Chong Zhang87b21722015-09-21 15:39:51 -0700170 mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_FORCED);
Chong Zhang3005e752015-09-18 18:46:28 -0700171 }
172
173 if (mCurrentDimSide != CTRL_NONE) {
174 final int createMode = mCurrentDimSide == CTRL_LEFT
175 ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
176 : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
177 mService.mActivityManager.moveTaskToDockedStack(
178 mTask.mTaskId, createMode, true /*toTop*/);
179 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700180 } catch(RemoteException e) {}
Chong Zhang3005e752015-09-18 18:46:28 -0700181
Chong Zhang8e89b312015-09-09 15:09:30 -0700182 // Post back to WM to handle clean-ups. We still need the input
183 // event handler for the last finishInputEvent()!
184 mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
185 }
186 handled = true;
187 } catch (Exception e) {
188 Slog.e(TAG, "Exception caught by drag handleMotion", e);
189 } finally {
190 finishInputEvent(event, handled);
191 }
192 }
193 }
194
195 TaskPositioner(WindowManagerService service) {
196 mService = service;
197 }
198
199 /**
200 * @param display The Display that the window being dragged is on.
201 */
202 void register(Display display) {
203 if (DEBUG_TASK_POSITIONING) {
204 Slog.d(TAG, "Registering task positioner");
205 }
206
207 if (mClientChannel != null) {
208 Slog.e(TAG, "Task positioner already registered");
209 return;
210 }
211
212 mDisplay = display;
213 mDisplay.getMetrics(mDisplayMetrics);
214 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
215 mServerChannel = channels[0];
216 mClientChannel = channels[1];
217 mService.mInputManager.registerInputChannel(mServerChannel, null);
218
Wale Ogunwale4f52bc62015-09-24 13:47:31 -0700219 mInputEventReceiver = new WindowPositionerEventReceiver(
220 mClientChannel, mService.mH.getLooper(), mService.mChoreographer);
Chong Zhang8e89b312015-09-09 15:09:30 -0700221
222 mDragApplicationHandle = new InputApplicationHandle(null);
223 mDragApplicationHandle.name = TAG;
224 mDragApplicationHandle.dispatchingTimeoutNanos =
225 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
226
227 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
228 mDisplay.getDisplayId());
229 mDragWindowHandle.name = TAG;
230 mDragWindowHandle.inputChannel = mServerChannel;
231 mDragWindowHandle.layer = getDragLayerLocked();
232 mDragWindowHandle.layoutParamsFlags = 0;
233 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
234 mDragWindowHandle.dispatchingTimeoutNanos =
235 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
236 mDragWindowHandle.visible = true;
237 mDragWindowHandle.canReceiveKeys = false;
238 mDragWindowHandle.hasFocus = true;
239 mDragWindowHandle.hasWallpaper = false;
240 mDragWindowHandle.paused = false;
241 mDragWindowHandle.ownerPid = Process.myPid();
242 mDragWindowHandle.ownerUid = Process.myUid();
243 mDragWindowHandle.inputFeatures = 0;
244 mDragWindowHandle.scaleFactor = 1.0f;
245
246 // The drag window cannot receive new touches.
247 mDragWindowHandle.touchableRegion.setEmpty();
248
249 // The drag window covers the entire display
250 mDragWindowHandle.frameLeft = 0;
251 mDragWindowHandle.frameTop = 0;
252 final Point p = new Point();
253 mDisplay.getRealSize(p);
254 mDragWindowHandle.frameRight = p.x;
255 mDragWindowHandle.frameBottom = p.y;
256
257 // Pause rotations before a drag.
258 if (WindowManagerService.DEBUG_ORIENTATION) {
259 Slog.d(TAG, "Pausing rotation during re-position");
260 }
261 mService.pauseRotationLocked();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700262
263 mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
Wale Ogunwale231b06e2015-09-16 12:03:09 -0700264 mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700265 mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
266 mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
Chong Zhang8e89b312015-09-09 15:09:30 -0700267 }
268
269 void unregister() {
270 if (DEBUG_TASK_POSITIONING) {
271 Slog.d(TAG, "Unregistering task positioner");
272 }
273
274 if (mClientChannel == null) {
275 Slog.e(TAG, "Task positioner not registered");
276 return;
277 }
278
279 mService.mInputManager.unregisterInputChannel(mServerChannel);
280
281 mInputEventReceiver.dispose();
282 mInputEventReceiver = null;
283 mClientChannel.dispose();
284 mServerChannel.dispose();
285 mClientChannel = null;
286 mServerChannel = null;
287
288 mDragWindowHandle = null;
289 mDragApplicationHandle = null;
290 mDisplay = null;
291
Wale Ogunwale228d4042015-09-13 10:17:34 -0700292 if (mDimLayer != null) {
293 mDimLayer.destroySurface();
294 mDimLayer = null;
295 }
296 mCurrentDimSide = CTRL_NONE;
297
Chong Zhang8e89b312015-09-09 15:09:30 -0700298 // Resume rotations after a drag.
299 if (WindowManagerService.DEBUG_ORIENTATION) {
300 Slog.d(TAG, "Resuming rotation after re-position");
301 }
302 mService.resumeRotationLocked();
303 }
304
305 void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700306 if (DEBUG_TASK_POSITIONING) {
307 Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
Chong Zhang8e89b312015-09-09 15:09:30 -0700308 + ", {" + startX + ", " + startY + "}");
309 }
310 mCtrlType = CTRL_NONE;
311 if (resize) {
312 final Rect visibleFrame = win.mVisibleFrame;
313 if (startX < visibleFrame.left) {
314 mCtrlType |= CTRL_LEFT;
315 }
316 if (startX > visibleFrame.right) {
317 mCtrlType |= CTRL_RIGHT;
318 }
319 if (startY < visibleFrame.top) {
320 mCtrlType |= CTRL_TOP;
321 }
322 if (startY > visibleFrame.bottom) {
323 mCtrlType |= CTRL_BOTTOM;
324 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700325 mResizing = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700326 }
327
Chong Zhang3005e752015-09-18 18:46:28 -0700328 mTask = win.getTask();
Chong Zhang8e89b312015-09-09 15:09:30 -0700329 mStartDragX = startX;
330 mStartDragY = startY;
331
Chong Zhang3005e752015-09-18 18:46:28 -0700332 mService.getTaskBounds(mTask.mTaskId, mWindowOriginalBounds);
333 }
334
335 private void endDragLocked() {
336 mResizing = false;
337 mTask.setDragResizing(false);
Chong Zhang8e89b312015-09-09 15:09:30 -0700338 }
339
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700340 /** Returns true if the move operation should be ended. */
341 private boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700342 if (DEBUG_TASK_POSITIONING) {
343 Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
344 }
345
346 if (mCtrlType != CTRL_NONE) {
347 // This is a resizing operation.
348 final int deltaX = Math.round(x - mStartDragX);
349 final int deltaY = Math.round(y - mStartDragY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700350 int left = mWindowOriginalBounds.left;
351 int top = mWindowOriginalBounds.top;
352 int right = mWindowOriginalBounds.right;
353 int bottom = mWindowOriginalBounds.bottom;
354 if ((mCtrlType & CTRL_LEFT) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700355 left = Math.min(left + deltaX, right - mMinVisibleWidth);
Chong Zhang8e89b312015-09-09 15:09:30 -0700356 }
357 if ((mCtrlType & CTRL_TOP) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700358 top = Math.min(top + deltaY, bottom - mMinVisibleHeight);
Chong Zhang8e89b312015-09-09 15:09:30 -0700359 }
360 if ((mCtrlType & CTRL_RIGHT) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700361 right = Math.max(left + mMinVisibleWidth, right + deltaX);
Chong Zhang8e89b312015-09-09 15:09:30 -0700362 }
363 if ((mCtrlType & CTRL_BOTTOM) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700364 bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700365 }
366 mWindowDragBounds.set(left, top, right, bottom);
Chong Zhang3005e752015-09-18 18:46:28 -0700367 mTask.setDragResizing(true);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700368 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700369 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700370
371 // This is a moving operation.
Chong Zhang3005e752015-09-18 18:46:28 -0700372 mTask.mStack.getBounds(mTmpRect);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700373 mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
374 if (!mTmpRect.contains((int) x, (int) y)) {
375 // We end the moving operation if position is outside the stack bounds.
376 return true;
377 }
378 mWindowDragBounds.set(mWindowOriginalBounds);
379 mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
380 updateDimLayerVisibility((int) x);
381 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700382 }
383
Wale Ogunwale228d4042015-09-13 10:17:34 -0700384 private void updateDimLayerVisibility(int x) {
385 @CtrlType
386 int dimSide = getDimSide(x);
387 if (dimSide == mCurrentDimSide) {
388 return;
389 }
390
391 mCurrentDimSide = dimSide;
392
393 if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
394 SurfaceControl.openTransaction();
395 if (mCurrentDimSide == CTRL_NONE) {
396 mDimLayer.hide();
397 } else {
398 showDimLayer();
399 }
400 SurfaceControl.closeTransaction();
401 }
402
403 /**
404 * Returns the side of the screen the dim layer should be shown.
405 * @param x horizontal coordinate used to determine if the dim layer should be shown
406 * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
407 * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
408 * shouldn't be shown.
409 */
410 private int getDimSide(int x) {
Chong Zhang3005e752015-09-18 18:46:28 -0700411 if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
412 || !mTask.mStack.isFullscreen()
Wale Ogunwale228d4042015-09-13 10:17:34 -0700413 || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
414 return CTRL_NONE;
415 }
416
Chong Zhang3005e752015-09-18 18:46:28 -0700417 mTask.mStack.getBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700418 if (x - mSideMargin <= mTmpRect.left) {
419 return CTRL_LEFT;
420 }
421 if (x + mSideMargin >= mTmpRect.right) {
422 return CTRL_RIGHT;
423 }
424
425 return CTRL_NONE;
426 }
427
428 private void showDimLayer() {
Chong Zhang3005e752015-09-18 18:46:28 -0700429 mTask.mStack.getBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700430 if (mCurrentDimSide == CTRL_LEFT) {
431 mTmpRect.right = mTmpRect.centerX();
432 } else if (mCurrentDimSide == CTRL_RIGHT) {
433 mTmpRect.left = mTmpRect.centerX();
434 }
435
436 mDimLayer.setBounds(mTmpRect);
437 mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
438 }
439
440 @Override /** {@link DimLayer.DimLayerUser} */
441 public boolean isFullscreen() {
442 return false;
443 }
444
445 @Override /** {@link DimLayer.DimLayerUser} */
446 public DisplayInfo getDisplayInfo() {
Chong Zhang3005e752015-09-18 18:46:28 -0700447 return mTask.mStack.getDisplayInfo();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700448 }
449
Chong Zhang8e89b312015-09-09 15:09:30 -0700450 private int getDragLayerLocked() {
451 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
452 * WindowManagerService.TYPE_LAYER_MULTIPLIER
453 + WindowManagerService.TYPE_LAYER_OFFSET;
454 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700455}