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