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