blob: b2cf3e0c39fc23e271d37f22f87d27799a335bd9 [file] [log] [blame]
Dianne Hackborn6e1eb762011-02-17 16:07:28 -08001/*
2 * Copyright (C) 2011 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
Jeff Brown4532e612012-04-05 14:27:12 -070019import com.android.server.input.InputApplicationHandle;
20import com.android.server.input.InputWindowHandle;
Jeff Brown32cbc38552011-12-01 14:01:49 -080021import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080022import com.android.server.wm.WindowManagerService.H;
23
24import android.content.ClipData;
25import android.content.ClipDescription;
26import android.graphics.Region;
27import android.os.IBinder;
28import android.os.Message;
29import android.os.Process;
30import android.os.RemoteException;
31import android.util.Slog;
32import android.view.DragEvent;
33import android.view.InputChannel;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080034import android.view.Surface;
35import android.view.View;
36import android.view.WindowManager;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080037
38import java.util.ArrayList;
39
40/**
41 * Drag/drop state
42 */
43class DragState {
44 final WindowManagerService mService;
45 IBinder mToken;
46 Surface mSurface;
47 int mFlags;
48 IBinder mLocalWin;
49 ClipData mData;
50 ClipDescription mDataDescription;
51 boolean mDragResult;
52 float mCurrentX, mCurrentY;
53 float mThumbOffsetX, mThumbOffsetY;
54 InputChannel mServerChannel, mClientChannel;
Jeff Brown32cbc38552011-12-01 14:01:49 -080055 DragInputEventReceiver mInputEventReceiver;
Jeff Brownea426552011-07-18 16:53:48 -070056 InputApplicationHandle mDragApplicationHandle;
57 InputWindowHandle mDragWindowHandle;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080058 WindowState mTargetWindow;
59 ArrayList<WindowState> mNotifiedWindows;
60 boolean mDragInProgress;
61
62 private final Region mTmpRegion = new Region();
63
64 DragState(WindowManagerService service, IBinder token, Surface surface,
65 int flags, IBinder localWin) {
66 mService = service;
67 mToken = token;
68 mSurface = surface;
69 mFlags = flags;
70 mLocalWin = localWin;
71 mNotifiedWindows = new ArrayList<WindowState>();
72 }
73
74 void reset() {
75 if (mSurface != null) {
76 mSurface.destroy();
77 }
78 mSurface = null;
79 mFlags = 0;
80 mLocalWin = null;
81 mToken = null;
82 mData = null;
83 mThumbOffsetX = mThumbOffsetY = 0;
84 mNotifiedWindows = null;
85 }
86
87 void register() {
88 if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
89 if (mClientChannel != null) {
90 Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
91 } else {
92 InputChannel[] channels = InputChannel.openInputChannelPair("drag");
93 mServerChannel = channels[0];
94 mClientChannel = channels[1];
95 mService.mInputManager.registerInputChannel(mServerChannel, null);
Jeff Brown32cbc38552011-12-01 14:01:49 -080096 mInputEventReceiver = mService.new DragInputEventReceiver(mClientChannel,
97 mService.mH.getLooper());
Jeff Brownea426552011-07-18 16:53:48 -070098
99 mDragApplicationHandle = new InputApplicationHandle(null);
100 mDragApplicationHandle.name = "drag";
101 mDragApplicationHandle.dispatchingTimeoutNanos =
102 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
103
104 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null);
105 mDragWindowHandle.name = "drag";
106 mDragWindowHandle.inputChannel = mServerChannel;
107 mDragWindowHandle.layer = getDragLayerLw();
108 mDragWindowHandle.layoutParamsFlags = 0;
109 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
110 mDragWindowHandle.dispatchingTimeoutNanos =
111 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
112 mDragWindowHandle.visible = true;
113 mDragWindowHandle.canReceiveKeys = false;
114 mDragWindowHandle.hasFocus = true;
115 mDragWindowHandle.hasWallpaper = false;
116 mDragWindowHandle.paused = false;
117 mDragWindowHandle.ownerPid = Process.myPid();
118 mDragWindowHandle.ownerUid = Process.myUid();
119 mDragWindowHandle.inputFeatures = 0;
120 mDragWindowHandle.scaleFactor = 1.0f;
121
122 // The drag window cannot receive new touches.
123 mDragWindowHandle.touchableRegion.setEmpty();
124
125 // The drag window covers the entire display
126 mDragWindowHandle.frameLeft = 0;
127 mDragWindowHandle.frameTop = 0;
Jeff Brownbc68a592011-07-25 12:58:12 -0700128 mDragWindowHandle.frameRight = mService.mCurDisplayWidth;
129 mDragWindowHandle.frameBottom = mService.mCurDisplayHeight;
Jeff Brown01a98dd2011-09-20 15:08:29 -0700130
131 // Pause rotations before a drag.
132 if (WindowManagerService.DEBUG_ORIENTATION) {
133 Slog.d(WindowManagerService.TAG, "Pausing rotation during drag");
134 }
135 mService.pauseRotationLocked();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800136 }
137 }
138
139 void unregister() {
140 if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "unregistering drag input channel");
141 if (mClientChannel == null) {
142 Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel");
143 } else {
144 mService.mInputManager.unregisterInputChannel(mServerChannel);
Jeff Brown32cbc38552011-12-01 14:01:49 -0800145 mInputEventReceiver.dispose();
146 mInputEventReceiver = null;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800147 mClientChannel.dispose();
148 mServerChannel.dispose();
149 mClientChannel = null;
150 mServerChannel = null;
Jeff Browncc4f7db2011-08-30 20:34:48 -0700151
152 mDragWindowHandle = null;
153 mDragApplicationHandle = null;
Jeff Brown01a98dd2011-09-20 15:08:29 -0700154
155 // Resume rotations after a drag.
156 if (WindowManagerService.DEBUG_ORIENTATION) {
157 Slog.d(WindowManagerService.TAG, "Resuming rotation after drag");
158 }
159 mService.resumeRotationLocked();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800160 }
161 }
162
163 int getDragLayerLw() {
164 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
165 * WindowManagerService.TYPE_LAYER_MULTIPLIER
166 + WindowManagerService.TYPE_LAYER_OFFSET;
167 }
168
169 /* call out to each visible window/session informing it about the drag
170 */
171 void broadcastDragStartedLw(final float touchX, final float touchY) {
172 // Cache a base-class instance of the clip metadata so that parceling
173 // works correctly in calling out to the apps.
174 mDataDescription = (mData != null) ? mData.getDescription() : null;
175 mNotifiedWindows.clear();
176 mDragInProgress = true;
177
178 if (WindowManagerService.DEBUG_DRAG) {
179 Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
180 }
181
182 final int N = mService.mWindows.size();
183 for (int i = 0; i < N; i++) {
184 sendDragStartedLw(mService.mWindows.get(i), touchX, touchY, mDataDescription);
185 }
186 }
187
188 /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
189 * designated window is potentially a drop recipient. There are race situations
190 * around DRAG_ENDED broadcast, so we make sure that once we've declared that
191 * the drag has ended, we never send out another DRAG_STARTED for this drag action.
192 *
193 * This method clones the 'event' parameter if it's being delivered to the same
194 * process, so it's safe for the caller to call recycle() on the event afterwards.
195 */
196 private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
197 ClipDescription desc) {
198 // Don't actually send the event if the drag is supposed to be pinned
199 // to the originating window but 'newWin' is not that window.
200 if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
201 final IBinder winBinder = newWin.mClient.asBinder();
202 if (winBinder != mLocalWin) {
203 if (WindowManagerService.DEBUG_DRAG) {
204 Slog.d(WindowManagerService.TAG, "Not dispatching local DRAG_STARTED to " + newWin);
205 }
206 return;
207 }
208 }
209
210 if (mDragInProgress && newWin.isPotentialDragTarget()) {
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700211 DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
212 touchX, touchY, null, desc, null, false);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800213 try {
214 newWin.mClient.dispatchDragEvent(event);
215 // track each window that we've notified that the drag is starting
216 mNotifiedWindows.add(newWin);
217 } catch (RemoteException e) {
218 Slog.w(WindowManagerService.TAG, "Unable to drag-start window " + newWin);
219 } finally {
220 // if the callee was local, the dispatch has already recycled the event
221 if (Process.myPid() != newWin.mSession.mPid) {
222 event.recycle();
223 }
224 }
225 }
226 }
227
228 /* helper - construct and send a DRAG_STARTED event only if the window has not
229 * previously been notified, i.e. it became visible after the drag operation
230 * was begun. This is a rare case.
231 */
232 void sendDragStartedIfNeededLw(WindowState newWin) {
233 if (mDragInProgress) {
234 // If we have sent the drag-started, we needn't do so again
235 for (WindowState ws : mNotifiedWindows) {
236 if (ws == newWin) {
237 return;
238 }
239 }
240 if (WindowManagerService.DEBUG_DRAG) {
241 Slog.d(WindowManagerService.TAG, "need to send DRAG_STARTED to new window " + newWin);
242 }
243 sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
244 }
245 }
246
247 void broadcastDragEndedLw() {
248 if (WindowManagerService.DEBUG_DRAG) {
249 Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
250 }
251 DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
252 0, 0, null, null, null, mDragResult);
253 for (WindowState ws: mNotifiedWindows) {
254 try {
255 ws.mClient.dispatchDragEvent(evt);
256 } catch (RemoteException e) {
257 Slog.w(WindowManagerService.TAG, "Unable to drag-end window " + ws);
258 }
259 }
260 mNotifiedWindows.clear();
261 mDragInProgress = false;
262 evt.recycle();
263 }
264
265 void endDragLw() {
266 mService.mDragState.broadcastDragEndedLw();
267
268 // stop intercepting input
269 mService.mDragState.unregister();
270 mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
271
272 // free our resources and drop all the object references
273 mService.mDragState.reset();
274 mService.mDragState = null;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800275 }
276
277 void notifyMoveLw(float x, float y) {
278 final int myPid = Process.myPid();
279
280 // Move the surface to the given touch
Dianne Hackborn36991742011-10-11 21:35:26 -0700281 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
282 WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800283 Surface.openTransaction();
284 try {
Dianne Hackbornd040edb2011-08-31 12:47:58 -0700285 mSurface.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800286 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DRAG "
287 + mSurface + ": pos=(" +
288 (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
289 } finally {
290 Surface.closeTransaction();
Dianne Hackborn36991742011-10-11 21:35:26 -0700291 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
292 WindowManagerService.TAG, "<<< CLOSE TRANSACTION notifyMoveLw");
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800293 }
294
295 // Tell the affected window
296 WindowState touchedWin = getTouchedWinAtPointLw(x, y);
297 if (touchedWin == null) {
298 if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "No touched win at x=" + x + " y=" + y);
299 return;
300 }
301 if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
302 final IBinder touchedBinder = touchedWin.mClient.asBinder();
303 if (touchedBinder != mLocalWin) {
304 // This drag is pinned only to the originating window, but the drag
305 // point is outside that window. Pretend it's over empty space.
306 touchedWin = null;
307 }
308 }
309 try {
310 // have we dragged over a new window?
311 if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
312 if (WindowManagerService.DEBUG_DRAG) {
313 Slog.d(WindowManagerService.TAG, "sending DRAG_EXITED to " + mTargetWindow);
314 }
315 // force DRAG_EXITED_EVENT if appropriate
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700316 DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
317 x, y, null, null, null, false);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800318 mTargetWindow.mClient.dispatchDragEvent(evt);
319 if (myPid != mTargetWindow.mSession.mPid) {
320 evt.recycle();
321 }
322 }
323 if (touchedWin != null) {
324 if (false && WindowManagerService.DEBUG_DRAG) {
325 Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin);
326 }
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700327 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
328 x, y, null, null, null, false);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800329 touchedWin.mClient.dispatchDragEvent(evt);
330 if (myPid != touchedWin.mSession.mPid) {
331 evt.recycle();
332 }
333 }
334 } catch (RemoteException e) {
335 Slog.w(WindowManagerService.TAG, "can't send drag notification to windows");
336 }
337 mTargetWindow = touchedWin;
338 }
339
340 // Tell the drop target about the data. Returns 'true' if we can immediately
341 // dispatch the global drag-ended message, 'false' if we need to wait for a
342 // result from the recipient.
343 boolean notifyDropLw(float x, float y) {
344 WindowState touchedWin = getTouchedWinAtPointLw(x, y);
345 if (touchedWin == null) {
346 // "drop" outside a valid window -- no recipient to apply a
347 // timeout to, and we can send the drag-ended message immediately.
348 mDragResult = false;
349 return true;
350 }
351
352 if (WindowManagerService.DEBUG_DRAG) {
353 Slog.d(WindowManagerService.TAG, "sending DROP to " + touchedWin);
354 }
355 final int myPid = Process.myPid();
356 final IBinder token = touchedWin.mClient.asBinder();
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700357 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800358 null, null, mData, false);
359 try {
360 touchedWin.mClient.dispatchDragEvent(evt);
361
362 // 5 second timeout for this window to respond to the drop
363 mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token);
364 Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
365 mService.mH.sendMessageDelayed(msg, 5000);
366 } catch (RemoteException e) {
367 Slog.w(WindowManagerService.TAG, "can't send drop notification to win " + touchedWin);
368 return true;
369 } finally {
370 if (myPid != touchedWin.mSession.mPid) {
371 evt.recycle();
372 }
373 }
374 mToken = token;
375 return false;
376 }
377
378 // Find the visible, touch-deliverable window under the given point
379 private WindowState getTouchedWinAtPointLw(float xf, float yf) {
380 WindowState touchedWin = null;
381 final int x = (int) xf;
382 final int y = (int) yf;
383 final ArrayList<WindowState> windows = mService.mWindows;
384 final int N = windows.size();
385 for (int i = N - 1; i >= 0; i--) {
386 WindowState child = windows.get(i);
387 final int flags = child.mAttrs.flags;
388 if (!child.isVisibleLw()) {
389 // not visible == don't tell about drags
390 continue;
391 }
392 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
393 // not touchable == don't tell about drags
394 continue;
395 }
396
397 child.getTouchableRegion(mTmpRegion);
398
399 final int touchFlags = flags &
400 (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
401 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
402 if (mTmpRegion.contains(x, y) || touchFlags == 0) {
403 // Found it
404 touchedWin = child;
405 break;
406 }
407 }
408
409 return touchedWin;
410 }
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700411
412 private static DragEvent obtainDragEvent(WindowState win, int action,
413 float x, float y, Object localState,
414 ClipDescription description, ClipData data, boolean result) {
415 float winX = x - win.mFrame.left;
416 float winY = y - win.mFrame.top;
417 if (win.mEnforceSizeCompat) {
418 winX *= win.mGlobalScale;
419 winY *= win.mGlobalScale;
420 }
421 return DragEvent.obtain(action, winX, winY, localState, description, data, result);
422 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800423}