1 /*
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 
17 package com.android.server.wm;
18 
19 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
21 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
22 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24 
25 import android.content.ClipData;
26 import android.content.ClipDescription;
27 import android.content.Context;
28 import android.graphics.Matrix;
29 import android.graphics.Point;
30 import android.hardware.input.InputManager;
31 import android.os.Build;
32 import android.os.IBinder;
33 import android.os.Message;
34 import android.os.Process;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.os.IUserManager;
40 import android.util.Slog;
41 import android.view.Display;
42 import android.view.DragEvent;
43 import android.view.InputChannel;
44 import android.view.InputDevice;
45 import android.view.PointerIcon;
46 import android.view.SurfaceControl;
47 import android.view.View;
48 import android.view.WindowManager;
49 import android.view.animation.AlphaAnimation;
50 import android.view.animation.Animation;
51 import android.view.animation.AnimationSet;
52 import android.view.animation.DecelerateInterpolator;
53 import android.view.animation.Interpolator;
54 import android.view.animation.ScaleAnimation;
55 import android.view.animation.Transformation;
56 import android.view.animation.TranslateAnimation;
57 
58 import com.android.server.input.InputApplicationHandle;
59 import com.android.server.input.InputWindowHandle;
60 import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
61 import com.android.server.wm.WindowManagerService.H;
62 
63 import com.android.internal.view.IDragAndDropPermissions;
64 
65 import java.util.ArrayList;
66 
67 /**
68  * Drag/drop state
69  */
70 class DragState {
71     private static final long MIN_ANIMATION_DURATION_MS = 195;
72     private static final long MAX_ANIMATION_DURATION_MS = 375;
73 
74     private static final int DRAG_FLAGS_URI_ACCESS = View.DRAG_FLAG_GLOBAL_URI_READ |
75             View.DRAG_FLAG_GLOBAL_URI_WRITE;
76 
77     private static final int DRAG_FLAGS_URI_PERMISSIONS = DRAG_FLAGS_URI_ACCESS |
78             View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION |
79             View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION;
80 
81     final WindowManagerService mService;
82     IBinder mToken;
83     SurfaceControl mSurfaceControl;
84     int mFlags;
85     IBinder mLocalWin;
86     int mPid;
87     int mUid;
88     int mSourceUserId;
89     boolean mCrossProfileCopyAllowed;
90     ClipData mData;
91     ClipDescription mDataDescription;
92     int mTouchSource;
93     boolean mDragResult;
94     float mOriginalAlpha;
95     float mOriginalX, mOriginalY;
96     float mCurrentX, mCurrentY;
97     float mThumbOffsetX, mThumbOffsetY;
98     InputInterceptor mInputInterceptor;
99     WindowState mTargetWindow;
100     ArrayList<WindowState> mNotifiedWindows;
101     boolean mDragInProgress;
102     DisplayContent mDisplayContent;
103 
104     private Animation mAnimation;
105     final Transformation mTransformation = new Transformation();
106     private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
107     private Point mDisplaySize = new Point();
108 
DragState(WindowManagerService service, IBinder token, SurfaceControl surface, int flags, IBinder localWin)109     DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
110             int flags, IBinder localWin) {
111         mService = service;
112         mToken = token;
113         mSurfaceControl = surface;
114         mFlags = flags;
115         mLocalWin = localWin;
116         mNotifiedWindows = new ArrayList<WindowState>();
117     }
118 
reset()119     void reset() {
120         if (mSurfaceControl != null) {
121             mSurfaceControl.destroy();
122         }
123         mSurfaceControl = null;
124         mFlags = 0;
125         mLocalWin = null;
126         mToken = null;
127         mData = null;
128         mThumbOffsetX = mThumbOffsetY = 0;
129         mNotifiedWindows = null;
130     }
131 
132     class InputInterceptor {
133         InputChannel mServerChannel, mClientChannel;
134         DragInputEventReceiver mInputEventReceiver;
135         InputApplicationHandle mDragApplicationHandle;
136         InputWindowHandle mDragWindowHandle;
137 
InputInterceptor(Display display)138         InputInterceptor(Display display) {
139             InputChannel[] channels = InputChannel.openInputChannelPair("drag");
140             mServerChannel = channels[0];
141             mClientChannel = channels[1];
142             mService.mInputManager.registerInputChannel(mServerChannel, null);
143             mInputEventReceiver = mService.new DragInputEventReceiver(mClientChannel,
144                     mService.mH.getLooper());
145 
146             mDragApplicationHandle = new InputApplicationHandle(null);
147             mDragApplicationHandle.name = "drag";
148             mDragApplicationHandle.dispatchingTimeoutNanos =
149                     WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
150 
151             mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
152                     display.getDisplayId());
153             mDragWindowHandle.name = "drag";
154             mDragWindowHandle.inputChannel = mServerChannel;
155             mDragWindowHandle.layer = getDragLayerLw();
156             mDragWindowHandle.layoutParamsFlags = 0;
157             mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
158             mDragWindowHandle.dispatchingTimeoutNanos =
159                     WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
160             mDragWindowHandle.visible = true;
161             mDragWindowHandle.canReceiveKeys = false;
162             mDragWindowHandle.hasFocus = true;
163             mDragWindowHandle.hasWallpaper = false;
164             mDragWindowHandle.paused = false;
165             mDragWindowHandle.ownerPid = Process.myPid();
166             mDragWindowHandle.ownerUid = Process.myUid();
167             mDragWindowHandle.inputFeatures = 0;
168             mDragWindowHandle.scaleFactor = 1.0f;
169 
170             // The drag window cannot receive new touches.
171             mDragWindowHandle.touchableRegion.setEmpty();
172 
173             // The drag window covers the entire display
174             mDragWindowHandle.frameLeft = 0;
175             mDragWindowHandle.frameTop = 0;
176             mDragWindowHandle.frameRight = mDisplaySize.x;
177             mDragWindowHandle.frameBottom = mDisplaySize.y;
178 
179             // Pause rotations before a drag.
180             if (DEBUG_ORIENTATION) {
181                 Slog.d(TAG_WM, "Pausing rotation during drag");
182             }
183             mService.pauseRotationLocked();
184         }
185 
tearDown()186         void tearDown() {
187             mService.mInputManager.unregisterInputChannel(mServerChannel);
188             mInputEventReceiver.dispose();
189             mInputEventReceiver = null;
190             mClientChannel.dispose();
191             mServerChannel.dispose();
192             mClientChannel = null;
193             mServerChannel = null;
194 
195             mDragWindowHandle = null;
196             mDragApplicationHandle = null;
197 
198             // Resume rotations after a drag.
199             if (DEBUG_ORIENTATION) {
200                 Slog.d(TAG_WM, "Resuming rotation after drag");
201             }
202             mService.resumeRotationLocked();
203         }
204     }
205 
getInputChannel()206     InputChannel getInputChannel() {
207         return mInputInterceptor == null ? null : mInputInterceptor.mServerChannel;
208     }
209 
getInputWindowHandle()210     InputWindowHandle getInputWindowHandle() {
211         return mInputInterceptor == null ? null : mInputInterceptor.mDragWindowHandle;
212     }
213 
214     /**
215      * @param display The Display that the window being dragged is on.
216      */
register(Display display)217     void register(Display display) {
218         display.getRealSize(mDisplaySize);
219         if (DEBUG_DRAG) Slog.d(TAG_WM, "registering drag input channel");
220         if (mInputInterceptor != null) {
221             Slog.e(TAG_WM, "Duplicate register of drag input channel");
222         } else {
223             mInputInterceptor = new InputInterceptor(display);
224             mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
225         }
226     }
227 
unregister()228     void unregister() {
229         if (DEBUG_DRAG) Slog.d(TAG_WM, "unregistering drag input channel");
230         if (mInputInterceptor == null) {
231             Slog.e(TAG_WM, "Unregister of nonexistent drag input channel");
232         } else {
233             // Input channel should be disposed on the thread where the input is being handled.
234             mService.mH.obtainMessage(
235                     H.TEAR_DOWN_DRAG_AND_DROP_INPUT, mInputInterceptor).sendToTarget();
236             mInputInterceptor = null;
237             mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
238         }
239     }
240 
getDragLayerLw()241     int getDragLayerLw() {
242         return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_DRAG)
243                 * WindowManagerService.TYPE_LAYER_MULTIPLIER
244                 + WindowManagerService.TYPE_LAYER_OFFSET;
245     }
246 
247     /* call out to each visible window/session informing it about the drag
248      */
broadcastDragStartedLw(final float touchX, final float touchY)249     void broadcastDragStartedLw(final float touchX, final float touchY) {
250         mOriginalX = mCurrentX = touchX;
251         mOriginalY = mCurrentY = touchY;
252 
253         // Cache a base-class instance of the clip metadata so that parceling
254         // works correctly in calling out to the apps.
255         mDataDescription = (mData != null) ? mData.getDescription() : null;
256         mNotifiedWindows.clear();
257         mDragInProgress = true;
258 
259         mSourceUserId = UserHandle.getUserId(mUid);
260 
261         final IUserManager userManager =
262                 (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
263         try {
264             mCrossProfileCopyAllowed = !userManager.getUserRestrictions(mSourceUserId).getBoolean(
265                     UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
266         } catch (RemoteException e) {
267             Slog.e(TAG_WM, "Remote Exception calling UserManager: " + e);
268             mCrossProfileCopyAllowed = false;
269         }
270 
271         if (DEBUG_DRAG) {
272             Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
273         }
274 
275         mDisplayContent.forAllWindows(w -> {
276             sendDragStartedLw(w, touchX, touchY, mDataDescription);
277         }, false /* traverseTopToBottom */ );
278     }
279 
280     /* helper - send a ACTION_DRAG_STARTED event, if the
281      * designated window is potentially a drop recipient.  There are race situations
282      * around DRAG_ENDED broadcast, so we make sure that once we've declared that
283      * the drag has ended, we never send out another DRAG_STARTED for this drag action.
284      *
285      * This method clones the 'event' parameter if it's being delivered to the same
286      * process, so it's safe for the caller to call recycle() on the event afterwards.
287      */
sendDragStartedLw(WindowState newWin, float touchX, float touchY, ClipDescription desc)288     private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
289             ClipDescription desc) {
290         if (mDragInProgress && isValidDropTarget(newWin)) {
291             DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
292                     touchX, touchY, null, desc, null, null, false);
293             try {
294                 newWin.mClient.dispatchDragEvent(event);
295                 // track each window that we've notified that the drag is starting
296                 mNotifiedWindows.add(newWin);
297             } catch (RemoteException e) {
298                 Slog.w(TAG_WM, "Unable to drag-start window " + newWin);
299             } finally {
300                 // if the callee was local, the dispatch has already recycled the event
301                 if (Process.myPid() != newWin.mSession.mPid) {
302                     event.recycle();
303                 }
304             }
305         }
306     }
307 
isValidDropTarget(WindowState targetWin)308     private boolean isValidDropTarget(WindowState targetWin) {
309         if (targetWin == null) {
310             return false;
311         }
312         if (!targetWin.isPotentialDragTarget()) {
313             return false;
314         }
315         if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0 || !targetWindowSupportsGlobalDrag(targetWin)) {
316             // Drag is limited to the current window.
317             if (mLocalWin != targetWin.mClient.asBinder()) {
318                 return false;
319             }
320         }
321 
322         return mCrossProfileCopyAllowed ||
323                 mSourceUserId == UserHandle.getUserId(targetWin.getOwningUid());
324     }
325 
targetWindowSupportsGlobalDrag(WindowState targetWin)326     private boolean targetWindowSupportsGlobalDrag(WindowState targetWin) {
327         // Global drags are limited to system windows, and windows for apps that are targeting N and
328         // above.
329         return targetWin.mAppToken == null
330                 || targetWin.mAppToken.mTargetSdk >= Build.VERSION_CODES.N;
331     }
332 
333     /* helper - send a ACTION_DRAG_STARTED event only if the window has not
334      * previously been notified, i.e. it became visible after the drag operation
335      * was begun.  This is a rare case.
336      */
sendDragStartedIfNeededLw(WindowState newWin)337     void sendDragStartedIfNeededLw(WindowState newWin) {
338         if (mDragInProgress) {
339             // If we have sent the drag-started, we needn't do so again
340             if (isWindowNotified(newWin)) {
341                 return;
342             }
343             if (DEBUG_DRAG) {
344                 Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin);
345             }
346             sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
347         }
348     }
349 
isWindowNotified(WindowState newWin)350     private boolean isWindowNotified(WindowState newWin) {
351         for (WindowState ws : mNotifiedWindows) {
352             if (ws == newWin) {
353                 return true;
354             }
355         }
356         return false;
357     }
358 
broadcastDragEndedLw()359     private void broadcastDragEndedLw() {
360         final int myPid = Process.myPid();
361 
362         if (DEBUG_DRAG) {
363             Slog.d(TAG_WM, "broadcasting DRAG_ENDED");
364         }
365         for (WindowState ws : mNotifiedWindows) {
366             float x = 0;
367             float y = 0;
368             if (!mDragResult && (ws.mSession.mPid == mPid)) {
369                 // Report unconsumed drop location back to the app that started the drag.
370                 x = mCurrentX;
371                 y = mCurrentY;
372             }
373             DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
374                     x, y, null, null, null, null, mDragResult);
375             try {
376                 ws.mClient.dispatchDragEvent(evt);
377             } catch (RemoteException e) {
378                 Slog.w(TAG_WM, "Unable to drag-end window " + ws);
379             }
380             // if the current window is in the same process,
381             // the dispatch has already recycled the event
382             if (myPid != ws.mSession.mPid) {
383                 evt.recycle();
384             }
385         }
386         mNotifiedWindows.clear();
387         mDragInProgress = false;
388     }
389 
endDragLw()390     void endDragLw() {
391         if (mAnimation != null) {
392             return;
393         }
394         if (!mDragResult) {
395             mAnimation = createReturnAnimationLocked();
396             mService.scheduleAnimationLocked();
397             return;  // Will call cleanUpDragLw when the animation is done.
398         }
399         cleanUpDragLw();
400     }
401 
cancelDragLw()402     void cancelDragLw() {
403         if (mAnimation != null) {
404             return;
405         }
406         mAnimation = createCancelAnimationLocked();
407         mService.scheduleAnimationLocked();
408     }
409 
cleanUpDragLw()410     private void cleanUpDragLw() {
411         broadcastDragEndedLw();
412         if (isFromSource(InputDevice.SOURCE_MOUSE)) {
413             mService.restorePointerIconLocked(mDisplayContent, mCurrentX, mCurrentY);
414         }
415 
416         // stop intercepting input
417         unregister();
418 
419         // free our resources and drop all the object references
420         reset();
421         mService.mDragState = null;
422     }
423 
notifyMoveLw(float x, float y)424     void notifyMoveLw(float x, float y) {
425         if (mAnimation != null) {
426             return;
427         }
428         mCurrentX = x;
429         mCurrentY = y;
430 
431         // Move the surface to the given touch
432         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
433                 TAG_WM, ">>> OPEN TRANSACTION notifyMoveLw");
434         mService.openSurfaceTransaction();
435         try {
436             mSurfaceControl.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
437             if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  DRAG "
438                     + mSurfaceControl + ": pos=(" +
439                     (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
440         } finally {
441             mService.closeSurfaceTransaction();
442             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
443                     TAG_WM, "<<< CLOSE TRANSACTION notifyMoveLw");
444         }
445         notifyLocationLw(x, y);
446     }
447 
notifyLocationLw(float x, float y)448     void notifyLocationLw(float x, float y) {
449         // Tell the affected window
450         WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
451         if (touchedWin != null && !isWindowNotified(touchedWin)) {
452             // The drag point is over a window which was not notified about a drag start.
453             // Pretend it's over empty space.
454             touchedWin = null;
455         }
456 
457         try {
458             final int myPid = Process.myPid();
459 
460             // have we dragged over a new window?
461             if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
462                 if (DEBUG_DRAG) {
463                     Slog.d(TAG_WM, "sending DRAG_EXITED to " + mTargetWindow);
464                 }
465                 // force DRAG_EXITED_EVENT if appropriate
466                 DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
467                         0, 0, null, null, null, null, false);
468                 mTargetWindow.mClient.dispatchDragEvent(evt);
469                 if (myPid != mTargetWindow.mSession.mPid) {
470                     evt.recycle();
471                 }
472             }
473             if (touchedWin != null) {
474                 if (false && DEBUG_DRAG) {
475                     Slog.d(TAG_WM, "sending DRAG_LOCATION to " + touchedWin);
476                 }
477                 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
478                         x, y, null, null, null, null, false);
479                 touchedWin.mClient.dispatchDragEvent(evt);
480                 if (myPid != touchedWin.mSession.mPid) {
481                     evt.recycle();
482                 }
483             }
484         } catch (RemoteException e) {
485             Slog.w(TAG_WM, "can't send drag notification to windows");
486         }
487         mTargetWindow = touchedWin;
488     }
489 
490     // Find the drop target and tell it about the data.  Returns 'true' if we can immediately
491     // dispatch the global drag-ended message, 'false' if we need to wait for a
492     // result from the recipient.
notifyDropLw(float x, float y)493     boolean notifyDropLw(float x, float y) {
494         if (mAnimation != null) {
495             return false;
496         }
497         mCurrentX = x;
498         mCurrentY = y;
499 
500         WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
501 
502         if (!isWindowNotified(touchedWin)) {
503             // "drop" outside a valid window -- no recipient to apply a
504             // timeout to, and we can send the drag-ended message immediately.
505             mDragResult = false;
506             return true;
507         }
508 
509         if (DEBUG_DRAG) {
510             Slog.d(TAG_WM, "sending DROP to " + touchedWin);
511         }
512 
513         final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());
514 
515         DragAndDropPermissionsHandler dragAndDropPermissions = null;
516         if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
517                 (mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
518             dragAndDropPermissions = new DragAndDropPermissionsHandler(
519                     mData,
520                     mUid,
521                     touchedWin.getOwningPackage(),
522                     mFlags & DRAG_FLAGS_URI_PERMISSIONS,
523                     mSourceUserId,
524                     targetUserId);
525         }
526         if (mSourceUserId != targetUserId){
527             mData.fixUris(mSourceUserId);
528         }
529         final int myPid = Process.myPid();
530         final IBinder token = touchedWin.mClient.asBinder();
531         DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
532                 null, null, mData, dragAndDropPermissions, false);
533         try {
534             touchedWin.mClient.dispatchDragEvent(evt);
535 
536             // 5 second timeout for this window to respond to the drop
537             mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token);
538             Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
539             mService.mH.sendMessageDelayed(msg, 5000);
540         } catch (RemoteException e) {
541             Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin);
542             return true;
543         } finally {
544             if (myPid != touchedWin.mSession.mPid) {
545                 evt.recycle();
546             }
547         }
548         mToken = token;
549         return false;
550     }
551 
obtainDragEvent(WindowState win, int action, float x, float y, Object localState, ClipDescription description, ClipData data, IDragAndDropPermissions dragAndDropPermissions, boolean result)552     private static DragEvent obtainDragEvent(WindowState win, int action,
553             float x, float y, Object localState,
554             ClipDescription description, ClipData data,
555             IDragAndDropPermissions dragAndDropPermissions,
556             boolean result) {
557         final float winX = win.translateToWindowX(x);
558         final float winY = win.translateToWindowY(y);
559         return DragEvent.obtain(action, winX, winY, localState, description, data,
560                 dragAndDropPermissions, result);
561     }
562 
stepAnimationLocked(long currentTimeMs)563     boolean stepAnimationLocked(long currentTimeMs) {
564         if (mAnimation == null) {
565             return false;
566         }
567 
568         mTransformation.clear();
569         if (!mAnimation.getTransformation(currentTimeMs, mTransformation)) {
570             cleanUpDragLw();
571             return false;
572         }
573 
574         mTransformation.getMatrix().postTranslate(
575                 mCurrentX - mThumbOffsetX, mCurrentY - mThumbOffsetY);
576         final float tmpFloats[] = mService.mTmpFloats;
577         mTransformation.getMatrix().getValues(tmpFloats);
578         mSurfaceControl.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
579         mSurfaceControl.setAlpha(mTransformation.getAlpha());
580         mSurfaceControl.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
581                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
582         return true;
583     }
584 
createReturnAnimationLocked()585     private Animation createReturnAnimationLocked() {
586         final AnimationSet set = new AnimationSet(false);
587         final float translateX = mOriginalX - mCurrentX;
588         final float translateY = mOriginalY - mCurrentY;
589         set.addAnimation(new TranslateAnimation( 0, translateX, 0, translateY));
590         set.addAnimation(new AlphaAnimation(mOriginalAlpha, mOriginalAlpha / 2));
591         // Adjust the duration to the travel distance.
592         final double travelDistance = Math.sqrt(translateX * translateX + translateY * translateY);
593         final double displayDiagonal =
594                 Math.sqrt(mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y);
595         final long duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal
596                 * (MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS));
597         set.setDuration(duration);
598         set.setInterpolator(mCubicEaseOutInterpolator);
599         set.initialize(0, 0, 0, 0);
600         set.start();  // Will start on the first call to getTransformation.
601         return set;
602     }
603 
createCancelAnimationLocked()604     private Animation createCancelAnimationLocked() {
605         final AnimationSet set = new AnimationSet(false);
606         set.addAnimation(new ScaleAnimation(1, 0, 1, 0, mThumbOffsetX, mThumbOffsetY));
607         set.addAnimation(new AlphaAnimation(mOriginalAlpha, 0));
608         set.setDuration(MIN_ANIMATION_DURATION_MS);
609         set.setInterpolator(mCubicEaseOutInterpolator);
610         set.initialize(0, 0, 0, 0);
611         set.start();  // Will start on the first call to getTransformation.
612         return set;
613     }
614 
isFromSource(int source)615     private boolean isFromSource(int source) {
616         return (mTouchSource & source) == source;
617     }
618 
overridePointerIconLw(int touchSource)619     void overridePointerIconLw(int touchSource) {
620         mTouchSource = touchSource;
621         if (isFromSource(InputDevice.SOURCE_MOUSE)) {
622             InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_GRABBING);
623         }
624     }
625 }
626