blob: 71b83a5e32ea7c9f2d061b74fff5f774c0e1e905 [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
19import com.android.server.input.InputApplicationHandle;
20import com.android.server.input.InputWindowHandle;
21import com.android.server.wm.WindowManagerService.H;
22import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
23
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26
27import android.annotation.IntDef;
28import android.graphics.Point;
29import android.graphics.Rect;
30import android.os.Looper;
31import android.os.Process;
32import android.os.RemoteException;
33import android.util.DisplayMetrics;
34import android.util.Slog;
35import android.util.TypedValue;
36import android.view.Display;
37import android.view.InputChannel;
38import android.view.InputDevice;
39import android.view.InputEvent;
40import android.view.InputEventReceiver;
41import android.view.MotionEvent;
42import android.view.WindowManager;
43
44class TaskPositioner {
45 private static final String TAG = "TaskPositioner";
46
47 @IntDef(flag = true,
48 value = {
49 CTRL_NONE,
50 CTRL_LEFT,
51 CTRL_RIGHT,
52 CTRL_TOP,
53 CTRL_BOTTOM
54 })
55 @Retention(RetentionPolicy.SOURCE)
56 @interface CtrlType {}
57
58 private static final int CTRL_NONE = 0x0;
59 private static final int CTRL_LEFT = 0x1;
60 private static final int CTRL_RIGHT = 0x2;
61 private static final int CTRL_TOP = 0x4;
62 private static final int CTRL_BOTTOM = 0x8;
63
64 private final WindowManagerService mService;
65 private WindowPositionerEventReceiver mInputEventReceiver;
66 private Display mDisplay;
67 private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
68
69 private int mTaskId;
70 private final Rect mWindowOriginalBounds = new Rect();
71 private final Rect mWindowDragBounds = new Rect();
72 private float mStartDragX;
73 private float mStartDragY;
74 @CtrlType
75 private int mCtrlType = CTRL_NONE;
76
77 InputChannel mServerChannel;
78 InputChannel mClientChannel;
79 InputApplicationHandle mDragApplicationHandle;
80 InputWindowHandle mDragWindowHandle;
81
82 private final class WindowPositionerEventReceiver extends InputEventReceiver {
83 public WindowPositionerEventReceiver(InputChannel inputChannel, Looper looper) {
84 super(inputChannel, looper);
85 }
86
87 @Override
88 public void onInputEvent(InputEvent event) {
89 if (!(event instanceof MotionEvent)
90 || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
91 return;
92 }
93 final MotionEvent motionEvent = (MotionEvent) event;
94 boolean handled = false;
95
96 try {
97 boolean endDrag = false;
98 final float newX = motionEvent.getRawX();
99 final float newY = motionEvent.getRawY();
100
101 switch (motionEvent.getAction()) {
102 case MotionEvent.ACTION_DOWN: {
103 if (DEBUG_TASK_POSITIONING) {
104 Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
105 }
106 } break;
107
108 case MotionEvent.ACTION_MOVE: {
109 if (DEBUG_TASK_POSITIONING){
110 Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
111 }
112 synchronized (mService.mWindowMap) {
113 notifyMoveLocked(newX, newY);
114 }
115 try {
116 mService.mActivityManager.resizeTask(mTaskId, mWindowDragBounds);
117 } catch(RemoteException e) {}
118 } break;
119
120 case MotionEvent.ACTION_UP: {
121 if (DEBUG_TASK_POSITIONING) {
122 Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
123 }
124 endDrag = true;
125 } break;
126
127 case MotionEvent.ACTION_CANCEL: {
128 if (DEBUG_TASK_POSITIONING) {
129 Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
130 }
131 endDrag = true;
132 } break;
133 }
134
135 if (endDrag) {
136 // Post back to WM to handle clean-ups. We still need the input
137 // event handler for the last finishInputEvent()!
138 mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
139 }
140 handled = true;
141 } catch (Exception e) {
142 Slog.e(TAG, "Exception caught by drag handleMotion", e);
143 } finally {
144 finishInputEvent(event, handled);
145 }
146 }
147 }
148
149 TaskPositioner(WindowManagerService service) {
150 mService = service;
151 }
152
153 /**
154 * @param display The Display that the window being dragged is on.
155 */
156 void register(Display display) {
157 if (DEBUG_TASK_POSITIONING) {
158 Slog.d(TAG, "Registering task positioner");
159 }
160
161 if (mClientChannel != null) {
162 Slog.e(TAG, "Task positioner already registered");
163 return;
164 }
165
166 mDisplay = display;
167 mDisplay.getMetrics(mDisplayMetrics);
168 final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
169 mServerChannel = channels[0];
170 mClientChannel = channels[1];
171 mService.mInputManager.registerInputChannel(mServerChannel, null);
172
173 mInputEventReceiver = new WindowPositionerEventReceiver(mClientChannel,
174 mService.mH.getLooper());
175
176 mDragApplicationHandle = new InputApplicationHandle(null);
177 mDragApplicationHandle.name = TAG;
178 mDragApplicationHandle.dispatchingTimeoutNanos =
179 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
180
181 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
182 mDisplay.getDisplayId());
183 mDragWindowHandle.name = TAG;
184 mDragWindowHandle.inputChannel = mServerChannel;
185 mDragWindowHandle.layer = getDragLayerLocked();
186 mDragWindowHandle.layoutParamsFlags = 0;
187 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
188 mDragWindowHandle.dispatchingTimeoutNanos =
189 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
190 mDragWindowHandle.visible = true;
191 mDragWindowHandle.canReceiveKeys = false;
192 mDragWindowHandle.hasFocus = true;
193 mDragWindowHandle.hasWallpaper = false;
194 mDragWindowHandle.paused = false;
195 mDragWindowHandle.ownerPid = Process.myPid();
196 mDragWindowHandle.ownerUid = Process.myUid();
197 mDragWindowHandle.inputFeatures = 0;
198 mDragWindowHandle.scaleFactor = 1.0f;
199
200 // The drag window cannot receive new touches.
201 mDragWindowHandle.touchableRegion.setEmpty();
202
203 // The drag window covers the entire display
204 mDragWindowHandle.frameLeft = 0;
205 mDragWindowHandle.frameTop = 0;
206 final Point p = new Point();
207 mDisplay.getRealSize(p);
208 mDragWindowHandle.frameRight = p.x;
209 mDragWindowHandle.frameBottom = p.y;
210
211 // Pause rotations before a drag.
212 if (WindowManagerService.DEBUG_ORIENTATION) {
213 Slog.d(TAG, "Pausing rotation during re-position");
214 }
215 mService.pauseRotationLocked();
216 }
217
218 void unregister() {
219 if (DEBUG_TASK_POSITIONING) {
220 Slog.d(TAG, "Unregistering task positioner");
221 }
222
223 if (mClientChannel == null) {
224 Slog.e(TAG, "Task positioner not registered");
225 return;
226 }
227
228 mService.mInputManager.unregisterInputChannel(mServerChannel);
229
230 mInputEventReceiver.dispose();
231 mInputEventReceiver = null;
232 mClientChannel.dispose();
233 mServerChannel.dispose();
234 mClientChannel = null;
235 mServerChannel = null;
236
237 mDragWindowHandle = null;
238 mDragApplicationHandle = null;
239 mDisplay = null;
240
241 // Resume rotations after a drag.
242 if (WindowManagerService.DEBUG_ORIENTATION) {
243 Slog.d(TAG, "Resuming rotation after re-position");
244 }
245 mService.resumeRotationLocked();
246 }
247
248 void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
249 if (DEBUG_TASK_POSITIONING) {Slog.d(TAG,
250 "startDragLocked: win=" + win + ", resize=" + resize
251 + ", {" + startX + ", " + startY + "}");
252 }
253 mCtrlType = CTRL_NONE;
254 if (resize) {
255 final Rect visibleFrame = win.mVisibleFrame;
256 if (startX < visibleFrame.left) {
257 mCtrlType |= CTRL_LEFT;
258 }
259 if (startX > visibleFrame.right) {
260 mCtrlType |= CTRL_RIGHT;
261 }
262 if (startY < visibleFrame.top) {
263 mCtrlType |= CTRL_TOP;
264 }
265 if (startY > visibleFrame.bottom) {
266 mCtrlType |= CTRL_BOTTOM;
267 }
268 }
269
270 final Task task = win.getTask();
271 mTaskId = task.mTaskId;
272 mStartDragX = startX;
273 mStartDragY = startY;
274
275 mService.getTaskBounds(mTaskId, mWindowOriginalBounds);
276 }
277
278 private void notifyMoveLocked(float x, float y) {
279 if (DEBUG_TASK_POSITIONING) {
280 Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
281 }
282
283 if (mCtrlType != CTRL_NONE) {
284 // This is a resizing operation.
285 final int deltaX = Math.round(x - mStartDragX);
286 final int deltaY = Math.round(y - mStartDragY);
287 // TODO: fix the min sizes when we have mininum width/height support,
288 // use hard-coded min sizes for now.
289 final int minSizeX = (int)(dipToPx(96));
290 final int minSizeY = (int)(dipToPx(64));
291 int left = mWindowOriginalBounds.left;
292 int top = mWindowOriginalBounds.top;
293 int right = mWindowOriginalBounds.right;
294 int bottom = mWindowOriginalBounds.bottom;
295 if ((mCtrlType & CTRL_LEFT) != 0) {
296 left = Math.min(left + deltaX, right - minSizeX);
297 }
298 if ((mCtrlType & CTRL_TOP) != 0) {
299 top = Math.min(top + deltaY, bottom - minSizeY);
300 }
301 if ((mCtrlType & CTRL_RIGHT) != 0) {
302 right = Math.max(left + minSizeX, right + deltaX);
303 }
304 if ((mCtrlType & CTRL_BOTTOM) != 0) {
305 bottom = Math.max(top + minSizeY, bottom + deltaY);
306 }
307 mWindowDragBounds.set(left, top, right, bottom);
308 } else {
309 // This is a moving operation.
310 mWindowDragBounds.set(mWindowOriginalBounds);
311 mWindowDragBounds.offset(Math.round(x - mStartDragX),
312 Math.round(y - mStartDragY));
313 }
314 }
315
316 private int getDragLayerLocked() {
317 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
318 * WindowManagerService.TYPE_LAYER_MULTIPLIER
319 + WindowManagerService.TYPE_LAYER_OFFSET;
320 }
321
322 private float dipToPx(float dip) {
323 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, mDisplayMetrics);
324 }
325}