blob: 548734939feabca15206c8c6b46bd13029356704 [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;
Wale Ogunwale228d4042015-09-13 10:17:34 -070022import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
Chong Zhang8e89b312015-09-09 15:09:30 -070023import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
Wale Ogunwale228d4042015-09-13 10:17:34 -070024import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
Wale Ogunwale231b06e2015-09-16 12:03:09 -070025import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
26import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
Chong Zhang8e89b312015-09-09 15:09:30 -070027
28import android.annotation.IntDef;
29import android.graphics.Point;
30import android.graphics.Rect;
31import android.os.Looper;
32import android.os.Process;
33import android.os.RemoteException;
34import android.util.DisplayMetrics;
35import android.util.Slog;
Chong Zhang8e89b312015-09-09 15:09:30 -070036import android.view.Display;
Wale Ogunwale228d4042015-09-13 10:17:34 -070037import android.view.DisplayInfo;
Chong Zhang8e89b312015-09-09 15:09:30 -070038import android.view.InputChannel;
39import android.view.InputDevice;
40import android.view.InputEvent;
41import android.view.InputEventReceiver;
42import android.view.MotionEvent;
Wale Ogunwale228d4042015-09-13 10:17:34 -070043import android.view.SurfaceControl;
Chong Zhang8e89b312015-09-09 15:09:30 -070044import android.view.WindowManager;
45
Wale Ogunwale228d4042015-09-13 10:17:34 -070046import com.android.server.input.InputApplicationHandle;
47import com.android.server.input.InputWindowHandle;
48import com.android.server.wm.WindowManagerService.H;
49
50import java.lang.annotation.Retention;
51import java.lang.annotation.RetentionPolicy;
52
53class TaskPositioner implements DimLayer.DimLayerUser {
Chong Zhang8e89b312015-09-09 15:09:30 -070054 private static final String TAG = "TaskPositioner";
55
Wale Ogunwale228d4042015-09-13 10:17:34 -070056 // The margin the pointer position has to be within the side of the screen to be
57 // considered at the side of the screen.
Wale Ogunwalef5a67f52015-09-15 09:54:33 -070058 private static final int SIDE_MARGIN_DIP = 100;
Wale Ogunwale228d4042015-09-13 10:17:34 -070059
Chong Zhang8e89b312015-09-09 15:09:30 -070060 @IntDef(flag = true,
61 value = {
62 CTRL_NONE,
63 CTRL_LEFT,
64 CTRL_RIGHT,
65 CTRL_TOP,
66 CTRL_BOTTOM
67 })
68 @Retention(RetentionPolicy.SOURCE)
69 @interface CtrlType {}
70
71 private static final int CTRL_NONE = 0x0;
72 private static final int CTRL_LEFT = 0x1;
73 private static final int CTRL_RIGHT = 0x2;
74 private static final int CTRL_TOP = 0x4;
75 private static final int CTRL_BOTTOM = 0x8;
76
77 private final WindowManagerService mService;
78 private WindowPositionerEventReceiver mInputEventReceiver;
79 private Display mDisplay;
80 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Wale Ogunwale228d4042015-09-13 10:17:34 -070081 private DimLayer mDimLayer;
82 @CtrlType
83 private int mCurrentDimSide;
84 private Rect mTmpRect = new Rect();
85 private int mSideMargin;
Wale Ogunwaleb8051b82015-09-17 15:41:52 -070086 private int mMinVisibleWidth;
87 private int mMinVisibleHeight;
Chong Zhang8e89b312015-09-09 15:09:30 -070088
Chong Zhang3005e752015-09-18 18:46:28 -070089 private Task mTask;
Chong Zhang09b21ef2015-09-14 10:20:21 -070090 private boolean mResizing;
Chong Zhang8e89b312015-09-09 15:09:30 -070091 private final Rect mWindowOriginalBounds = new Rect();
92 private final Rect mWindowDragBounds = new Rect();
93 private float mStartDragX;
94 private float mStartDragY;
95 @CtrlType
96 private int mCtrlType = CTRL_NONE;
97
98 InputChannel mServerChannel;
99 InputChannel mClientChannel;
100 InputApplicationHandle mDragApplicationHandle;
101 InputWindowHandle mDragWindowHandle;
102
103 private final class WindowPositionerEventReceiver extends InputEventReceiver {
104 public WindowPositionerEventReceiver(InputChannel inputChannel, Looper looper) {
105 super(inputChannel, looper);
106 }
107
108 @Override
109 public void onInputEvent(InputEvent event) {
110 if (!(event instanceof MotionEvent)
111 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
112 return;
113 }
114 final MotionEvent motionEvent = (MotionEvent) event;
115 boolean handled = false;
116
117 try {
118 boolean endDrag = false;
119 final float newX = motionEvent.getRawX();
120 final float newY = motionEvent.getRawY();
121
122 switch (motionEvent.getAction()) {
123 case MotionEvent.ACTION_DOWN: {
124 if (DEBUG_TASK_POSITIONING) {
125 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
126 }
127 } break;
128
129 case MotionEvent.ACTION_MOVE: {
130 if (DEBUG_TASK_POSITIONING){
131 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
132 }
133 synchronized (mService.mWindowMap) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700134 endDrag = notifyMoveLocked(newX, newY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700135 }
136 try {
Chong Zhang09b21ef2015-09-14 10:20:21 -0700137 mService.mActivityManager.resizeTask(
Chong Zhang3005e752015-09-18 18:46:28 -0700138 mTask.mTaskId, mWindowDragBounds, true /* resizedByUser */);
Chong Zhang8e89b312015-09-09 15:09:30 -0700139 } catch(RemoteException e) {}
140 } break;
141
142 case MotionEvent.ACTION_UP: {
143 if (DEBUG_TASK_POSITIONING) {
144 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
145 }
146 endDrag = true;
147 } break;
148
149 case MotionEvent.ACTION_CANCEL: {
150 if (DEBUG_TASK_POSITIONING) {
151 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
152 }
153 endDrag = true;
154 } break;
155 }
156
157 if (endDrag) {
Chong Zhang3005e752015-09-18 18:46:28 -0700158 synchronized (mService.mWindowMap) {
159 endDragLocked();
160 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700161 try {
Chong Zhang3005e752015-09-18 18:46:28 -0700162 if (mResizing) {
163 // We were using fullscreen surface during resizing. Request
164 // resizeTask() one last time to restore surface to window size.
165 mService.mActivityManager.resizeTask(
166 mTask.mTaskId, mWindowDragBounds, true /* resizedByUser */);
167 }
168
169 if (mCurrentDimSide != CTRL_NONE) {
170 final int createMode = mCurrentDimSide == CTRL_LEFT
171 ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
172 : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
173 mService.mActivityManager.moveTaskToDockedStack(
174 mTask.mTaskId, createMode, true /*toTop*/);
175 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700176 } catch(RemoteException e) {}
Chong Zhang3005e752015-09-18 18:46:28 -0700177
Chong Zhang8e89b312015-09-09 15:09:30 -0700178 // Post back to WM to handle clean-ups. We still need the input
179 // event handler for the last finishInputEvent()!
180 mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
181 }
182 handled = true;
183 } catch (Exception e) {
184 Slog.e(TAG, "Exception caught by drag handleMotion", e);
185 } finally {
186 finishInputEvent(event, handled);
187 }
188 }
189 }
190
191 TaskPositioner(WindowManagerService service) {
192 mService = service;
193 }
194
195 /**
196 * @param display The Display that the window being dragged is on.
197 */
198 void register(Display display) {
199 if (DEBUG_TASK_POSITIONING) {
200 Slog.d(TAG, "Registering task positioner");
201 }
202
203 if (mClientChannel != null) {
204 Slog.e(TAG, "Task positioner already registered");
205 return;
206 }
207
208 mDisplay = display;
209 mDisplay.getMetrics(mDisplayMetrics);
210 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
211 mServerChannel = channels[0];
212 mClientChannel = channels[1];
213 mService.mInputManager.registerInputChannel(mServerChannel, null);
214
215 mInputEventReceiver = new WindowPositionerEventReceiver(mClientChannel,
216 mService.mH.getLooper());
217
218 mDragApplicationHandle = new InputApplicationHandle(null);
219 mDragApplicationHandle.name = TAG;
220 mDragApplicationHandle.dispatchingTimeoutNanos =
221 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
222
223 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
224 mDisplay.getDisplayId());
225 mDragWindowHandle.name = TAG;
226 mDragWindowHandle.inputChannel = mServerChannel;
227 mDragWindowHandle.layer = getDragLayerLocked();
228 mDragWindowHandle.layoutParamsFlags = 0;
229 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
230 mDragWindowHandle.dispatchingTimeoutNanos =
231 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
232 mDragWindowHandle.visible = true;
233 mDragWindowHandle.canReceiveKeys = false;
234 mDragWindowHandle.hasFocus = true;
235 mDragWindowHandle.hasWallpaper = false;
236 mDragWindowHandle.paused = false;
237 mDragWindowHandle.ownerPid = Process.myPid();
238 mDragWindowHandle.ownerUid = Process.myUid();
239 mDragWindowHandle.inputFeatures = 0;
240 mDragWindowHandle.scaleFactor = 1.0f;
241
242 // The drag window cannot receive new touches.
243 mDragWindowHandle.touchableRegion.setEmpty();
244
245 // The drag window covers the entire display
246 mDragWindowHandle.frameLeft = 0;
247 mDragWindowHandle.frameTop = 0;
248 final Point p = new Point();
249 mDisplay.getRealSize(p);
250 mDragWindowHandle.frameRight = p.x;
251 mDragWindowHandle.frameBottom = p.y;
252
253 // Pause rotations before a drag.
254 if (WindowManagerService.DEBUG_ORIENTATION) {
255 Slog.d(TAG, "Pausing rotation during re-position");
256 }
257 mService.pauseRotationLocked();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700258
259 mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
Wale Ogunwale231b06e2015-09-16 12:03:09 -0700260 mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700261 mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
262 mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
Chong Zhang8e89b312015-09-09 15:09:30 -0700263 }
264
265 void unregister() {
266 if (DEBUG_TASK_POSITIONING) {
267 Slog.d(TAG, "Unregistering task positioner");
268 }
269
270 if (mClientChannel == null) {
271 Slog.e(TAG, "Task positioner not registered");
272 return;
273 }
274
275 mService.mInputManager.unregisterInputChannel(mServerChannel);
276
277 mInputEventReceiver.dispose();
278 mInputEventReceiver = null;
279 mClientChannel.dispose();
280 mServerChannel.dispose();
281 mClientChannel = null;
282 mServerChannel = null;
283
284 mDragWindowHandle = null;
285 mDragApplicationHandle = null;
286 mDisplay = null;
287
Wale Ogunwale228d4042015-09-13 10:17:34 -0700288 if (mDimLayer != null) {
289 mDimLayer.destroySurface();
290 mDimLayer = null;
291 }
292 mCurrentDimSide = CTRL_NONE;
293
Chong Zhang8e89b312015-09-09 15:09:30 -0700294 // Resume rotations after a drag.
295 if (WindowManagerService.DEBUG_ORIENTATION) {
296 Slog.d(TAG, "Resuming rotation after re-position");
297 }
298 mService.resumeRotationLocked();
299 }
300
301 void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700302 if (DEBUG_TASK_POSITIONING) {
303 Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
Chong Zhang8e89b312015-09-09 15:09:30 -0700304 + ", {" + startX + ", " + startY + "}");
305 }
306 mCtrlType = CTRL_NONE;
307 if (resize) {
308 final Rect visibleFrame = win.mVisibleFrame;
309 if (startX < visibleFrame.left) {
310 mCtrlType |= CTRL_LEFT;
311 }
312 if (startX > visibleFrame.right) {
313 mCtrlType |= CTRL_RIGHT;
314 }
315 if (startY < visibleFrame.top) {
316 mCtrlType |= CTRL_TOP;
317 }
318 if (startY > visibleFrame.bottom) {
319 mCtrlType |= CTRL_BOTTOM;
320 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700321 mResizing = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700322 }
323
Chong Zhang3005e752015-09-18 18:46:28 -0700324 mTask = win.getTask();
Chong Zhang8e89b312015-09-09 15:09:30 -0700325 mStartDragX = startX;
326 mStartDragY = startY;
327
Chong Zhang3005e752015-09-18 18:46:28 -0700328 mService.getTaskBounds(mTask.mTaskId, mWindowOriginalBounds);
329 }
330
331 private void endDragLocked() {
332 mResizing = false;
333 mTask.setDragResizing(false);
Chong Zhang8e89b312015-09-09 15:09:30 -0700334 }
335
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700336 /** Returns true if the move operation should be ended. */
337 private boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700338 if (DEBUG_TASK_POSITIONING) {
339 Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
340 }
341
342 if (mCtrlType != CTRL_NONE) {
343 // This is a resizing operation.
344 final int deltaX = Math.round(x - mStartDragX);
345 final int deltaY = Math.round(y - mStartDragY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700346 int left = mWindowOriginalBounds.left;
347 int top = mWindowOriginalBounds.top;
348 int right = mWindowOriginalBounds.right;
349 int bottom = mWindowOriginalBounds.bottom;
350 if ((mCtrlType & CTRL_LEFT) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700351 left = Math.min(left + deltaX, right - mMinVisibleWidth);
Chong Zhang8e89b312015-09-09 15:09:30 -0700352 }
353 if ((mCtrlType & CTRL_TOP) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700354 top = Math.min(top + deltaY, bottom - mMinVisibleHeight);
Chong Zhang8e89b312015-09-09 15:09:30 -0700355 }
356 if ((mCtrlType & CTRL_RIGHT) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700357 right = Math.max(left + mMinVisibleWidth, right + deltaX);
Chong Zhang8e89b312015-09-09 15:09:30 -0700358 }
359 if ((mCtrlType & CTRL_BOTTOM) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700360 bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700361 }
362 mWindowDragBounds.set(left, top, right, bottom);
Chong Zhang3005e752015-09-18 18:46:28 -0700363 mTask.setDragResizing(true);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700364 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700365 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700366
367 // This is a moving operation.
Chong Zhang3005e752015-09-18 18:46:28 -0700368 mTask.mStack.getBounds(mTmpRect);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700369 mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
370 if (!mTmpRect.contains((int) x, (int) y)) {
371 // We end the moving operation if position is outside the stack bounds.
372 return true;
373 }
374 mWindowDragBounds.set(mWindowOriginalBounds);
375 mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
376 updateDimLayerVisibility((int) x);
377 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700378 }
379
Wale Ogunwale228d4042015-09-13 10:17:34 -0700380 private void updateDimLayerVisibility(int x) {
381 @CtrlType
382 int dimSide = getDimSide(x);
383 if (dimSide == mCurrentDimSide) {
384 return;
385 }
386
387 mCurrentDimSide = dimSide;
388
389 if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
390 SurfaceControl.openTransaction();
391 if (mCurrentDimSide == CTRL_NONE) {
392 mDimLayer.hide();
393 } else {
394 showDimLayer();
395 }
396 SurfaceControl.closeTransaction();
397 }
398
399 /**
400 * Returns the side of the screen the dim layer should be shown.
401 * @param x horizontal coordinate used to determine if the dim layer should be shown
402 * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
403 * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
404 * shouldn't be shown.
405 */
406 private int getDimSide(int x) {
Chong Zhang3005e752015-09-18 18:46:28 -0700407 if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
408 || !mTask.mStack.isFullscreen()
Wale Ogunwale228d4042015-09-13 10:17:34 -0700409 || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
410 return CTRL_NONE;
411 }
412
Chong Zhang3005e752015-09-18 18:46:28 -0700413 mTask.mStack.getBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700414 if (x - mSideMargin <= mTmpRect.left) {
415 return CTRL_LEFT;
416 }
417 if (x + mSideMargin >= mTmpRect.right) {
418 return CTRL_RIGHT;
419 }
420
421 return CTRL_NONE;
422 }
423
424 private void showDimLayer() {
Chong Zhang3005e752015-09-18 18:46:28 -0700425 mTask.mStack.getBounds(mTmpRect);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700426 if (mCurrentDimSide == CTRL_LEFT) {
427 mTmpRect.right = mTmpRect.centerX();
428 } else if (mCurrentDimSide == CTRL_RIGHT) {
429 mTmpRect.left = mTmpRect.centerX();
430 }
431
432 mDimLayer.setBounds(mTmpRect);
433 mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
434 }
435
436 @Override /** {@link DimLayer.DimLayerUser} */
437 public boolean isFullscreen() {
438 return false;
439 }
440
441 @Override /** {@link DimLayer.DimLayerUser} */
442 public DisplayInfo getDisplayInfo() {
Chong Zhang3005e752015-09-18 18:46:28 -0700443 return mTask.mStack.getDisplayInfo();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700444 }
445
Chong Zhang8e89b312015-09-09 15:09:30 -0700446 private int getDragLayerLocked() {
447 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
448 * WindowManagerService.TYPE_LAYER_MULTIPLIER
449 + WindowManagerService.TYPE_LAYER_OFFSET;
450 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700451}