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 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 */ 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 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 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 281 InputChannel getInputChannel() { 282 return mInputInterceptor == null ? null : mInputInterceptor.mServerChannel; 283 } 284 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 */ 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 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 */ 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 */ 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 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 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 */ 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 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 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 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 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 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 */ 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 */ 574 boolean isInProgress() { 575 return mDragInProgress; 576 } 577 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 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 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 637 private boolean isFromSource(int source) { 638 return (mTouchSource & source) == source; 639 } 640 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 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 669 public void onAnimationStart(Animator animator) {} 670 671 @Override 672 public void onAnimationCancel(Animator animator) {} 673 674 @Override 675 public void onAnimationRepeat(Animator animator) {} 676 677 @Override 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