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