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