DragLayer.java revision eecf02da58adef5486bf0c9ff7127ea891f457a4
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 parentChildren = (CellLayoutChildren) child.getParent(); 437 CellLayout parent = (CellLayout) (CellLayout) parentChildren.getParent(); 438 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 439 parentChildren.measureChild(child); 440 441 Rect r = new Rect(); 442 getViewRectRelativeToSelf(dragView, r); 443 444 int coord[] = new int[2]; 445 coord[0] = lp.x; 446 coord[1] = lp.y; 447 448 // Since the child hasn't necessarily been laid out, we force the lp to be updated with 449 // the correct coordinates (above) and use these to determine the final location 450 float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord); 451 int toX = coord[0]; 452 int toY = coord[1]; 453 if (child instanceof TextView) { 454 float childrenScale = parent.getChildrenScale(); 455 TextView tv = (TextView) child; 456 457 // The child may be scaled (always about the center of the view) so to account for it, 458 // we have to offset the position by the scaled size. Once we do that, we can center 459 // the drag view about the scaled child view. 460 toY += Math.round(((1f - childrenScale) * child.getMeasuredHeight()) / 2 + 461 scale * childrenScale * tv.getPaddingTop()); 462 toY -= dragView.getMeasuredHeight() * (1 - scale * childrenScale) / 2; 463 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; 464 465 scale *= childrenScale; 466 } else if (child instanceof FolderIcon) { 467 // Account for holographic blur padding on the drag view 468 toY -= Workspace.DRAG_BITMAP_PADDING / 2; 469 // Center in the x coordinate about the target's drawable 470 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; 471 } else { 472 toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2; 473 toX -= (Math.round(scale * (dragView.getMeasuredWidth() 474 - child.getMeasuredWidth()))) / 2; 475 } 476 477 final int fromX = r.left; 478 final int fromY = r.top; 479 child.setVisibility(INVISIBLE); 480 Runnable onCompleteRunnable = new Runnable() { 481 public void run() { 482 child.setVisibility(VISIBLE); 483 if (onFinishAnimationRunnable != null) { 484 onFinishAnimationRunnable.run(); 485 } 486 } 487 }; 488 animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, scale, scale, 489 onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView); 490 } 491 492 public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY, 493 final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY, 494 float finalScaleX, float finalScaleY, Runnable onCompleteRunnable, 495 int animationEndStyle, 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, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration, 500 null, null, onCompleteRunnable, animationEndStyle, 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 DragView view, final Rect from, final Rect to, 525 final float finalAlpha, final float initScaleX, final float initScaleY, 526 final float finalScaleX, final float finalScaleY, int duration, 527 final Interpolator motionInterpolator, final Interpolator alphaInterpolator, 528 final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) { 529 530 // Calculate the duration of the animation based on the object's distance 531 final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) + 532 Math.pow(to.top - from.top, 2)); 533 final Resources res = getResources(); 534 final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist); 535 536 // If duration < 0, this is a cue to compute the duration based on the distance 537 if (duration < 0) { 538 duration = res.getInteger(R.integer.config_dropAnimMaxDuration); 539 if (dist < maxDist) { 540 duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist); 541 } 542 duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration)); 543 } 544 545 if (mDropAnim != null) { 546 mDropAnim.cancel(); 547 } 548 549 if (mFadeOutAnim != null) { 550 mFadeOutAnim.cancel(); 551 } 552 553 // Show the drop view if it was previously hidden 554 mDropView = view; 555 mDropView.cancelAnimation(); 556 mDropView.resetLayoutParams(); 557 mDropAnim = new ValueAnimator(); 558 if (alphaInterpolator == null || motionInterpolator == null) { 559 mDropAnim.setInterpolator(mCubicEaseOutInterpolator); 560 } 561 562 if (anchorView != null) { 563 mAnchorViewInitialScrollX = anchorView.getScrollX(); 564 } 565 mAnchorView = anchorView; 566 567 final float initAlpha = view.getAlpha(); 568 final float dropViewScale = mDropView.getScaleX(); 569 570 mDropAnim.setDuration(duration); 571 mDropAnim.setFloatValues(0.0f, 1.0f); 572 mDropAnim.removeAllUpdateListeners(); 573 mDropAnim.addUpdateListener(new AnimatorUpdateListener() { 574 public void onAnimationUpdate(ValueAnimator animation) { 575 final float percent = (Float) animation.getAnimatedValue(); 576 final int width = view.getMeasuredWidth(); 577 final int height = view.getMeasuredHeight(); 578 579 float alphaPercent = alphaInterpolator == null ? percent : 580 alphaInterpolator.getInterpolation(percent); 581 float motionPercent = motionInterpolator == null ? percent : 582 motionInterpolator.getInterpolation(percent); 583 584 float initialScaleX = initScaleX * dropViewScale; 585 float initialScaleY = initScaleY * dropViewScale; 586 float scaleX = finalScaleX * percent + initialScaleX * (1 - percent); 587 float scaleY = finalScaleY * percent + initialScaleY * (1 - percent); 588 float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent); 589 590 float fromLeft = from.left + (initialScaleX - 1f) * width / 2; 591 float fromTop = from.top + (initialScaleY - 1f) * height / 2; 592 593 int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent))); 594 int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent))); 595 596 int xPos = x - mDropView.getScrollX() + (mAnchorView != null 597 ? (mAnchorViewInitialScrollX - mAnchorView.getScrollX()) : 0); 598 int yPos = y - mDropView.getScrollY(); 599 600 mDropView.setTranslationX(xPos); 601 mDropView.setTranslationY(yPos); 602 mDropView.setScaleX(scaleX); 603 mDropView.setScaleY(scaleY); 604 mDropView.setAlpha(alpha); 605 } 606 }); 607 mDropAnim.addListener(new AnimatorListenerAdapter() { 608 public void onAnimationEnd(Animator animation) { 609 if (onCompleteRunnable != null) { 610 onCompleteRunnable.run(); 611 } 612 switch (animationEndStyle) { 613 case ANIMATION_END_DISAPPEAR: 614 clearAnimatedView(); 615 break; 616 case ANIMATION_END_FADE_OUT: 617 fadeOutDragView(); 618 break; 619 case ANIMATION_END_REMAIN_VISIBLE: 620 break; 621 } 622 } 623 }); 624 mDropAnim.start(); 625 } 626 627 public void clearAnimatedView() { 628 if (mDropAnim != null) { 629 mDropAnim.cancel(); 630 } 631 if (mDropView != null) { 632 mDropView.remove(); 633 } 634 mDropView = null; 635 invalidate(); 636 } 637 638 public View getAnimatedView() { 639 return mDropView; 640 } 641 642 private void fadeOutDragView() { 643 mFadeOutAnim = new ValueAnimator(); 644 mFadeOutAnim.setDuration(150); 645 mFadeOutAnim.setFloatValues(0f, 1f); 646 mFadeOutAnim.removeAllUpdateListeners(); 647 mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() { 648 public void onAnimationUpdate(ValueAnimator animation) { 649 final float percent = (Float) animation.getAnimatedValue(); 650 651 float alpha = 1 - percent; 652 mDropView.setAlpha(alpha); 653 } 654 }); 655 mFadeOutAnim.addListener(new AnimatorListenerAdapter() { 656 public void onAnimationEnd(Animator animation) { 657 if (mDropView != null) { 658 mDropView.remove(); 659 } 660 mDropView = null; 661 invalidate(); 662 } 663 }); 664 mFadeOutAnim.start(); 665 } 666 667 @Override 668 protected void onViewAdded(View child) { 669 super.onViewAdded(child); 670 updateChildIndices(); 671 } 672 673 @Override 674 protected void onViewRemoved(View child) { 675 super.onViewRemoved(child); 676 updateChildIndices(); 677 } 678 679 private void updateChildIndices() { 680 if (mLauncher != null) { 681 mWorkspaceIndex = indexOfChild(mLauncher.getWorkspace()); 682 mQsbIndex = indexOfChild(mLauncher.getSearchBar()); 683 } 684 } 685 686 @Override 687 protected int getChildDrawingOrder(int childCount, int i) { 688 // We don't want to prioritize the workspace drawing on top of the other children in 689 // landscape for the overscroll event. 690 if (LauncherApplication.isScreenLandscape(getContext())) { 691 return super.getChildDrawingOrder(childCount, i); 692 } 693 694 if (mWorkspaceIndex == -1 || mQsbIndex == -1 || 695 mLauncher.getWorkspace().isDrawingBackgroundGradient()) { 696 return i; 697 } 698 699 // This ensures that the workspace is drawn above the hotseat and qsb, 700 // except when the workspace is drawing a background gradient, in which 701 // case we want the workspace to stay behind these elements. 702 if (i == mQsbIndex) { 703 return mWorkspaceIndex; 704 } else if (i == mWorkspaceIndex) { 705 return mQsbIndex; 706 } else { 707 return i; 708 } 709 } 710} 711