DragLayer.java revision 6441de0ec2a71862798dd51180d0811b42edd514
1/* 2 * Copyright (C) 2008 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 17package com.android.launcher2; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.animation.TimeInterpolator; 23import android.animation.ValueAnimator; 24import android.animation.ValueAnimator.AnimatorUpdateListener; 25import android.content.Context; 26import android.content.res.Resources; 27import android.graphics.Canvas; 28import android.graphics.Rect; 29import android.graphics.drawable.Drawable; 30import android.util.AttributeSet; 31import android.view.KeyEvent; 32import android.view.MotionEvent; 33import android.view.View; 34import android.view.ViewParent; 35import android.view.accessibility.AccessibilityEvent; 36import android.view.accessibility.AccessibilityManager; 37import android.view.animation.DecelerateInterpolator; 38import android.view.animation.Interpolator; 39import android.widget.FrameLayout; 40import android.widget.TextView; 41 42import com.android.launcher.R; 43 44import java.util.ArrayList; 45 46/** 47 * A ViewGroup that coordinates dragging across its descendants 48 */ 49public class DragLayer extends FrameLayout { 50 private DragController mDragController; 51 private int[] mTmpXY = new int[2]; 52 53 private int mXDown, mYDown; 54 private Launcher mLauncher; 55 56 // Variables relating to resizing widgets 57 private final ArrayList<AppWidgetResizeFrame> mResizeFrames = 58 new ArrayList<AppWidgetResizeFrame>(); 59 private AppWidgetResizeFrame mCurrentResizeFrame; 60 61 // Variables relating to animation of views after drop 62 private ValueAnimator mDropAnim = null; 63 private ValueAnimator mFadeOutAnim = null; 64 private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); 65 private View mDropView = null; 66 private int mAnchorViewInitialScrollX = 0; 67 private View mAnchorView = null; 68 69 private int[] mDropViewPos = new int[2]; 70 private float mDropViewScale; 71 private float mDropViewAlpha; 72 private boolean mHoverPointClosesFolder = false; 73 private Rect mHitRect = new Rect(); 74 private int mWorkspaceIndex = -1; 75 private int mQsbIndex = -1; 76 77 /** 78 * Used to create a new DragLayer from XML. 79 * 80 * @param context The application's context. 81 * @param attrs The attributes set containing the Workspace's customization values. 82 */ 83 public DragLayer(Context context, AttributeSet attrs) { 84 super(context, attrs); 85 86 // Disable multitouch across the workspace/all apps/customize tray 87 setMotionEventSplittingEnabled(false); 88 setChildrenDrawingOrderEnabled(true); 89 } 90 91 public void setup(Launcher launcher, DragController controller) { 92 mLauncher = launcher; 93 mDragController = controller; 94 } 95 96 @Override 97 public boolean dispatchKeyEvent(KeyEvent event) { 98 return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); 99 } 100 101 private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) { 102 getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect); 103 if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) { 104 return true; 105 } 106 return false; 107 } 108 109 private boolean isEventOverFolder(Folder folder, MotionEvent ev) { 110 getDescendantRectRelativeToSelf(folder, mHitRect); 111 if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) { 112 return true; 113 } 114 return false; 115 } 116 117 private boolean handleTouchDown(MotionEvent ev, boolean intercept) { 118 Rect hitRect = new Rect(); 119 int x = (int) ev.getX(); 120 int y = (int) ev.getY(); 121 122 for (AppWidgetResizeFrame child: mResizeFrames) { 123 child.getHitRect(hitRect); 124 if (hitRect.contains(x, y)) { 125 if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) { 126 mCurrentResizeFrame = child; 127 mXDown = x; 128 mYDown = y; 129 requestDisallowInterceptTouchEvent(true); 130 return true; 131 } 132 } 133 } 134 135 Folder currentFolder = mLauncher.getWorkspace().getOpenFolder(); 136 if (currentFolder != null && !mLauncher.isFolderClingVisible() && intercept) { 137 if (currentFolder.isEditingName()) { 138 if (!isEventOverFolderTextRegion(currentFolder, ev)) { 139 currentFolder.dismissEditingName(); 140 return true; 141 } 142 } 143 144 getDescendantRectRelativeToSelf(currentFolder, hitRect); 145 if (!isEventOverFolder(currentFolder, ev)) { 146 mLauncher.closeFolder(); 147 return true; 148 } 149 } 150 return false; 151 } 152 153 @Override 154 public boolean onInterceptTouchEvent(MotionEvent ev) { 155 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 156 if (handleTouchDown(ev, true)) { 157 return true; 158 } 159 } 160 clearAllResizeFrames(); 161 return mDragController.onInterceptTouchEvent(ev); 162 } 163 164 @Override 165 public boolean onInterceptHoverEvent(MotionEvent ev) { 166 Folder currentFolder = mLauncher.getWorkspace().getOpenFolder(); 167 if (currentFolder == null) { 168 return false; 169 } else { 170 if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) { 171 final int action = ev.getAction(); 172 boolean isOverFolder; 173 switch (action) { 174 case MotionEvent.ACTION_HOVER_ENTER: 175 isOverFolder = isEventOverFolder(currentFolder, ev); 176 if (!isOverFolder) { 177 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); 178 mHoverPointClosesFolder = true; 179 return true; 180 } else if (isOverFolder) { 181 mHoverPointClosesFolder = false; 182 } else { 183 return true; 184 } 185 case MotionEvent.ACTION_HOVER_MOVE: 186 isOverFolder = isEventOverFolder(currentFolder, ev); 187 if (!isOverFolder && !mHoverPointClosesFolder) { 188 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); 189 mHoverPointClosesFolder = true; 190 return true; 191 } else if (isOverFolder) { 192 mHoverPointClosesFolder = false; 193 } else { 194 return true; 195 } 196 } 197 } 198 } 199 return false; 200 } 201 202 private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) { 203 if (AccessibilityManager.getInstance(mContext).isEnabled()) { 204 int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close; 205 AccessibilityEvent event = AccessibilityEvent.obtain( 206 AccessibilityEvent.TYPE_VIEW_FOCUSED); 207 onInitializeAccessibilityEvent(event); 208 event.getText().add(mContext.getString(stringId)); 209 AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event); 210 } 211 } 212 213 @Override 214 public boolean onHoverEvent(MotionEvent ev) { 215 // If we've received this, we've already done the necessary handling 216 // in onInterceptHoverEvent. Return true to consume the event. 217 return false; 218 } 219 220 @Override 221 public boolean onTouchEvent(MotionEvent ev) { 222 boolean handled = false; 223 int action = ev.getAction(); 224 225 int x = (int) ev.getX(); 226 int y = (int) ev.getY(); 227 228 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 229 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 230 if (handleTouchDown(ev, false)) { 231 return true; 232 } 233 } 234 } 235 236 if (mCurrentResizeFrame != null) { 237 handled = true; 238 switch (action) { 239 case MotionEvent.ACTION_MOVE: 240 mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown); 241 break; 242 case MotionEvent.ACTION_CANCEL: 243 case MotionEvent.ACTION_UP: 244 mCurrentResizeFrame.commitResizeForDelta(x - mXDown, y - mYDown); 245 mCurrentResizeFrame = null; 246 } 247 } 248 if (handled) return true; 249 return mDragController.onTouchEvent(ev); 250 } 251 252 /** 253 * Determine the rect of the descendant in this DragLayer's coordinates 254 * 255 * @param descendant The descendant whose coordinates we want to find. 256 * @param r The rect into which to place the results. 257 * @return The factor by which this descendant is scaled relative to this DragLayer. 258 */ 259 public float getDescendantRectRelativeToSelf(View descendant, Rect r) { 260 mTmpXY[0] = 0; 261 mTmpXY[1] = 0; 262 float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY); 263 r.set(mTmpXY[0], mTmpXY[1], 264 mTmpXY[0] + descendant.getWidth(), mTmpXY[1] + descendant.getHeight()); 265 return scale; 266 } 267 268 public void getLocationInDragLayer(View child, int[] loc) { 269 loc[0] = 0; 270 loc[1] = 0; 271 getDescendantCoordRelativeToSelf(child, loc); 272 } 273 274 /** 275 * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's 276 * coordinates. 277 * 278 * @param descendant The descendant to which the passed coordinate is relative. 279 * @param coord The coordinate that we want mapped. 280 * @return The factor by which this descendant is scaled relative to this DragLayer. 281 */ 282 public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) { 283 float scale = 1.0f; 284 float[] pt = {coord[0], coord[1]}; 285 descendant.getMatrix().mapPoints(pt); 286 scale *= descendant.getScaleX(); 287 pt[0] += descendant.getLeft(); 288 pt[1] += descendant.getTop(); 289 ViewParent viewParent = descendant.getParent(); 290 while (viewParent instanceof View && viewParent != this) { 291 final View view = (View)viewParent; 292 view.getMatrix().mapPoints(pt); 293 scale *= view.getScaleX(); 294 pt[0] += view.getLeft() - view.getScrollX(); 295 pt[1] += view.getTop() - view.getScrollY(); 296 viewParent = view.getParent(); 297 } 298 coord[0] = (int) Math.round(pt[0]); 299 coord[1] = (int) Math.round(pt[1]); 300 return scale; 301 } 302 303 public void getViewRectRelativeToSelf(View v, Rect r) { 304 int[] loc = new int[2]; 305 getLocationInWindow(loc); 306 int x = loc[0]; 307 int y = loc[1]; 308 309 v.getLocationInWindow(loc); 310 int vX = loc[0]; 311 int vY = loc[1]; 312 313 int left = vX - x; 314 int top = vY - y; 315 r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight()); 316 } 317 318 @Override 319 public boolean dispatchUnhandledMove(View focused, int direction) { 320 return mDragController.dispatchUnhandledMove(focused, direction); 321 } 322 323 public static class LayoutParams extends FrameLayout.LayoutParams { 324 public int x, y; 325 public boolean customPosition = false; 326 327 /** 328 * {@inheritDoc} 329 */ 330 public LayoutParams(int width, int height) { 331 super(width, height); 332 } 333 334 public void setWidth(int width) { 335 this.width = width; 336 } 337 338 public int getWidth() { 339 return width; 340 } 341 342 public void setHeight(int height) { 343 this.height = height; 344 } 345 346 public int getHeight() { 347 return height; 348 } 349 350 public void setX(int x) { 351 this.x = x; 352 } 353 354 public int getX() { 355 return x; 356 } 357 358 public void setY(int y) { 359 this.y = y; 360 } 361 362 public int getY() { 363 return y; 364 } 365 } 366 367 protected void onLayout(boolean changed, int l, int t, int r, int b) { 368 super.onLayout(changed, l, t, r, b); 369 int count = getChildCount(); 370 for (int i = 0; i < count; i++) { 371 View child = getChildAt(i); 372 final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams(); 373 if (flp instanceof LayoutParams) { 374 final LayoutParams lp = (LayoutParams) flp; 375 if (lp.customPosition) { 376 child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height); 377 } 378 } 379 } 380 } 381 382 public void clearAllResizeFrames() { 383 if (mResizeFrames.size() > 0) { 384 for (AppWidgetResizeFrame frame: mResizeFrames) { 385 removeView(frame); 386 } 387 mResizeFrames.clear(); 388 } 389 } 390 391 public boolean hasResizeFrames() { 392 return mResizeFrames.size() > 0; 393 } 394 395 public boolean isWidgetBeingResized() { 396 return mCurrentResizeFrame != null; 397 } 398 399 public void addResizeFrame(ItemInfo itemInfo, LauncherAppWidgetHostView widget, 400 CellLayout cellLayout) { 401 AppWidgetResizeFrame resizeFrame = new AppWidgetResizeFrame(getContext(), 402 itemInfo, widget, cellLayout, this); 403 404 LayoutParams lp = new LayoutParams(-1, -1); 405 lp.customPosition = true; 406 407 addView(resizeFrame, lp); 408 mResizeFrames.add(resizeFrame); 409 410 resizeFrame.snapToWidget(false); 411 } 412 413 public void animateViewIntoPosition(DragView dragView, final View child) { 414 animateViewIntoPosition(dragView, child, null); 415 } 416 417 public void animateViewIntoPosition(DragView dragView, final int[] pos, float scale, 418 Runnable onFinishRunnable) { 419 Rect r = new Rect(); 420 getViewRectRelativeToSelf(dragView, r); 421 final int fromX = r.left; 422 final int fromY = r.top; 423 424 animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], scale, 425 onFinishRunnable, true, -1, null); 426 } 427 428 public void animateViewIntoPosition(DragView dragView, final View child, 429 final Runnable onFinishAnimationRunnable) { 430 animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, null); 431 } 432 433 public void animateViewIntoPosition(DragView dragView, final View child, int duration, 434 final Runnable onFinishAnimationRunnable, View anchorView) { 435 ((CellLayoutChildren) child.getParent()).measureChild(child); 436 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 437 438 Rect r = new Rect(); 439 getViewRectRelativeToSelf(dragView, r); 440 441 int coord[] = new int[2]; 442 coord[0] = lp.x; 443 coord[1] = lp.y; 444 // Since the child hasn't necessarily been laid out, we force the lp to be updated with 445 // the correct coordinates (above) and use these to determine the final location 446 float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord); 447 int toX = coord[0]; 448 int toY = coord[1]; 449 if (child instanceof TextView) { 450 TextView tv = (TextView) child; 451 Drawable d = tv.getCompoundDrawables()[1]; 452 453 // Center in the y coordinate about the target's drawable 454 toY += Math.round(scale * tv.getPaddingTop()); 455 toY -= (dragView.getHeight() - (int) Math.round(scale * d.getIntrinsicHeight())) / 2; 456 // Center in the x coordinate about the target's drawable 457 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; 458 } else if (child instanceof FolderIcon) { 459 // Account for holographic blur padding on the drag view 460 toY -= HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS / 2; 461 // Center in the x coordinate about the target's drawable 462 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; 463 } else { 464 toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2; 465 toX -= (Math.round(scale * (dragView.getMeasuredWidth() 466 - child.getMeasuredWidth()))) / 2; 467 } 468 469 final int fromX = r.left; 470 final int fromY = r.top; 471 child.setVisibility(INVISIBLE); 472 child.setAlpha(0); 473 Runnable onCompleteRunnable = new Runnable() { 474 public void run() { 475 child.setVisibility(VISIBLE); 476 ObjectAnimator oa = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f); 477 oa.setDuration(60); 478 oa.addListener(new AnimatorListenerAdapter() { 479 @Override 480 public void onAnimationEnd(android.animation.Animator animation) { 481 if (onFinishAnimationRunnable != null) { 482 onFinishAnimationRunnable.run(); 483 } 484 } 485 }); 486 oa.start(); 487 } 488 }; 489 animateViewIntoPosition(dragView, fromX, fromY, toX, toY, scale, 490 onCompleteRunnable, true, duration, anchorView); 491 } 492 493 private void animateViewIntoPosition(final View view, final int fromX, final int fromY, 494 final int toX, final int toY, float finalScale, Runnable onCompleteRunnable, 495 boolean fadeOut, int duration, View anchorView) { 496 Rect from = new Rect(fromX, fromY, fromX + 497 view.getMeasuredWidth(), fromY + view.getMeasuredHeight()); 498 Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight()); 499 animateView(view, from, to, 1f, finalScale, duration, null, null, 500 onCompleteRunnable, true, anchorView); 501 } 502 503 /** 504 * This method animates a view at the end of a drag and drop animation. 505 * 506 * @param view The view to be animated. This view is drawn directly into DragLayer, and so 507 * doesn't need to be a child of DragLayer. 508 * @param from The initial location of the view. Only the left and top parameters are used. 509 * @param to The final location of the view. Only the left and top parameters are used. This 510 * location doesn't account for scaling, and so should be centered about the desired 511 * final location (including scaling). 512 * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates. 513 * @param finalScale The final scale of the view. The view is scaled about its center. 514 * @param duration The duration of the animation. 515 * @param motionInterpolator The interpolator to use for the location of the view. 516 * @param alphaInterpolator The interpolator to use for the alpha of the view. 517 * @param onCompleteRunnable Optional runnable to run on animation completion. 518 * @param fadeOut Whether or not to fade out the view once the animation completes. If true, 519 * the runnable will execute after the view is faded out. 520 * @param anchorView If not null, this represents the view which the animated view stays 521 * anchored to in case scrolling is currently taking place. Note: currently this is 522 * only used for the X dimension for the case of the workspace. 523 */ 524 public void animateView(final View view, final Rect from, final Rect to, final float finalAlpha, 525 final float finalScale, int duration, final Interpolator motionInterpolator, 526 final Interpolator alphaInterpolator, final Runnable onCompleteRunnable, 527 final boolean fadeOut, View anchorView) { 528 // Calculate the duration of the animation based on the object's distance 529 final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) + 530 Math.pow(to.top - from.top, 2)); 531 final Resources res = getResources(); 532 final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist); 533 534 // If duration < 0, this is a cue to compute the duration based on the distance 535 if (duration < 0) { 536 duration = res.getInteger(R.integer.config_dropAnimMaxDuration); 537 if (dist < maxDist) { 538 duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist); 539 } 540 } 541 542 if (mDropAnim != null) { 543 mDropAnim.cancel(); 544 } 545 546 if (mFadeOutAnim != null) { 547 mFadeOutAnim.cancel(); 548 } 549 550 mDropView = view; 551 final float initialAlpha = view.getAlpha(); 552 mDropAnim = new ValueAnimator(); 553 if (alphaInterpolator == null || motionInterpolator == null) { 554 mDropAnim.setInterpolator(mCubicEaseOutInterpolator); 555 } 556 557 if (anchorView != null) { 558 mAnchorViewInitialScrollX = anchorView.getScrollX(); 559 } 560 mAnchorView = anchorView; 561 562 mDropAnim.setDuration(duration); 563 mDropAnim.setFloatValues(0.0f, 1.0f); 564 mDropAnim.removeAllUpdateListeners(); 565 mDropAnim.addUpdateListener(new AnimatorUpdateListener() { 566 public void onAnimationUpdate(ValueAnimator animation) { 567 final float percent = (Float) animation.getAnimatedValue(); 568 // Invalidate the old position 569 int width = view.getMeasuredWidth(); 570 int height = view.getMeasuredHeight(); 571 invalidate(mDropViewPos[0], mDropViewPos[1], 572 mDropViewPos[0] + width, mDropViewPos[1] + height); 573 574 float alphaPercent = alphaInterpolator == null ? percent : 575 alphaInterpolator.getInterpolation(percent); 576 float motionPercent = motionInterpolator == null ? percent : 577 motionInterpolator.getInterpolation(percent); 578 579 mDropViewPos[0] = from.left + (int) Math.round(((to.left - from.left) * motionPercent)); 580 mDropViewPos[1] = from.top + (int) Math.round(((to.top - from.top) * motionPercent)); 581 mDropViewScale = percent * finalScale + (1 - percent); 582 mDropViewAlpha = alphaPercent * finalAlpha + (1 - alphaPercent) * initialAlpha; 583 invalidate(mDropViewPos[0], mDropViewPos[1], 584 mDropViewPos[0] + width, mDropViewPos[1] + height); 585 } 586 }); 587 mDropAnim.addListener(new AnimatorListenerAdapter() { 588 public void onAnimationEnd(Animator animation) { 589 if (onCompleteRunnable != null) { 590 onCompleteRunnable.run(); 591 } 592 if (fadeOut) { 593 fadeOutDragView(); 594 } else { 595 mDropView = null; 596 } 597 } 598 }); 599 mDropAnim.start(); 600 } 601 602 private void fadeOutDragView() { 603 mFadeOutAnim = new ValueAnimator(); 604 mFadeOutAnim.setDuration(150); 605 mFadeOutAnim.setFloatValues(0f, 1f); 606 mFadeOutAnim.removeAllUpdateListeners(); 607 mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() { 608 public void onAnimationUpdate(ValueAnimator animation) { 609 final float percent = (Float) animation.getAnimatedValue(); 610 mDropViewAlpha = 1 - percent; 611 int width = mDropView.getMeasuredWidth(); 612 int height = mDropView.getMeasuredHeight(); 613 invalidate(mDropViewPos[0], mDropViewPos[1], 614 mDropViewPos[0] + width, mDropViewPos[1] + height); 615 } 616 }); 617 mFadeOutAnim.addListener(new AnimatorListenerAdapter() { 618 public void onAnimationEnd(Animator animation) { 619 mDropView = null; 620 } 621 }); 622 mFadeOutAnim.start(); 623 } 624 625 @Override 626 protected void onViewAdded(View child) { 627 super.onViewAdded(child); 628 updateChildIndices(); 629 } 630 631 @Override 632 protected void onViewRemoved(View child) { 633 super.onViewRemoved(child); 634 updateChildIndices(); 635 } 636 637 private void updateChildIndices() { 638 if (mLauncher != null) { 639 mWorkspaceIndex = indexOfChild(mLauncher.getWorkspace()); 640 mQsbIndex = indexOfChild(mLauncher.getSearchBar()); 641 } 642 } 643 644 @Override 645 protected int getChildDrawingOrder(int childCount, int i) { 646 // We don't want to prioritize the workspace drawing on top of the other children in 647 // landscape for the overscroll event. 648 if (LauncherApplication.isScreenLandscape(getContext())) { 649 return super.getChildDrawingOrder(childCount, i); 650 } 651 652 if (mWorkspaceIndex == -1 || mQsbIndex == -1 || 653 mLauncher.getWorkspace().isDrawingBackgroundGradient()) { 654 return i; 655 } 656 657 // This ensures that the workspace is drawn above the hotseat and qsb, 658 // except when the workspace is drawing a background gradient, in which 659 // case we want the workspace to stay behind these elements. 660 if (i == mQsbIndex) { 661 return mWorkspaceIndex; 662 } else if (i == mWorkspaceIndex) { 663 return mQsbIndex; 664 } else { 665 return i; 666 } 667 } 668 669 @Override 670 protected void dispatchDraw(Canvas canvas) { 671 super.dispatchDraw(canvas); 672 if (mDropView != null) { 673 // We are animating an item that was just dropped on the home screen. 674 // Render its View in the current animation position. 675 canvas.save(Canvas.MATRIX_SAVE_FLAG); 676 final int xPos = mDropViewPos[0] - mDropView.getScrollX() + (mAnchorView != null 677 ? (mAnchorViewInitialScrollX - mAnchorView.getScrollX()) : 0); 678 final int yPos = mDropViewPos[1] - mDropView.getScrollY(); 679 int width = mDropView.getMeasuredWidth(); 680 int height = mDropView.getMeasuredHeight(); 681 canvas.translate(xPos, yPos); 682 canvas.translate((1 - mDropViewScale) * width / 2, (1 - mDropViewScale) * height / 2); 683 canvas.scale(mDropViewScale, mDropViewScale); 684 mDropView.setAlpha(mDropViewAlpha); 685 mDropView.draw(canvas); 686 canvas.restore(); 687 } 688 } 689} 690