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 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 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 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 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 206 InputChannel getInputChannel() { 207 return mInputInterceptor == null ? null : mInputInterceptor.mServerChannel; 208 } 209 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 */ 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 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 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 */ 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 */ 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 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 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 */ 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 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 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 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 402 void cancelDragLw() { 403 if (mAnimation != null) { 404 return; 405 } 406 mAnimation = createCancelAnimationLocked(); 407 mService.scheduleAnimationLocked(); 408 } 409 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 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 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. 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 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 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 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 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 615 private boolean isFromSource(int source) { 616 return (mTouchSource & source) == source; 617 } 618 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