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 17 package com.android.launcher2; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ValueAnimator; 22 import android.animation.ValueAnimator.AnimatorUpdateListener; 23 import android.content.Context; 24 import android.content.res.Resources; 25 import android.graphics.Canvas; 26 import android.graphics.Color; 27 import android.graphics.PorterDuff; 28 import android.graphics.Rect; 29 import android.graphics.drawable.Drawable; 30 import android.os.Parcelable; 31 import android.util.AttributeSet; 32 import android.view.LayoutInflater; 33 import android.view.MotionEvent; 34 import android.view.View; 35 import android.view.ViewGroup; 36 import android.view.animation.AccelerateInterpolator; 37 import android.view.animation.DecelerateInterpolator; 38 import android.widget.ImageView; 39 import android.widget.LinearLayout; 40 import android.widget.TextView; 41 42 import com.android.launcher.R; 43 import com.android.launcher2.DropTarget.DragObject; 44 import com.android.launcher2.FolderInfo.FolderListener; 45 46 import java.util.ArrayList; 47 48 /** 49 * An icon that can appear on in the workspace representing an {@link UserFolder}. 50 */ 51 public class FolderIcon extends LinearLayout implements FolderListener { 52 private Launcher mLauncher; 53 private Folder mFolder; 54 private FolderInfo mInfo; 55 private static boolean sStaticValuesDirty = true; 56 57 private CheckLongPressHelper mLongPressHelper; 58 59 // The number of icons to display in the 60 private static final int NUM_ITEMS_IN_PREVIEW = 3; 61 private static final int CONSUMPTION_ANIMATION_DURATION = 100; 62 private static final int DROP_IN_ANIMATION_DURATION = 400; 63 private static final int INITIAL_ITEM_ANIMATION_DURATION = 350; 64 private static final int FINAL_ITEM_ANIMATION_DURATION = 200; 65 66 // The degree to which the inner ring grows when accepting drop 67 private static final float INNER_RING_GROWTH_FACTOR = 0.15f; 68 69 // The degree to which the outer ring is scaled in its natural state 70 private static final float OUTER_RING_GROWTH_FACTOR = 0.3f; 71 72 // The amount of vertical spread between items in the stack [0...1] 73 private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f; 74 75 // The degree to which the item in the back of the stack is scaled [0...1] 76 // (0 means it's not scaled at all, 1 means it's scaled to nothing) 77 private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f; 78 79 public static Drawable sSharedFolderLeaveBehind = null; 80 81 private ImageView mPreviewBackground; 82 private BubbleTextView mFolderName; 83 84 FolderRingAnimator mFolderRingAnimator = null; 85 86 // These variables are all associated with the drawing of the preview; they are stored 87 // as member variables for shared usage and to avoid computation on each frame 88 private int mIntrinsicIconSize; 89 private float mBaselineIconScale; 90 private int mBaselineIconSize; 91 private int mAvailableSpaceInPreview; 92 private int mTotalWidth = -1; 93 private int mPreviewOffsetX; 94 private int mPreviewOffsetY; 95 private float mMaxPerspectiveShift; 96 boolean mAnimating = false; 97 98 private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0); 99 private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0); 100 private ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>(); 101 102 public FolderIcon(Context context, AttributeSet attrs) { 103 super(context, attrs); 104 init(); 105 } 106 107 public FolderIcon(Context context) { 108 super(context); 109 init(); 110 } 111 112 private void init() { 113 mLongPressHelper = new CheckLongPressHelper(this); 114 } 115 116 public boolean isDropEnabled() { 117 final ViewGroup cellLayoutChildren = (ViewGroup) getParent(); 118 final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent(); 119 final Workspace workspace = (Workspace) cellLayout.getParent(); 120 return !workspace.isSmall(); 121 } 122 123 static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group, 124 FolderInfo folderInfo, IconCache iconCache) { 125 @SuppressWarnings("all") // suppress dead code warning 126 final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION; 127 if (error) { 128 throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " + 129 "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " + 130 "is dependent on this"); 131 } 132 133 FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false); 134 135 icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name); 136 icon.mFolderName.setText(folderInfo.title); 137 icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background); 138 139 icon.setTag(folderInfo); 140 icon.setOnClickListener(launcher); 141 icon.mInfo = folderInfo; 142 icon.mLauncher = launcher; 143 icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format), 144 folderInfo.title)); 145 Folder folder = Folder.fromXml(launcher); 146 folder.setDragController(launcher.getDragController()); 147 folder.setFolderIcon(icon); 148 folder.bind(folderInfo); 149 icon.mFolder = folder; 150 151 icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon); 152 folderInfo.addListener(icon); 153 154 return icon; 155 } 156 157 @Override 158 protected Parcelable onSaveInstanceState() { 159 sStaticValuesDirty = true; 160 return super.onSaveInstanceState(); 161 } 162 163 public static class FolderRingAnimator { 164 public int mCellX; 165 public int mCellY; 166 private CellLayout mCellLayout; 167 public float mOuterRingSize; 168 public float mInnerRingSize; 169 public FolderIcon mFolderIcon = null; 170 public Drawable mOuterRingDrawable = null; 171 public Drawable mInnerRingDrawable = null; 172 public static Drawable sSharedOuterRingDrawable = null; 173 public static Drawable sSharedInnerRingDrawable = null; 174 public static int sPreviewSize = -1; 175 public static int sPreviewPadding = -1; 176 177 private ValueAnimator mAcceptAnimator; 178 private ValueAnimator mNeutralAnimator; 179 180 public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) { 181 mFolderIcon = folderIcon; 182 Resources res = launcher.getResources(); 183 mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo); 184 mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo); 185 186 // We need to reload the static values when configuration changes in case they are 187 // different in another configuration 188 if (sStaticValuesDirty) { 189 sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size); 190 sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding); 191 sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo); 192 sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo); 193 sSharedFolderLeaveBehind = res.getDrawable(R.drawable.portal_ring_rest); 194 sStaticValuesDirty = false; 195 } 196 } 197 198 public void animateToAcceptState() { 199 if (mNeutralAnimator != null) { 200 mNeutralAnimator.cancel(); 201 } 202 mAcceptAnimator = LauncherAnimUtils.ofFloat(mCellLayout, 0f, 1f); 203 mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION); 204 205 final int previewSize = sPreviewSize; 206 mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() { 207 public void onAnimationUpdate(ValueAnimator animation) { 208 final float percent = (Float) animation.getAnimatedValue(); 209 mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * previewSize; 210 mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * previewSize; 211 if (mCellLayout != null) { 212 mCellLayout.invalidate(); 213 } 214 } 215 }); 216 mAcceptAnimator.addListener(new AnimatorListenerAdapter() { 217 @Override 218 public void onAnimationStart(Animator animation) { 219 if (mFolderIcon != null) { 220 mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE); 221 } 222 } 223 }); 224 mAcceptAnimator.start(); 225 } 226 227 public void animateToNaturalState() { 228 if (mAcceptAnimator != null) { 229 mAcceptAnimator.cancel(); 230 } 231 mNeutralAnimator = LauncherAnimUtils.ofFloat(mCellLayout, 0f, 1f); 232 mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION); 233 234 final int previewSize = sPreviewSize; 235 mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() { 236 public void onAnimationUpdate(ValueAnimator animation) { 237 final float percent = (Float) animation.getAnimatedValue(); 238 mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * previewSize; 239 mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * previewSize; 240 if (mCellLayout != null) { 241 mCellLayout.invalidate(); 242 } 243 } 244 }); 245 mNeutralAnimator.addListener(new AnimatorListenerAdapter() { 246 @Override 247 public void onAnimationEnd(Animator animation) { 248 if (mCellLayout != null) { 249 mCellLayout.hideFolderAccept(FolderRingAnimator.this); 250 } 251 if (mFolderIcon != null) { 252 mFolderIcon.mPreviewBackground.setVisibility(VISIBLE); 253 } 254 } 255 }); 256 mNeutralAnimator.start(); 257 } 258 259 // Location is expressed in window coordinates 260 public void getCell(int[] loc) { 261 loc[0] = mCellX; 262 loc[1] = mCellY; 263 } 264 265 // Location is expressed in window coordinates 266 public void setCell(int x, int y) { 267 mCellX = x; 268 mCellY = y; 269 } 270 271 public void setCellLayout(CellLayout layout) { 272 mCellLayout = layout; 273 } 274 275 public float getOuterRingSize() { 276 return mOuterRingSize; 277 } 278 279 public float getInnerRingSize() { 280 return mInnerRingSize; 281 } 282 } 283 284 Folder getFolder() { 285 return mFolder; 286 } 287 288 FolderInfo getFolderInfo() { 289 return mInfo; 290 } 291 292 private boolean willAcceptItem(ItemInfo item) { 293 final int itemType = item.itemType; 294 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || 295 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) && 296 !mFolder.isFull() && item != mInfo && !mInfo.opened); 297 } 298 299 public boolean acceptDrop(Object dragInfo) { 300 final ItemInfo item = (ItemInfo) dragInfo; 301 return !mFolder.isDestroyed() && willAcceptItem(item); 302 } 303 304 public void addItem(ShortcutInfo item) { 305 mInfo.add(item); 306 } 307 308 public void onDragEnter(Object dragInfo) { 309 if (mFolder.isDestroyed() || !willAcceptItem((ItemInfo) dragInfo)) return; 310 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 311 CellLayout layout = (CellLayout) getParent().getParent(); 312 mFolderRingAnimator.setCell(lp.cellX, lp.cellY); 313 mFolderRingAnimator.setCellLayout(layout); 314 mFolderRingAnimator.animateToAcceptState(); 315 layout.showFolderAccept(mFolderRingAnimator); 316 } 317 318 public void onDragOver(Object dragInfo) { 319 } 320 321 public void performCreateAnimation(final ShortcutInfo destInfo, final View destView, 322 final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect, 323 float scaleRelativeToDragLayer, Runnable postAnimationRunnable) { 324 325 // These correspond two the drawable and view that the icon was dropped _onto_ 326 Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1]; 327 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), 328 destView.getMeasuredWidth()); 329 330 // This will animate the first item from it's position as an icon into its 331 // position as the first item in the preview 332 animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null); 333 addItem(destInfo); 334 335 // This will animate the dragView (srcView) into the new folder 336 onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null); 337 } 338 339 public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) { 340 Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1]; 341 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), 342 finalView.getMeasuredWidth()); 343 344 // This will animate the first item from it's position as an icon into its 345 // position as the first item in the preview 346 animateFirstItem(animateDrawable, FINAL_ITEM_ANIMATION_DURATION, true, 347 onCompleteRunnable); 348 } 349 350 public void onDragExit(Object dragInfo) { 351 onDragExit(); 352 } 353 354 public void onDragExit() { 355 mFolderRingAnimator.animateToNaturalState(); 356 } 357 358 private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect, 359 float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable, 360 DragObject d) { 361 item.cellX = -1; 362 item.cellY = -1; 363 364 // Typically, the animateView corresponds to the DragView; however, if this is being done 365 // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we 366 // will not have a view to animate 367 if (animateView != null) { 368 DragLayer dragLayer = mLauncher.getDragLayer(); 369 Rect from = new Rect(); 370 dragLayer.getViewRectRelativeToSelf(animateView, from); 371 Rect to = finalRect; 372 if (to == null) { 373 to = new Rect(); 374 Workspace workspace = mLauncher.getWorkspace(); 375 // Set cellLayout and this to it's final state to compute final animation locations 376 workspace.setFinalTransitionTransform((CellLayout) getParent().getParent()); 377 float scaleX = getScaleX(); 378 float scaleY = getScaleY(); 379 setScaleX(1.0f); 380 setScaleY(1.0f); 381 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to); 382 // Finished computing final animation locations, restore current state 383 setScaleX(scaleX); 384 setScaleY(scaleY); 385 workspace.resetTransitionTransform((CellLayout) getParent().getParent()); 386 } 387 388 int[] center = new int[2]; 389 float scale = getLocalCenterForIndex(index, center); 390 center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); 391 center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); 392 393 to.offset(center[0] - animateView.getMeasuredWidth() / 2, 394 center[1] - animateView.getMeasuredHeight() / 2); 395 396 float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f; 397 398 float finalScale = scale * scaleRelativeToDragLayer; 399 dragLayer.animateView(animateView, from, to, finalAlpha, 400 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION, 401 new DecelerateInterpolator(2), new AccelerateInterpolator(2), 402 postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); 403 addItem(item); 404 mHiddenItems.add(item); 405 mFolder.hideItem(item); 406 postDelayed(new Runnable() { 407 public void run() { 408 mHiddenItems.remove(item); 409 mFolder.showItem(item); 410 invalidate(); 411 } 412 }, DROP_IN_ANIMATION_DURATION); 413 } else { 414 addItem(item); 415 } 416 } 417 418 public void onDrop(DragObject d) { 419 ShortcutInfo item; 420 if (d.dragInfo instanceof ApplicationInfo) { 421 // Came from all apps -- make a copy 422 item = ((ApplicationInfo) d.dragInfo).makeShortcut(); 423 } else { 424 item = (ShortcutInfo) d.dragInfo; 425 } 426 mFolder.notifyDrop(); 427 onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d); 428 } 429 430 public DropTarget getDropTargetDelegate(DragObject d) { 431 return null; 432 } 433 434 private void computePreviewDrawingParams(int drawableSize, int totalSize) { 435 if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) { 436 mIntrinsicIconSize = drawableSize; 437 mTotalWidth = totalSize; 438 439 final int previewSize = FolderRingAnimator.sPreviewSize; 440 final int previewPadding = FolderRingAnimator.sPreviewPadding; 441 442 mAvailableSpaceInPreview = (previewSize - 2 * previewPadding); 443 // cos(45) = 0.707 + ~= 0.1) = 0.8f 444 int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f)); 445 446 int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR)); 447 mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight); 448 449 mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale); 450 mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR; 451 452 mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2; 453 mPreviewOffsetY = previewPadding; 454 } 455 } 456 457 private void computePreviewDrawingParams(Drawable d) { 458 computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth()); 459 } 460 461 class PreviewItemDrawingParams { 462 PreviewItemDrawingParams(float transX, float transY, float scale, int overlayAlpha) { 463 this.transX = transX; 464 this.transY = transY; 465 this.scale = scale; 466 this.overlayAlpha = overlayAlpha; 467 } 468 float transX; 469 float transY; 470 float scale; 471 int overlayAlpha; 472 Drawable drawable; 473 } 474 475 private float getLocalCenterForIndex(int index, int[] center) { 476 mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams); 477 478 mParams.transX += mPreviewOffsetX; 479 mParams.transY += mPreviewOffsetY; 480 float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2; 481 float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2; 482 483 center[0] = (int) Math.round(offsetX); 484 center[1] = (int) Math.round(offsetY); 485 return mParams.scale; 486 } 487 488 private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, 489 PreviewItemDrawingParams params) { 490 index = NUM_ITEMS_IN_PREVIEW - index - 1; 491 float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1); 492 float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r)); 493 494 float offset = (1 - r) * mMaxPerspectiveShift; 495 float scaledSize = scale * mBaselineIconSize; 496 float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize; 497 498 // We want to imagine our coordinates from the bottom left, growing up and to the 499 // right. This is natural for the x-axis, but for the y-axis, we have to invert things. 500 float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection); 501 float transX = offset + scaleOffsetCorrection; 502 float totalScale = mBaselineIconScale * scale; 503 final int overlayAlpha = (int) (80 * (1 - r)); 504 505 if (params == null) { 506 params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha); 507 } else { 508 params.transX = transX; 509 params.transY = transY; 510 params.scale = totalScale; 511 params.overlayAlpha = overlayAlpha; 512 } 513 return params; 514 } 515 516 private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) { 517 canvas.save(); 518 canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY); 519 canvas.scale(params.scale, params.scale); 520 Drawable d = params.drawable; 521 522 if (d != null) { 523 d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize); 524 d.setFilterBitmap(true); 525 d.setColorFilter(Color.argb(params.overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP); 526 d.draw(canvas); 527 d.clearColorFilter(); 528 d.setFilterBitmap(false); 529 } 530 canvas.restore(); 531 } 532 533 @Override 534 protected void dispatchDraw(Canvas canvas) { 535 super.dispatchDraw(canvas); 536 537 if (mFolder == null) return; 538 if (mFolder.getItemCount() == 0 && !mAnimating) return; 539 540 ArrayList<View> items = mFolder.getItemsInReadingOrder(); 541 Drawable d; 542 TextView v; 543 544 // Update our drawing parameters if necessary 545 if (mAnimating) { 546 computePreviewDrawingParams(mAnimParams.drawable); 547 } else { 548 v = (TextView) items.get(0); 549 d = v.getCompoundDrawables()[1]; 550 computePreviewDrawingParams(d); 551 } 552 553 int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW); 554 if (!mAnimating) { 555 for (int i = nItemsInPreview - 1; i >= 0; i--) { 556 v = (TextView) items.get(i); 557 if (!mHiddenItems.contains(v.getTag())) { 558 d = v.getCompoundDrawables()[1]; 559 mParams = computePreviewItemDrawingParams(i, mParams); 560 mParams.drawable = d; 561 drawPreviewItem(canvas, mParams); 562 } 563 } 564 } else { 565 drawPreviewItem(canvas, mAnimParams); 566 } 567 } 568 569 private void animateFirstItem(final Drawable d, int duration, final boolean reverse, 570 final Runnable onCompleteRunnable) { 571 final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null); 572 573 final float scale0 = 1.0f; 574 final float transX0 = (mAvailableSpaceInPreview - d.getIntrinsicWidth()) / 2; 575 final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2; 576 mAnimParams.drawable = d; 577 578 ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1.0f); 579 va.addUpdateListener(new AnimatorUpdateListener(){ 580 public void onAnimationUpdate(ValueAnimator animation) { 581 float progress = (Float) animation.getAnimatedValue(); 582 if (reverse) { 583 progress = 1 - progress; 584 mPreviewBackground.setAlpha(progress); 585 } 586 587 mAnimParams.transX = transX0 + progress * (finalParams.transX - transX0); 588 mAnimParams.transY = transY0 + progress * (finalParams.transY - transY0); 589 mAnimParams.scale = scale0 + progress * (finalParams.scale - scale0); 590 invalidate(); 591 } 592 }); 593 va.addListener(new AnimatorListenerAdapter() { 594 @Override 595 public void onAnimationStart(Animator animation) { 596 mAnimating = true; 597 } 598 @Override 599 public void onAnimationEnd(Animator animation) { 600 mAnimating = false; 601 if (onCompleteRunnable != null) { 602 onCompleteRunnable.run(); 603 } 604 } 605 }); 606 va.setDuration(duration); 607 va.start(); 608 } 609 610 public void setTextVisible(boolean visible) { 611 if (visible) { 612 mFolderName.setVisibility(VISIBLE); 613 } else { 614 mFolderName.setVisibility(INVISIBLE); 615 } 616 } 617 618 public boolean getTextVisible() { 619 return mFolderName.getVisibility() == VISIBLE; 620 } 621 622 public void onItemsChanged() { 623 invalidate(); 624 requestLayout(); 625 } 626 627 public void onAdd(ShortcutInfo item) { 628 invalidate(); 629 requestLayout(); 630 } 631 632 public void onRemove(ShortcutInfo item) { 633 invalidate(); 634 requestLayout(); 635 } 636 637 public void onTitleChanged(CharSequence title) { 638 mFolderName.setText(title.toString()); 639 setContentDescription(String.format(getContext().getString(R.string.folder_name_format), 640 title)); 641 } 642 643 @Override 644 public boolean onTouchEvent(MotionEvent event) { 645 // Call the superclass onTouchEvent first, because sometimes it changes the state to 646 // isPressed() on an ACTION_UP 647 boolean result = super.onTouchEvent(event); 648 649 switch (event.getAction()) { 650 case MotionEvent.ACTION_DOWN: 651 mLongPressHelper.postCheckForLongPress(); 652 break; 653 case MotionEvent.ACTION_CANCEL: 654 case MotionEvent.ACTION_UP: 655 mLongPressHelper.cancelLongPress(); 656 break; 657 } 658 return result; 659 } 660 661 @Override 662 public void cancelLongPress() { 663 super.cancelLongPress(); 664 665 mLongPressHelper.cancelLongPress(); 666 } 667 } 668