blob: 9e29925f1dbf1f3956c105d769828f11185fe362 [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;
Chong Zhang8e89b312015-09-09 15:09:30 -070025
26import android.annotation.IntDef;
27import android.graphics.Point;
28import android.graphics.Rect;
29import android.os.Looper;
30import android.os.Process;
31import android.os.RemoteException;
32import android.util.DisplayMetrics;
33import android.util.Slog;
34import android.util.TypedValue;
35import android.view.Display;
Wale Ogunwale228d4042015-09-13 10:17:34 -070036import android.view.DisplayInfo;
Chong Zhang8e89b312015-09-09 15:09:30 -070037import android.view.InputChannel;
38import android.view.InputDevice;
39import android.view.InputEvent;
40import android.view.InputEventReceiver;
41import android.view.MotionEvent;
Wale Ogunwale228d4042015-09-13 10:17:34 -070042import android.view.SurfaceControl;
Chong Zhang8e89b312015-09-09 15:09:30 -070043import android.view.WindowManager;
44
Wale Ogunwale228d4042015-09-13 10:17:34 -070045import com.android.server.input.InputApplicationHandle;
46import com.android.server.input.InputWindowHandle;
47import com.android.server.wm.WindowManagerService.H;
48
49import java.lang.annotation.Retention;
50import java.lang.annotation.RetentionPolicy;
51
52class TaskPositioner implements DimLayer.DimLayerUser {
Chong Zhang8e89b312015-09-09 15:09:30 -070053 private static final String TAG = "TaskPositioner";
54
Wale Ogunwale228d4042015-09-13 10:17:34 -070055 // The margin the pointer position has to be within the side of the screen to be
56 // considered at the side of the screen.
57 private static final int SIDE_MARGIN_DIP = 5;
58
Chong Zhang8e89b312015-09-09 15:09:30 -070059 @IntDef(flag = true,
60 value = {
61 CTRL_NONE,
62 CTRL_LEFT,
63 CTRL_RIGHT,
64 CTRL_TOP,
65 CTRL_BOTTOM
66 })
67 @Retention(RetentionPolicy.SOURCE)
68 @interface CtrlType {}
69
70 private static final int CTRL_NONE = 0x0;
71 private static final int CTRL_LEFT = 0x1;
72 private static final int CTRL_RIGHT = 0x2;
73 private static final int CTRL_TOP = 0x4;
74 private static final int CTRL_BOTTOM = 0x8;
75
76 private final WindowManagerService mService;
77 private WindowPositionerEventReceiver mInputEventReceiver;
78 private Display mDisplay;
79 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Wale Ogunwale228d4042015-09-13 10:17:34 -070080 private DimLayer mDimLayer;
81 @CtrlType
82 private int mCurrentDimSide;
83 private Rect mTmpRect = new Rect();
84 private int mSideMargin;
Chong Zhang8e89b312015-09-09 15:09:30 -070085
86 private int mTaskId;
Wale Ogunwale228d4042015-09-13 10:17:34 -070087 private TaskStack mStack;
Chong Zhang09b21ef2015-09-14 10:20:21 -070088 private boolean mResizing;
Chong Zhang8e89b312015-09-09 15:09:30 -070089 private final Rect mWindowOriginalBounds = new Rect();
90 private final Rect mWindowDragBounds = new Rect();
91 private float mStartDragX;
92 private float mStartDragY;
93 @CtrlType
94 private int mCtrlType = CTRL_NONE;
95
96 InputChannel mServerChannel;
97 InputChannel mClientChannel;
98 InputApplicationHandle mDragApplicationHandle;
99 InputWindowHandle mDragWindowHandle;
100
101 private final class WindowPositionerEventReceiver extends InputEventReceiver {
102 public WindowPositionerEventReceiver(InputChannel inputChannel, Looper looper) {
103 super(inputChannel, looper);
104 }
105
106 @Override
107 public void onInputEvent(InputEvent event) {
108 if (!(event instanceof MotionEvent)
109 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
110 return;
111 }
112 final MotionEvent motionEvent = (MotionEvent) event;
113 boolean handled = false;
114
115 try {
116 boolean endDrag = false;
117 final float newX = motionEvent.getRawX();
118 final float newY = motionEvent.getRawY();
119
120 switch (motionEvent.getAction()) {
121 case MotionEvent.ACTION_DOWN: {
122 if (DEBUG_TASK_POSITIONING) {
123 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
124 }
125 } break;
126
127 case MotionEvent.ACTION_MOVE: {
128 if (DEBUG_TASK_POSITIONING){
129 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
130 }
131 synchronized (mService.mWindowMap) {
132 notifyMoveLocked(newX, newY);
133 }
134 try {
Chong Zhang09b21ef2015-09-14 10:20:21 -0700135 mService.mActivityManager.resizeTask(
136 mTaskId, mWindowDragBounds, true /* resizedByUser */);
Chong Zhang8e89b312015-09-09 15:09:30 -0700137 } catch(RemoteException e) {}
138 } break;
139
140 case MotionEvent.ACTION_UP: {
141 if (DEBUG_TASK_POSITIONING) {
142 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
143 }
144 endDrag = true;
145 } break;
146
147 case MotionEvent.ACTION_CANCEL: {
148 if (DEBUG_TASK_POSITIONING) {
149 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
150 }
151 endDrag = true;
152 } break;
153 }
154
155 if (endDrag) {
Chong Zhang09b21ef2015-09-14 10:20:21 -0700156 mResizing = false;
157 try {
158 mService.mActivityManager.resizeTask(
159 mTaskId, mWindowDragBounds, true /* resizedByUser */);
160 } catch(RemoteException e) {}
Chong Zhang8e89b312015-09-09 15:09:30 -0700161 // Post back to WM to handle clean-ups. We still need the input
162 // event handler for the last finishInputEvent()!
163 mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700164 if (mCurrentDimSide != CTRL_NONE) {
Wale Ogunwale59a73ca2015-09-14 12:54:50 -0700165 final int createMode = mCurrentDimSide == CTRL_LEFT
166 ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
167 : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
168 mService.mActivityManager.moveTaskToDockedStack(
169 mTaskId, createMode, true /*toTop*/);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700170 }
Chong Zhang8e89b312015-09-09 15:09:30 -0700171 }
172 handled = true;
173 } catch (Exception e) {
174 Slog.e(TAG, "Exception caught by drag handleMotion", e);
175 } finally {
176 finishInputEvent(event, handled);
177 }
178 }
179 }
180
181 TaskPositioner(WindowManagerService service) {
182 mService = service;
183 }
184
185 /**
186 * @param display The Display that the window being dragged is on.
187 */
188 void register(Display display) {
189 if (DEBUG_TASK_POSITIONING) {
190 Slog.d(TAG, "Registering task positioner");
191 }
192
193 if (mClientChannel != null) {
194 Slog.e(TAG, "Task positioner already registered");
195 return;
196 }
197
198 mDisplay = display;
199 mDisplay.getMetrics(mDisplayMetrics);
200 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
201 mServerChannel = channels[0];
202 mClientChannel = channels[1];
203 mService.mInputManager.registerInputChannel(mServerChannel, null);
204
205 mInputEventReceiver = new WindowPositionerEventReceiver(mClientChannel,
206 mService.mH.getLooper());
207
208 mDragApplicationHandle = new InputApplicationHandle(null);
209 mDragApplicationHandle.name = TAG;
210 mDragApplicationHandle.dispatchingTimeoutNanos =
211 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
212
213 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
214 mDisplay.getDisplayId());
215 mDragWindowHandle.name = TAG;
216 mDragWindowHandle.inputChannel = mServerChannel;
217 mDragWindowHandle.layer = getDragLayerLocked();
218 mDragWindowHandle.layoutParamsFlags = 0;
219 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
220 mDragWindowHandle.dispatchingTimeoutNanos =
221 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
222 mDragWindowHandle.visible = true;
223 mDragWindowHandle.canReceiveKeys = false;
224 mDragWindowHandle.hasFocus = true;
225 mDragWindowHandle.hasWallpaper = false;
226 mDragWindowHandle.paused = false;
227 mDragWindowHandle.ownerPid = Process.myPid();
228 mDragWindowHandle.ownerUid = Process.myUid();
229 mDragWindowHandle.inputFeatures = 0;
230 mDragWindowHandle.scaleFactor = 1.0f;
231
232 // The drag window cannot receive new touches.
233 mDragWindowHandle.touchableRegion.setEmpty();
234
235 // The drag window covers the entire display
236 mDragWindowHandle.frameLeft = 0;
237 mDragWindowHandle.frameTop = 0;
238 final Point p = new Point();
239 mDisplay.getRealSize(p);
240 mDragWindowHandle.frameRight = p.x;
241 mDragWindowHandle.frameBottom = p.y;
242
243 // Pause rotations before a drag.
244 if (WindowManagerService.DEBUG_ORIENTATION) {
245 Slog.d(TAG, "Pausing rotation during re-position");
246 }
247 mService.pauseRotationLocked();
Wale Ogunwale228d4042015-09-13 10:17:34 -0700248
249 mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
250 mSideMargin = (int)dipToPx(SIDE_MARGIN_DIP);
Chong Zhang8e89b312015-09-09 15:09:30 -0700251 }
252
253 void unregister() {
254 if (DEBUG_TASK_POSITIONING) {
255 Slog.d(TAG, "Unregistering task positioner");
256 }
257
258 if (mClientChannel == null) {
259 Slog.e(TAG, "Task positioner not registered");
260 return;
261 }
262
263 mService.mInputManager.unregisterInputChannel(mServerChannel);
264
265 mInputEventReceiver.dispose();
266 mInputEventReceiver = null;
267 mClientChannel.dispose();
268 mServerChannel.dispose();
269 mClientChannel = null;
270 mServerChannel = null;
271
272 mDragWindowHandle = null;
273 mDragApplicationHandle = null;
274 mDisplay = null;
275
Wale Ogunwale228d4042015-09-13 10:17:34 -0700276 if (mDimLayer != null) {
277 mDimLayer.destroySurface();
278 mDimLayer = null;
279 }
280 mCurrentDimSide = CTRL_NONE;
281
Chong Zhang8e89b312015-09-09 15:09:30 -0700282 // Resume rotations after a drag.
283 if (WindowManagerService.DEBUG_ORIENTATION) {
284 Slog.d(TAG, "Resuming rotation after re-position");
285 }
286 mService.resumeRotationLocked();
287 }
288
Chong Zhang09b21ef2015-09-14 10:20:21 -0700289 boolean isTaskResizing(final Task task) {
290 return mResizing && task != null && mTaskId == task.mTaskId;
291 }
292
Chong Zhang8e89b312015-09-09 15:09:30 -0700293 void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
Wale Ogunwale228d4042015-09-13 10:17:34 -0700294 if (DEBUG_TASK_POSITIONING) {
295 Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
Chong Zhang8e89b312015-09-09 15:09:30 -0700296 + ", {" + startX + ", " + startY + "}");
297 }
298 mCtrlType = CTRL_NONE;
299 if (resize) {
300 final Rect visibleFrame = win.mVisibleFrame;
301 if (startX < visibleFrame.left) {
302 mCtrlType |= CTRL_LEFT;
303 }
304 if (startX > visibleFrame.right) {
305 mCtrlType |= CTRL_RIGHT;
306 }
307 if (startY < visibleFrame.top) {
308 mCtrlType |= CTRL_TOP;
309 }
310 if (startY > visibleFrame.bottom) {
311 mCtrlType |= CTRL_BOTTOM;
312 }
Chong Zhang09b21ef2015-09-14 10:20:21 -0700313 mResizing = true;
Chong Zhang8e89b312015-09-09 15:09:30 -0700314 }
315
316 final Task task = win.getTask();
317 mTaskId = task.mTaskId;
Wale Ogunwale228d4042015-09-13 10:17:34 -0700318 mStack = task.mStack;
Chong Zhang8e89b312015-09-09 15:09:30 -0700319 mStartDragX = startX;
320 mStartDragY = startY;
321
322 mService.getTaskBounds(mTaskId, mWindowOriginalBounds);
323 }
324
325 private void notifyMoveLocked(float x, float y) {
326 if (DEBUG_TASK_POSITIONING) {
327 Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
328 }
329
330 if (mCtrlType != CTRL_NONE) {
331 // This is a resizing operation.
332 final int deltaX = Math.round(x - mStartDragX);
333 final int deltaY = Math.round(y - mStartDragY);
334 // TODO: fix the min sizes when we have mininum width/height support,
335 // use hard-coded min sizes for now.
336 final int minSizeX = (int)(dipToPx(96));
337 final int minSizeY = (int)(dipToPx(64));
338 int left = mWindowOriginalBounds.left;
339 int top = mWindowOriginalBounds.top;
340 int right = mWindowOriginalBounds.right;
341 int bottom = mWindowOriginalBounds.bottom;
342 if ((mCtrlType & CTRL_LEFT) != 0) {
343 left = Math.min(left + deltaX, right - minSizeX);
344 }
345 if ((mCtrlType & CTRL_TOP) != 0) {
346 top = Math.min(top + deltaY, bottom - minSizeY);
347 }
348 if ((mCtrlType & CTRL_RIGHT) != 0) {
349 right = Math.max(left + minSizeX, right + deltaX);
350 }
351 if ((mCtrlType & CTRL_BOTTOM) != 0) {
352 bottom = Math.max(top + minSizeY, bottom + deltaY);
353 }
354 mWindowDragBounds.set(left, top, right, bottom);
355 } else {
356 // This is a moving operation.
357 mWindowDragBounds.set(mWindowOriginalBounds);
Wale Ogunwale228d4042015-09-13 10:17:34 -0700358 mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
359 updateDimLayerVisibility((int) x);
Chong Zhang8e89b312015-09-09 15:09:30 -0700360 }
361 }
362
Wale Ogunwale228d4042015-09-13 10:17:34 -0700363 private void updateDimLayerVisibility(int x) {
364 @CtrlType
365 int dimSide = getDimSide(x);
366 if (dimSide == mCurrentDimSide) {
367 return;
368 }
369
370 mCurrentDimSide = dimSide;
371
372 if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
373 SurfaceControl.openTransaction();
374 if (mCurrentDimSide == CTRL_NONE) {
375 mDimLayer.hide();
376 } else {
377 showDimLayer();
378 }
379 SurfaceControl.closeTransaction();
380 }
381
382 /**
383 * Returns the side of the screen the dim layer should be shown.
384 * @param x horizontal coordinate used to determine if the dim layer should be shown
385 * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
386 * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
387 * shouldn't be shown.
388 */
389 private int getDimSide(int x) {
390 if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
391 || !mStack.isFullscreen()
392 || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
393 return CTRL_NONE;
394 }
395
396 mStack.getBounds(mTmpRect);
397 if (x - mSideMargin <= mTmpRect.left) {
398 return CTRL_LEFT;
399 }
400 if (x + mSideMargin >= mTmpRect.right) {
401 return CTRL_RIGHT;
402 }
403
404 return CTRL_NONE;
405 }
406
407 private void showDimLayer() {
408 mStack.getBounds(mTmpRect);
409 if (mCurrentDimSide == CTRL_LEFT) {
410 mTmpRect.right = mTmpRect.centerX();
411 } else if (mCurrentDimSide == CTRL_RIGHT) {
412 mTmpRect.left = mTmpRect.centerX();
413 }
414
415 mDimLayer.setBounds(mTmpRect);
416 mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
417 }
418
419 @Override /** {@link DimLayer.DimLayerUser} */
420 public boolean isFullscreen() {
421 return false;
422 }
423
424 @Override /** {@link DimLayer.DimLayerUser} */
425 public DisplayInfo getDisplayInfo() {
426 return mStack.getDisplayInfo();
427 }
428
Chong Zhang8e89b312015-09-09 15:09:30 -0700429 private int getDragLayerLocked() {
430 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
431 * WindowManagerService.TYPE_LAYER_MULTIPLIER
432 + WindowManagerService.TYPE_LAYER_OFFSET;
433 }
434
435 private float dipToPx(float dip) {
436 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, mDisplayMetrics);
437 }
Wale Ogunwale228d4042015-09-13 10:17:34 -0700438}