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