blob: 17b56ba2ada2435997a18761ed4f7dcd720855d6 [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
89 private int mTaskId;
Wale Ogunwale228d4042015-09-13 10:17:34 -070090 private TaskStack mStack;
Chong Zhang09b21ef2015-09-14 10:20:21 -070091 private boolean mResizing;
Chong Zhang8e89b312015-09-09 15:09:30 -070092 private final Rect mWindowOriginalBounds = new Rect();
93 private final Rect mWindowDragBounds = new Rect();
94 private float mStartDragX;
95 private float mStartDragY;
96 @CtrlType
97 private int mCtrlType = CTRL_NONE;
98
99 InputChannel mServerChannel;
100 InputChannel mClientChannel;
101 InputApplicationHandle mDragApplicationHandle;
102 InputWindowHandle mDragWindowHandle;
103
104 private final class WindowPositionerEventReceiver extends InputEventReceiver {
105 public WindowPositionerEventReceiver(InputChannel inputChannel, Looper looper) {
106 super(inputChannel, looper);
107 }
108
109 @Override
110 public void onInputEvent(InputEvent event) {
111 if (!(event instanceof MotionEvent)
112 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
113 return;
114 }
115 final MotionEvent motionEvent = (MotionEvent) event;
116 boolean handled = false;
117
118 try {
119 boolean endDrag = false;
120 final float newX = motionEvent.getRawX();
121 final float newY = motionEvent.getRawY();
122
123 switch (motionEvent.getAction()) {
124 case MotionEvent.ACTION_DOWN: {
125 if (DEBUG_TASK_POSITIONING) {
126 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
127 }
128 } break;
129
130 case MotionEvent.ACTION_MOVE: {
131 if (DEBUG_TASK_POSITIONING){
132 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
133 }
134 synchronized (mService.mWindowMap) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700135 endDrag = notifyMoveLocked(newX, newY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700136 }
137 try {
Chong Zhang09b21ef2015-09-14 10:20:21 -0700138 mService.mActivityManager.resizeTask(
139 mTaskId, mWindowDragBounds, true /* resizedByUser */);
Chong Zhang8e89b312015-09-09 15:09:30 -0700140 } catch(RemoteException e) {}
141 } break;
142
143 case MotionEvent.ACTION_UP: {
144 if (DEBUG_TASK_POSITIONING) {
145 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
146 }
147 endDrag = true;
148 } break;
149
150 case MotionEvent.ACTION_CANCEL: {
151 if (DEBUG_TASK_POSITIONING) {
152 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
153 }
154 endDrag = true;
155 } break;
156 }
157
158 if (endDrag) {
Chong Zhang09b21ef2015-09-14 10:20:21 -0700159 mResizing = false;
160 try {
161 mService.mActivityManager.resizeTask(
162 mTaskId, mWindowDragBounds, true /* resizedByUser */);
163 } catch(RemoteException e) {}
Chong Zhang8e89b312015-09-09 15:09:30 -0700164 // Post back to WM to handle clean-ups. We still need the input
165 // event handler for the last finishInputEvent()!
166 mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700167 if (mCurrentDimSide != CTRL_NONE) {
Wale Ogunwale59a73ca2015-09-14 12:54:50 -0700168 final int createMode = mCurrentDimSide == CTRL_LEFT
169 ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
170 : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
171 mService.mActivityManager.moveTaskToDockedStack(
172 mTaskId, createMode, true /*toTop*/);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700173 }
Chong Zhang8e89b312015-09-09 15:09:30 -0700174 }
175 handled = true;
176 } catch (Exception e) {
177 Slog.e(TAG, "Exception caught by drag handleMotion", e);
178 } finally {
179 finishInputEvent(event, handled);
180 }
181 }
182 }
183
184 TaskPositioner(WindowManagerService service) {
185 mService = service;
186 }
187
188 /**
189 * @param display The Display that the window being dragged is on.
190 */
191 void register(Display display) {
192 if (DEBUG_TASK_POSITIONING) {
193 Slog.d(TAG, "Registering task positioner");
194 }
195
196 if (mClientChannel != null) {
197 Slog.e(TAG, "Task positioner already registered");
198 return;
199 }
200
201 mDisplay = display;
202 mDisplay.getMetrics(mDisplayMetrics);
203 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
204 mServerChannel = channels[0];
205 mClientChannel = channels[1];
206 mService.mInputManager.registerInputChannel(mServerChannel, null);
207
208 mInputEventReceiver = new WindowPositionerEventReceiver(mClientChannel,
209 mService.mH.getLooper());
210
211 mDragApplicationHandle = new InputApplicationHandle(null);
212 mDragApplicationHandle.name = TAG;
213 mDragApplicationHandle.dispatchingTimeoutNanos =
214 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
215
216 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
217 mDisplay.getDisplayId());
218 mDragWindowHandle.name = TAG;
219 mDragWindowHandle.inputChannel = mServerChannel;
220 mDragWindowHandle.layer = getDragLayerLocked();
221 mDragWindowHandle.layoutParamsFlags = 0;
222 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
223 mDragWindowHandle.dispatchingTimeoutNanos =
224 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
225 mDragWindowHandle.visible = true;
226 mDragWindowHandle.canReceiveKeys = false;
227 mDragWindowHandle.hasFocus = true;
228 mDragWindowHandle.hasWallpaper = false;
229 mDragWindowHandle.paused = false;
230 mDragWindowHandle.ownerPid = Process.myPid();
231 mDragWindowHandle.ownerUid = Process.myUid();
232 mDragWindowHandle.inputFeatures = 0;
233 mDragWindowHandle.scaleFactor = 1.0f;
234
235 // The drag window cannot receive new touches.
236 mDragWindowHandle.touchableRegion.setEmpty();
237
238 // The drag window covers the entire display
239 mDragWindowHandle.frameLeft = 0;
240 mDragWindowHandle.frameTop = 0;
241 final Point p = new Point();
242 mDisplay.getRealSize(p);
243 mDragWindowHandle.frameRight = p.x;
244 mDragWindowHandle.frameBottom = p.y;
245
246 // Pause rotations before a drag.
247 if (WindowManagerService.DEBUG_ORIENTATION) {
248 Slog.d(TAG, "Pausing rotation during re-position");
249 }
250 mService.pauseRotationLocked();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700251
252 mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
Wale Ogunwale231b06e2015-09-16 12:03:09 -0700253 mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700254 mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
255 mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
Chong Zhang8e89b312015-09-09 15:09:30 -0700256 }
257
258 void unregister() {
259 if (DEBUG_TASK_POSITIONING) {
260 Slog.d(TAG, "Unregistering task positioner");
261 }
262
263 if (mClientChannel == null) {
264 Slog.e(TAG, "Task positioner not registered");
265 return;
266 }
267
268 mService.mInputManager.unregisterInputChannel(mServerChannel);
269
270 mInputEventReceiver.dispose();
271 mInputEventReceiver = null;
272 mClientChannel.dispose();
273 mServerChannel.dispose();
274 mClientChannel = null;
275 mServerChannel = null;
276
277 mDragWindowHandle = null;
278 mDragApplicationHandle = null;
279 mDisplay = null;
280
Wale Ogunwale228d4042015-09-13 10:17:34 -0700281 if (mDimLayer != null) {
282 mDimLayer.destroySurface();
283 mDimLayer = null;
284 }
285 mCurrentDimSide = CTRL_NONE;
286
Chong Zhang8e89b312015-09-09 15:09:30 -0700287 // Resume rotations after a drag.
288 if (WindowManagerService.DEBUG_ORIENTATION) {
289 Slog.d(TAG, "Resuming rotation after re-position");
290 }
291 mService.resumeRotationLocked();
292 }
293
Chong Zhang09b21ef2015-09-14 10:20:21 -0700294 boolean isTaskResizing(final Task task) {
295 return mResizing && task != null && mTaskId == task.mTaskId;
296 }
297
Chong Zhang8e89b312015-09-09 15:09:30 -0700298 void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700299 if (DEBUG_TASK_POSITIONING) {
300 Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
Chong Zhang8e89b312015-09-09 15:09:30 -0700301 + ", {" + startX + ", " + startY + "}");
302 }
303 mCtrlType = CTRL_NONE;
304 if (resize) {
305 final Rect visibleFrame = win.mVisibleFrame;
306 if (startX < visibleFrame.left) {
307 mCtrlType |= CTRL_LEFT;
308 }
309 if (startX > visibleFrame.right) {
310 mCtrlType |= CTRL_RIGHT;
311 }
312 if (startY < visibleFrame.top) {
313 mCtrlType |= CTRL_TOP;
314 }
315 if (startY > visibleFrame.bottom) {
316 mCtrlType |= CTRL_BOTTOM;
317 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700318 mResizing = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700319 }
320
321 final Task task = win.getTask();
322 mTaskId = task.mTaskId;
Wale Ogunwale228d4042015-09-13 10:17:34 -0700323 mStack = task.mStack;
Chong Zhang8e89b312015-09-09 15:09:30 -0700324 mStartDragX = startX;
325 mStartDragY = startY;
326
327 mService.getTaskBounds(mTaskId, mWindowOriginalBounds);
328 }
329
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700330 /** Returns true if the move operation should be ended. */
331 private boolean notifyMoveLocked(float x, float y) {
Chong Zhang8e89b312015-09-09 15:09:30 -0700332 if (DEBUG_TASK_POSITIONING) {
333 Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
334 }
335
336 if (mCtrlType != CTRL_NONE) {
337 // This is a resizing operation.
338 final int deltaX = Math.round(x - mStartDragX);
339 final int deltaY = Math.round(y - mStartDragY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700340 int left = mWindowOriginalBounds.left;
341 int top = mWindowOriginalBounds.top;
342 int right = mWindowOriginalBounds.right;
343 int bottom = mWindowOriginalBounds.bottom;
344 if ((mCtrlType & CTRL_LEFT) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700345 left = Math.min(left + deltaX, right - mMinVisibleWidth);
Chong Zhang8e89b312015-09-09 15:09:30 -0700346 }
347 if ((mCtrlType & CTRL_TOP) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700348 top = Math.min(top + deltaY, bottom - mMinVisibleHeight);
Chong Zhang8e89b312015-09-09 15:09:30 -0700349 }
350 if ((mCtrlType & CTRL_RIGHT) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700351 right = Math.max(left + mMinVisibleWidth, right + deltaX);
Chong Zhang8e89b312015-09-09 15:09:30 -0700352 }
353 if ((mCtrlType & CTRL_BOTTOM) != 0) {
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700354 bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
Chong Zhang8e89b312015-09-09 15:09:30 -0700355 }
356 mWindowDragBounds.set(left, top, right, bottom);
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700357 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700358 }
Wale Ogunwaleb8051b82015-09-17 15:41:52 -0700359
360 // This is a moving operation.
361 mStack.getBounds(mTmpRect);
362 mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
363 if (!mTmpRect.contains((int) x, (int) y)) {
364 // We end the moving operation if position is outside the stack bounds.
365 return true;
366 }
367 mWindowDragBounds.set(mWindowOriginalBounds);
368 mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
369 updateDimLayerVisibility((int) x);
370 return false;
Chong Zhang8e89b312015-09-09 15:09:30 -0700371 }
372
Wale Ogunwale228d4042015-09-13 10:17:34 -0700373 private void updateDimLayerVisibility(int x) {
374 @CtrlType
375 int dimSide = getDimSide(x);
376 if (dimSide == mCurrentDimSide) {
377 return;
378 }
379
380 mCurrentDimSide = dimSide;
381
382 if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
383 SurfaceControl.openTransaction();
384 if (mCurrentDimSide == CTRL_NONE) {
385 mDimLayer.hide();
386 } else {
387 showDimLayer();
388 }
389 SurfaceControl.closeTransaction();
390 }
391
392 /**
393 * Returns the side of the screen the dim layer should be shown.
394 * @param x horizontal coordinate used to determine if the dim layer should be shown
395 * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
396 * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
397 * shouldn't be shown.
398 */
399 private int getDimSide(int x) {
400 if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
401 || !mStack.isFullscreen()
402 || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
403 return CTRL_NONE;
404 }
405
406 mStack.getBounds(mTmpRect);
407 if (x - mSideMargin <= mTmpRect.left) {
408 return CTRL_LEFT;
409 }
410 if (x + mSideMargin >= mTmpRect.right) {
411 return CTRL_RIGHT;
412 }
413
414 return CTRL_NONE;
415 }
416
417 private void showDimLayer() {
418 mStack.getBounds(mTmpRect);
419 if (mCurrentDimSide == CTRL_LEFT) {
420 mTmpRect.right = mTmpRect.centerX();
421 } else if (mCurrentDimSide == CTRL_RIGHT) {
422 mTmpRect.left = mTmpRect.centerX();
423 }
424
425 mDimLayer.setBounds(mTmpRect);
426 mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
427 }
428
429 @Override /** {@link DimLayer.DimLayerUser} */
430 public boolean isFullscreen() {
431 return false;
432 }
433
434 @Override /** {@link DimLayer.DimLayerUser} */
435 public DisplayInfo getDisplayInfo() {
436 return mStack.getDisplayInfo();
437 }
438
Chong Zhang8e89b312015-09-09 15:09:30 -0700439 private int getDragLayerLocked() {
440 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
441 * WindowManagerService.TYPE_LAYER_MULTIPLIER
442 + WindowManagerService.TYPE_LAYER_OFFSET;
443 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700444}