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