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