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