DragLayer.java revision 7bd1bbb509f9569fa18d6b4d33242679fd98bc9b
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 DragView mDropView = null; 66 private int mAnchorViewInitialScrollX = 0; 67 private View mAnchorView = null; 68 69 private boolean mHoverPointClosesFolder = false; 70 private Rect mHitRect = new Rect(); 71 private int mWorkspaceIndex = -1; 72 private int mQsbIndex = -1; 73 public static final int ANIMATION_END_DISAPPEAR = 0; 74 public static final int ANIMATION_END_FADE_OUT = 1; 75 public static final int ANIMATION_END_REMAIN_VISIBLE = 2; 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 alpha, 418 float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable, 419 int duration) { 420 Rect r = new Rect(); 421 getViewRectRelativeToSelf(dragView, r); 422 final int fromX = r.left; 423 final int fromY = r.top; 424 425 animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY, 426 onFinishRunnable, animationEndStyle, duration, null); 427 } 428 429 public void animateViewIntoPosition(DragView dragView, final View child, 430 final Runnable onFinishAnimationRunnable) { 431 animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, null); 432 } 433 434 public void animateViewIntoPosition(DragView dragView, final View child, int duration, 435 final Runnable onFinishAnimationRunnable, View anchorView) { 436 ((CellLayoutChildren) child.getParent()).measureChild(child); 437 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 438 439 Rect r = new Rect(); 440 getViewRectRelativeToSelf(dragView, r); 441 442 int coord[] = new int[2]; 443 coord[0] = lp.x; 444 coord[1] = lp.y; 445 // Since the child hasn't necessarily been laid out, we force the lp to be updated with 446 // the correct coordinates (above) and use these to determine the final location 447 float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord); 448 int toX = coord[0]; 449 int toY = coord[1]; 450 if (child instanceof TextView) { 451 TextView tv = (TextView) child; 452 Drawable d = tv.getCompoundDrawables()[1]; 453 454 // Center in the y coordinate about the target's drawable 455 toY += Math.round(scale * tv.getPaddingTop()); 456 toY -= (dragView.getHeight() - (int) Math.round(scale * d.getIntrinsicHeight())) / 2; 457 // Center in the x coordinate about the target's drawable 458 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; 459 } else if (child instanceof FolderIcon) { 460 // Account for holographic blur padding on the drag view 461 toY -= HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS / 2; 462 // Center in the x coordinate about the target's drawable 463 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; 464 } else { 465 toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2; 466 toX -= (Math.round(scale * (dragView.getMeasuredWidth() 467 - child.getMeasuredWidth()))) / 2; 468 } 469 470 final int fromX = r.left; 471 final int fromY = r.top; 472 child.setVisibility(INVISIBLE); 473 child.setAlpha(0); 474 Runnable onCompleteRunnable = new Runnable() { 475 public void run() { 476 child.setVisibility(VISIBLE); 477 ObjectAnimator oa = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f); 478 oa.setDuration(60); 479 oa.addListener(new AnimatorListenerAdapter() { 480 @Override 481 public void onAnimationEnd(android.animation.Animator animation) { 482 if (onFinishAnimationRunnable != null) { 483 onFinishAnimationRunnable.run(); 484 } 485 } 486 }); 487 oa.start(); 488 } 489 }; 490 animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, scale, scale, 491 onCompleteRunnable, ANIMATION_END_FADE_OUT, duration, anchorView); 492 } 493 494 private void animateViewIntoPosition(final DragView view, final int fromX, final int fromY, 495 final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY, 496 float finalScaleX, float finalScaleY, Runnable onCompleteRunnable, 497 int animationEndStyle, int duration, View anchorView) { 498 Rect from = new Rect(fromX, fromY, fromX + 499 view.getMeasuredWidth(), fromY + view.getMeasuredHeight()); 500 Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight()); 501 animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration, 502 null, null, onCompleteRunnable, animationEndStyle, anchorView); 503 } 504 505 /** 506 * This method animates a view at the end of a drag and drop animation. 507 * 508 * @param view The view to be animated. This view is drawn directly into DragLayer, and so 509 * doesn't need to be a child of DragLayer. 510 * @param from The initial location of the view. Only the left and top parameters are used. 511 * @param to The final location of the view. Only the left and top parameters are used. This 512 * location doesn't account for scaling, and so should be centered about the desired 513 * final location (including scaling). 514 * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates. 515 * @param finalScale The final scale of the view. The view is scaled about its center. 516 * @param duration The duration of the animation. 517 * @param motionInterpolator The interpolator to use for the location of the view. 518 * @param alphaInterpolator The interpolator to use for the alpha of the view. 519 * @param onCompleteRunnable Optional runnable to run on animation completion. 520 * @param fadeOut Whether or not to fade out the view once the animation completes. If true, 521 * the runnable will execute after the view is faded out. 522 * @param anchorView If not null, this represents the view which the animated view stays 523 * anchored to in case scrolling is currently taking place. Note: currently this is 524 * only used for the X dimension for the case of the workspace. 525 */ 526 public void animateView(final DragView view, final Rect from, final Rect to, 527 final float finalAlpha, final float initScaleX, final float initScaleY, 528 final float finalScaleX, final float finalScaleY, int duration, 529 final Interpolator motionInterpolator, final Interpolator alphaInterpolator, 530 final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) { 531 532 // Calculate the duration of the animation based on the object's distance 533 final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) + 534 Math.pow(to.top - from.top, 2)); 535 final Resources res = getResources(); 536 final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist); 537 538 // If duration < 0, this is a cue to compute the duration based on the distance 539 if (duration < 0) { 540 duration = res.getInteger(R.integer.config_dropAnimMaxDuration); 541 if (dist < maxDist) { 542 duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist); 543 } 544 } 545 546 if (mDropAnim != null) { 547 mDropAnim.cancel(); 548 } 549 550 if (mFadeOutAnim != null) { 551 mFadeOutAnim.cancel(); 552 } 553 554 // Show the drop view if it was previously hidden 555 mDropView = view; 556 mDropView.cancelAnimation(); 557 mDropView.resetLayoutParams(); 558 mDropAnim = new ValueAnimator(); 559 if (alphaInterpolator == null || motionInterpolator == null) { 560 mDropAnim.setInterpolator(mCubicEaseOutInterpolator); 561 } 562 563 if (anchorView != null) { 564 mAnchorViewInitialScrollX = anchorView.getScrollX(); 565 } 566 mAnchorView = anchorView; 567 568 final float initAlpha = view.getAlpha(); 569 final float dropViewScale = mDropView.getScaleX(); 570 571 mDropAnim.setDuration(duration); 572 mDropAnim.setFloatValues(0.0f, 1.0f); 573 mDropAnim.removeAllUpdateListeners(); 574 mDropAnim.addUpdateListener(new AnimatorUpdateListener() { 575 public void onAnimationUpdate(ValueAnimator animation) { 576 final float percent = (Float) animation.getAnimatedValue(); 577 final int width = view.getMeasuredWidth(); 578 final int height = view.getMeasuredHeight(); 579 580 float alphaPercent = alphaInterpolator == null ? percent : 581 alphaInterpolator.getInterpolation(percent); 582 float motionPercent = motionInterpolator == null ? percent : 583 motionInterpolator.getInterpolation(percent); 584 585 float initialScaleX = initScaleX * dropViewScale; 586 float initialScaleY = initScaleY * dropViewScale; 587 float scaleX = finalScaleX * percent + initialScaleX * (1 - percent); 588 float scaleY = finalScaleY * percent + initialScaleY * (1 - percent); 589 float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent); 590 591 float fromLeft = from.left + (initialScaleX - 1f) * width / 2; 592 float fromTop = from.top + (initialScaleY - 1f) * height / 2; 593 594 int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent))); 595 int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent))); 596 597 int xPos = x - mDropView.getScrollX() + (mAnchorView != null 598 ? (mAnchorViewInitialScrollX - mAnchorView.getScrollX()) : 0); 599 int yPos = y - mDropView.getScrollY(); 600 601 mDropView.setTranslationX(xPos); 602 mDropView.setTranslationY(yPos); 603 mDropView.setScaleX(scaleX); 604 mDropView.setScaleY(scaleY); 605 mDropView.setAlpha(alpha); 606 } 607 }); 608 mDropAnim.addListener(new AnimatorListenerAdapter() { 609 public void onAnimationEnd(Animator animation) { 610 if (onCompleteRunnable != null) { 611 onCompleteRunnable.run(); 612 } 613 switch (animationEndStyle) { 614 case ANIMATION_END_DISAPPEAR: 615 clearAnimatedView(); 616 break; 617 case ANIMATION_END_FADE_OUT: 618 fadeOutDragView(); 619 break; 620 case ANIMATION_END_REMAIN_VISIBLE: 621 break; 622 } 623 } 624 }); 625 mDropAnim.start(); 626 } 627 628 public void clearAnimatedView() { 629 if (mDropAnim != null) { 630 mDropAnim.cancel(); 631 } 632 if (mDropView != null) { 633 mDropView.remove(); 634 } 635 mDropView = null; 636 invalidate(); 637 } 638 639 public View getAnimatedView() { 640 return mDropView; 641 } 642 643 private void fadeOutDragView() { 644 mFadeOutAnim = new ValueAnimator(); 645 mFadeOutAnim.setDuration(150); 646 mFadeOutAnim.setFloatValues(0f, 1f); 647 mFadeOutAnim.removeAllUpdateListeners(); 648 mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() { 649 public void onAnimationUpdate(ValueAnimator animation) { 650 final float percent = (Float) animation.getAnimatedValue(); 651 652 float alpha = 1 - percent; 653 mDropView.setAlpha(alpha); 654 } 655 }); 656 mFadeOutAnim.addListener(new AnimatorListenerAdapter() { 657 public void onAnimationEnd(Animator animation) { 658 if (mDropView != null) { 659 mDropView.remove(); 660 } 661 mDropView = null; 662 invalidate(); 663 } 664 }); 665 mFadeOutAnim.start(); 666 } 667 668 @Override 669 protected void onViewAdded(View child) { 670 super.onViewAdded(child); 671 updateChildIndices(); 672 } 673 674 @Override 675 protected void onViewRemoved(View child) { 676 super.onViewRemoved(child); 677 updateChildIndices(); 678 } 679 680 private void updateChildIndices() { 681 if (mLauncher != null) { 682 mWorkspaceIndex = indexOfChild(mLauncher.getWorkspace()); 683 mQsbIndex = indexOfChild(mLauncher.getSearchBar()); 684 } 685 } 686 687 @Override 688 protected int getChildDrawingOrder(int childCount, int i) { 689 // We don't want to prioritize the workspace drawing on top of the other children in 690 // landscape for the overscroll event. 691 if (LauncherApplication.isScreenLandscape(getContext())) { 692 return super.getChildDrawingOrder(childCount, i); 693 } 694 695 if (mWorkspaceIndex == -1 || mQsbIndex == -1 || 696 mLauncher.getWorkspace().isDrawingBackgroundGradient()) { 697 return i; 698 } 699 700 // This ensures that the workspace is drawn above the hotseat and qsb, 701 // except when the workspace is drawing a background gradient, in which 702 // case we want the workspace to stay behind these elements. 703 if (i == mQsbIndex) { 704 return mWorkspaceIndex; 705 } else if (i == mWorkspaceIndex) { 706 return mQsbIndex; 707 } else { 708 return i; 709 } 710 } 711} 712