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