Home | History | Annotate | Download | only in folder
      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.launcher3.folder;
     18 
     19 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
     20 import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
     21 
     22 import android.animation.Animator;
     23 import android.animation.ObjectAnimator;
     24 import android.content.Context;
     25 import android.graphics.Canvas;
     26 import android.graphics.Point;
     27 import android.graphics.Rect;
     28 import android.graphics.Region;
     29 import android.graphics.drawable.Drawable;
     30 import android.os.Parcelable;
     31 import android.support.annotation.NonNull;
     32 import android.util.AttributeSet;
     33 import android.util.Property;
     34 import android.view.LayoutInflater;
     35 import android.view.MotionEvent;
     36 import android.view.View;
     37 import android.view.ViewConfiguration;
     38 import android.view.ViewGroup;
     39 import android.widget.FrameLayout;
     40 
     41 import com.android.launcher3.Alarm;
     42 import com.android.launcher3.AppInfo;
     43 import com.android.launcher3.BubbleTextView;
     44 import com.android.launcher3.CellLayout;
     45 import com.android.launcher3.CheckLongPressHelper;
     46 import com.android.launcher3.DeviceProfile;
     47 import com.android.launcher3.DropTarget.DragObject;
     48 import com.android.launcher3.FolderInfo;
     49 import com.android.launcher3.FolderInfo.FolderListener;
     50 import com.android.launcher3.ItemInfo;
     51 import com.android.launcher3.Launcher;
     52 import com.android.launcher3.LauncherSettings;
     53 import com.android.launcher3.OnAlarmListener;
     54 import com.android.launcher3.R;
     55 import com.android.launcher3.ShortcutInfo;
     56 import com.android.launcher3.SimpleOnStylusPressListener;
     57 import com.android.launcher3.StylusEventHelper;
     58 import com.android.launcher3.Utilities;
     59 import com.android.launcher3.Workspace;
     60 import com.android.launcher3.anim.Interpolators;
     61 import com.android.launcher3.badge.BadgeRenderer;
     62 import com.android.launcher3.badge.FolderBadgeInfo;
     63 import com.android.launcher3.dragndrop.BaseItemDragListener;
     64 import com.android.launcher3.dragndrop.DragLayer;
     65 import com.android.launcher3.dragndrop.DragView;
     66 import com.android.launcher3.touch.ItemClickHandler;
     67 import com.android.launcher3.util.Thunk;
     68 import com.android.launcher3.widget.PendingAddShortcutInfo;
     69 
     70 import java.util.ArrayList;
     71 import java.util.List;
     72 
     73 /**
     74  * An icon that can appear on in the workspace representing an {@link Folder}.
     75  */
     76 public class FolderIcon extends FrameLayout implements FolderListener {
     77     @Thunk Launcher mLauncher;
     78     @Thunk Folder mFolder;
     79     private FolderInfo mInfo;
     80     @Thunk static boolean sStaticValuesDirty = true;
     81 
     82     private CheckLongPressHelper mLongPressHelper;
     83     private StylusEventHelper mStylusEventHelper;
     84 
     85     static final int DROP_IN_ANIMATION_DURATION = 400;
     86 
     87     // Flag whether the folder should open itself when an item is dragged over is enabled.
     88     public static final boolean SPRING_LOADING_ENABLED = true;
     89 
     90     // Delay when drag enters until the folder opens, in miliseconds.
     91     private static final int ON_OPEN_DELAY = 800;
     92 
     93     @Thunk BubbleTextView mFolderName;
     94 
     95     PreviewBackground mBackground = new PreviewBackground();
     96     private boolean mBackgroundIsVisible = true;
     97 
     98     FolderIconPreviewVerifier mPreviewVerifier;
     99     ClippedFolderIconLayoutRule mPreviewLayoutRule;
    100     private PreviewItemManager mPreviewItemManager;
    101     private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
    102     private List<BubbleTextView> mCurrentPreviewItems = new ArrayList<>();
    103 
    104     boolean mAnimating = false;
    105     private Rect mTempBounds = new Rect();
    106 
    107     private float mSlop;
    108 
    109     private Alarm mOpenAlarm = new Alarm();
    110 
    111     private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
    112     private BadgeRenderer mBadgeRenderer;
    113     private float mBadgeScale;
    114     private Point mTempSpaceForBadgeOffset = new Point();
    115 
    116     private static final Property<FolderIcon, Float> BADGE_SCALE_PROPERTY
    117             = new Property<FolderIcon, Float>(Float.TYPE, "badgeScale") {
    118         @Override
    119         public Float get(FolderIcon folderIcon) {
    120             return folderIcon.mBadgeScale;
    121         }
    122 
    123         @Override
    124         public void set(FolderIcon folderIcon, Float value) {
    125             folderIcon.mBadgeScale = value;
    126             folderIcon.invalidate();
    127         }
    128     };
    129 
    130     public FolderIcon(Context context, AttributeSet attrs) {
    131         super(context, attrs);
    132         init();
    133     }
    134 
    135     public FolderIcon(Context context) {
    136         super(context);
    137         init();
    138     }
    139 
    140     private void init() {
    141         mLongPressHelper = new CheckLongPressHelper(this);
    142         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
    143         mPreviewLayoutRule = new ClippedFolderIconLayoutRule();
    144         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    145         mPreviewItemManager = new PreviewItemManager(this);
    146     }
    147 
    148     public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
    149             FolderInfo folderInfo) {
    150         @SuppressWarnings("all") // suppress dead code warning
    151         final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION;
    152         if (error) {
    153             throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +
    154                     "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
    155                     "is dependent on this");
    156         }
    157 
    158         DeviceProfile grid = launcher.getDeviceProfile();
    159         FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
    160                 .inflate(resId, group, false);
    161 
    162         icon.setClipToPadding(false);
    163         icon.mFolderName = icon.findViewById(R.id.folder_icon_name);
    164         icon.mFolderName.setText(folderInfo.title);
    165         icon.mFolderName.setCompoundDrawablePadding(0);
    166         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
    167         lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
    168 
    169         icon.setTag(folderInfo);
    170         icon.setOnClickListener(ItemClickHandler.INSTANCE);
    171         icon.mInfo = folderInfo;
    172         icon.mLauncher = launcher;
    173         icon.mBadgeRenderer = launcher.getDeviceProfile().mBadgeRenderer;
    174         icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title));
    175         Folder folder = Folder.fromXml(launcher);
    176         folder.setDragController(launcher.getDragController());
    177         folder.setFolderIcon(icon);
    178         folder.bind(folderInfo);
    179         icon.setFolder(folder);
    180         icon.setAccessibilityDelegate(launcher.getAccessibilityDelegate());
    181 
    182         folderInfo.addListener(icon);
    183 
    184         icon.setOnFocusChangeListener(launcher.mFocusHandler);
    185         return icon;
    186     }
    187 
    188     @Override
    189     protected Parcelable onSaveInstanceState() {
    190         sStaticValuesDirty = true;
    191         return super.onSaveInstanceState();
    192     }
    193 
    194     public Folder getFolder() {
    195         return mFolder;
    196     }
    197 
    198     private void setFolder(Folder folder) {
    199         mFolder = folder;
    200         mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
    201         updatePreviewItems(false);
    202     }
    203 
    204     private boolean willAcceptItem(ItemInfo item) {
    205         final int itemType = item.itemType;
    206         return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
    207                 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
    208                 itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
    209                 item != mInfo && !mFolder.isOpen());
    210     }
    211 
    212     public boolean acceptDrop(ItemInfo dragInfo) {
    213         return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
    214     }
    215 
    216     public void addItem(ShortcutInfo item) {
    217         addItem(item, true);
    218     }
    219 
    220     public void addItem(ShortcutInfo item, boolean animate) {
    221         mInfo.add(item, animate);
    222     }
    223 
    224     public void removeItem(ShortcutInfo item, boolean animate) {
    225         mInfo.remove(item, animate);
    226     }
    227 
    228     public void onDragEnter(ItemInfo dragInfo) {
    229         if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return;
    230         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
    231         CellLayout cl = (CellLayout) getParent().getParent();
    232 
    233         mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
    234         mOpenAlarm.setOnAlarmListener(mOnOpenListener);
    235         if (SPRING_LOADING_ENABLED &&
    236                 ((dragInfo instanceof AppInfo)
    237                         || (dragInfo instanceof ShortcutInfo)
    238                         || (dragInfo instanceof PendingAddShortcutInfo))) {
    239             mOpenAlarm.setAlarm(ON_OPEN_DELAY);
    240         }
    241     }
    242 
    243     OnAlarmListener mOnOpenListener = new OnAlarmListener() {
    244         public void onAlarm(Alarm alarm) {
    245             mFolder.beginExternalDrag();
    246             mFolder.animateOpen();
    247         }
    248     };
    249 
    250     public Drawable prepareCreateAnimation(final View destView) {
    251         return mPreviewItemManager.prepareCreateAnimation(destView);
    252     }
    253 
    254     public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
    255             final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
    256             float scaleRelativeToDragLayer) {
    257         prepareCreateAnimation(destView);
    258         addItem(destInfo);
    259         // This will animate the first item from it's position as an icon into its
    260         // position as the first item in the preview
    261         mPreviewItemManager.createFirstItemAnimation(false /* reverse */, null)
    262                 .start();
    263 
    264         // This will animate the dragView (srcView) into the new folder
    265         onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1,
    266                 false /* itemReturnedOnFailedDrop */);
    267     }
    268 
    269     public void performDestroyAnimation(Runnable onCompleteRunnable) {
    270         // This will animate the final item in the preview to be full size.
    271         mPreviewItemManager.createFirstItemAnimation(true /* reverse */, onCompleteRunnable)
    272                 .start();
    273     }
    274 
    275     public void onDragExit() {
    276         mBackground.animateToRest();
    277         mOpenAlarm.cancelAlarm();
    278     }
    279 
    280     private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
    281             float scaleRelativeToDragLayer, int index,
    282             boolean itemReturnedOnFailedDrop) {
    283         item.cellX = -1;
    284         item.cellY = -1;
    285 
    286         // Typically, the animateView corresponds to the DragView; however, if this is being done
    287         // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we
    288         // will not have a view to animate
    289         if (animateView != null) {
    290             DragLayer dragLayer = mLauncher.getDragLayer();
    291             Rect from = new Rect();
    292             dragLayer.getViewRectRelativeToSelf(animateView, from);
    293             Rect to = finalRect;
    294             if (to == null) {
    295                 to = new Rect();
    296                 Workspace workspace = mLauncher.getWorkspace();
    297                 // Set cellLayout and this to it's final state to compute final animation locations
    298                 workspace.setFinalTransitionTransform();
    299                 float scaleX = getScaleX();
    300                 float scaleY = getScaleY();
    301                 setScaleX(1.0f);
    302                 setScaleY(1.0f);
    303                 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
    304                 // Finished computing final animation locations, restore current state
    305                 setScaleX(scaleX);
    306                 setScaleY(scaleY);
    307                 workspace.resetTransitionTransform();
    308             }
    309 
    310             int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1);
    311             boolean itemAdded = false;
    312             if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
    313                 List<BubbleTextView> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
    314                 addItem(item, false);
    315                 mCurrentPreviewItems.clear();
    316                 mCurrentPreviewItems.addAll(getPreviewItems());
    317 
    318                 if (!oldPreviewItems.equals(mCurrentPreviewItems)) {
    319                     for (int i = 0; i < mCurrentPreviewItems.size(); ++i) {
    320                         if (mCurrentPreviewItems.get(i).getTag().equals(item)) {
    321                             // If the item dropped is going to be in the preview, we update the
    322                             // index here to reflect its position in the preview.
    323                             index = i;
    324                         }
    325                     }
    326 
    327                     mPreviewItemManager.hidePreviewItem(index, true);
    328                     mPreviewItemManager.onDrop(oldPreviewItems, mCurrentPreviewItems, item);
    329                     itemAdded = true;
    330                 } else {
    331                     removeItem(item, false);
    332                 }
    333             }
    334 
    335             if (!itemAdded) {
    336                 addItem(item);
    337             }
    338 
    339             int[] center = new int[2];
    340             float scale = getLocalCenterForIndex(index, numItemsInPreview, center);
    341             center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
    342             center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
    343 
    344             to.offset(center[0] - animateView.getMeasuredWidth() / 2,
    345                     center[1] - animateView.getMeasuredHeight() / 2);
    346 
    347             float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
    348 
    349             float finalScale = scale * scaleRelativeToDragLayer;
    350             dragLayer.animateView(animateView, from, to, finalAlpha,
    351                     1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
    352                     Interpolators.DEACCEL_2, Interpolators.ACCEL_2,
    353                     null, DragLayer.ANIMATION_END_DISAPPEAR, null);
    354 
    355             mFolder.hideItem(item);
    356 
    357             if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
    358             final int finalIndex = index;
    359             postDelayed(new Runnable() {
    360                 public void run() {
    361                     mPreviewItemManager.hidePreviewItem(finalIndex, false);
    362                     mFolder.showItem(item);
    363                     invalidate();
    364                 }
    365             }, DROP_IN_ANIMATION_DURATION);
    366         } else {
    367             addItem(item);
    368         }
    369     }
    370 
    371     public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) {
    372         ShortcutInfo item;
    373         if (d.dragInfo instanceof AppInfo) {
    374             // Came from all apps -- make a copy
    375             item = ((AppInfo) d.dragInfo).makeShortcut();
    376         } else if (d.dragSource instanceof BaseItemDragListener){
    377             // Came from a different window -- make a copy
    378             item = new ShortcutInfo((ShortcutInfo) d.dragInfo);
    379         } else {
    380             item = (ShortcutInfo) d.dragInfo;
    381         }
    382         mFolder.notifyDrop();
    383         onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(),
    384                 itemReturnedOnFailedDrop);
    385     }
    386 
    387     public void setBadgeInfo(FolderBadgeInfo badgeInfo) {
    388         updateBadgeScale(mBadgeInfo.hasBadge(), badgeInfo.hasBadge());
    389         mBadgeInfo = badgeInfo;
    390     }
    391 
    392     public ClippedFolderIconLayoutRule getLayoutRule() {
    393         return mPreviewLayoutRule;
    394     }
    395 
    396     /**
    397      * Sets mBadgeScale to 1 or 0, animating if wasBadged or isBadged is false
    398      * (the badge is being added or removed).
    399      */
    400     private void updateBadgeScale(boolean wasBadged, boolean isBadged) {
    401         float newBadgeScale = isBadged ? 1f : 0f;
    402         // Animate when a badge is first added or when it is removed.
    403         if ((wasBadged ^ isBadged) && isShown()) {
    404             createBadgeScaleAnimator(newBadgeScale).start();
    405         } else {
    406             mBadgeScale = newBadgeScale;
    407             invalidate();
    408         }
    409     }
    410 
    411     public Animator createBadgeScaleAnimator(float... badgeScales) {
    412         return ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, badgeScales);
    413     }
    414 
    415     public boolean hasBadge() {
    416         return mBadgeInfo != null && mBadgeInfo.hasBadge();
    417     }
    418 
    419     private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
    420         mTmpParams = mPreviewItemManager.computePreviewItemDrawingParams(
    421                 Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index), curNumItems, mTmpParams);
    422 
    423         mTmpParams.transX += mBackground.basePreviewOffsetX;
    424         mTmpParams.transY += mBackground.basePreviewOffsetY;
    425 
    426         float intrinsicIconSize = mPreviewItemManager.getIntrinsicIconSize();
    427         float offsetX = mTmpParams.transX + (mTmpParams.scale * intrinsicIconSize) / 2;
    428         float offsetY = mTmpParams.transY + (mTmpParams.scale * intrinsicIconSize) / 2;
    429 
    430         center[0] = Math.round(offsetX);
    431         center[1] = Math.round(offsetY);
    432         return mTmpParams.scale;
    433     }
    434 
    435     public void setFolderBackground(PreviewBackground bg) {
    436         mBackground = bg;
    437         mBackground.setInvalidateDelegate(this);
    438     }
    439 
    440     public void setBackgroundVisible(boolean visible) {
    441         mBackgroundIsVisible = visible;
    442         invalidate();
    443     }
    444 
    445     public PreviewBackground getFolderBackground() {
    446         return mBackground;
    447     }
    448 
    449     public PreviewItemManager getPreviewItemManager() {
    450         return mPreviewItemManager;
    451     }
    452 
    453     @Override
    454     protected void dispatchDraw(Canvas canvas) {
    455         super.dispatchDraw(canvas);
    456 
    457         if (!mBackgroundIsVisible) return;
    458 
    459         mPreviewItemManager.recomputePreviewDrawingParams();
    460 
    461         if (!mBackground.drawingDelegated()) {
    462             mBackground.drawBackground(canvas);
    463         }
    464 
    465         if (mFolder == null) return;
    466         if (mFolder.getItemCount() == 0 && !mAnimating) return;
    467 
    468         final int saveCount;
    469 
    470         if (canvas.isHardwareAccelerated()) {
    471             saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
    472         } else {
    473             saveCount = canvas.save();
    474             canvas.clipPath(mBackground.getClipPath());
    475         }
    476 
    477         mPreviewItemManager.draw(canvas);
    478 
    479         if (canvas.isHardwareAccelerated()) {
    480             mBackground.clipCanvasHardware(canvas);
    481         }
    482         canvas.restoreToCount(saveCount);
    483 
    484         if (!mBackground.drawingDelegated()) {
    485             mBackground.drawBackgroundStroke(canvas);
    486         }
    487 
    488         drawBadge(canvas);
    489     }
    490 
    491     public void drawBadge(Canvas canvas) {
    492         if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) {
    493             int offsetX = mBackground.getOffsetX();
    494             int offsetY = mBackground.getOffsetY();
    495             int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
    496             mTempBounds.set(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
    497 
    498             // If we are animating to the accepting state, animate the badge out.
    499             float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
    500             mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top);
    501             mBadgeRenderer.draw(canvas, mBackground.getBadgeColor(), mTempBounds,
    502                     badgeScale, mTempSpaceForBadgeOffset);
    503         }
    504     }
    505 
    506     public void setTextVisible(boolean visible) {
    507         if (visible) {
    508             mFolderName.setVisibility(VISIBLE);
    509         } else {
    510             mFolderName.setVisibility(INVISIBLE);
    511         }
    512     }
    513 
    514     public boolean getTextVisible() {
    515         return mFolderName.getVisibility() == VISIBLE;
    516     }
    517 
    518     /**
    519      * Returns the list of preview items displayed in the icon.
    520      */
    521     public List<BubbleTextView> getPreviewItems() {
    522         return getPreviewItemsOnPage(0);
    523     }
    524 
    525     /**
    526      * Returns the list of "preview items" on {@param page}.
    527      */
    528     public List<BubbleTextView> getPreviewItemsOnPage(int page) {
    529         mPreviewVerifier.setFolderInfo(mFolder.getInfo());
    530 
    531         List<BubbleTextView> itemsToDisplay = new ArrayList<>();
    532         List<BubbleTextView> itemsOnPage = mFolder.getItemsOnPage(page);
    533         int numItems = itemsOnPage.size();
    534         for (int rank = 0; rank < numItems; ++rank) {
    535             if (mPreviewVerifier.isItemInPreview(page, rank)) {
    536                 itemsToDisplay.add(itemsOnPage.get(rank));
    537             }
    538 
    539             if (itemsToDisplay.size() == MAX_NUM_ITEMS_IN_PREVIEW) {
    540                 break;
    541             }
    542         }
    543         return itemsToDisplay;
    544     }
    545 
    546     @Override
    547     protected boolean verifyDrawable(@NonNull Drawable who) {
    548         return mPreviewItemManager.verifyDrawable(who) || super.verifyDrawable(who);
    549     }
    550 
    551     @Override
    552     public void onItemsChanged(boolean animate) {
    553         updatePreviewItems(animate);
    554         invalidate();
    555         requestLayout();
    556     }
    557 
    558     private void updatePreviewItems(boolean animate) {
    559         mPreviewItemManager.updatePreviewItems(animate);
    560         mCurrentPreviewItems.clear();
    561         mCurrentPreviewItems.addAll(getPreviewItems());
    562     }
    563 
    564     @Override
    565     public void prepareAutoUpdate() {
    566     }
    567 
    568     @Override
    569     public void onAdd(ShortcutInfo item, int rank) {
    570         boolean wasBadged = mBadgeInfo.hasBadge();
    571         mBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(item));
    572         boolean isBadged = mBadgeInfo.hasBadge();
    573         updateBadgeScale(wasBadged, isBadged);
    574         invalidate();
    575         requestLayout();
    576     }
    577 
    578     @Override
    579     public void onRemove(ShortcutInfo item) {
    580         boolean wasBadged = mBadgeInfo.hasBadge();
    581         mBadgeInfo.subtractBadgeInfo(mLauncher.getBadgeInfoForItem(item));
    582         boolean isBadged = mBadgeInfo.hasBadge();
    583         updateBadgeScale(wasBadged, isBadged);
    584         invalidate();
    585         requestLayout();
    586     }
    587 
    588     @Override
    589     public void onTitleChanged(CharSequence title) {
    590         mFolderName.setText(title);
    591         setContentDescription(getContext().getString(R.string.folder_name_format, title));
    592     }
    593 
    594     @Override
    595     public boolean onTouchEvent(MotionEvent event) {
    596         // Call the superclass onTouchEvent first, because sometimes it changes the state to
    597         // isPressed() on an ACTION_UP
    598         boolean result = super.onTouchEvent(event);
    599 
    600         // Check for a stylus button press, if it occurs cancel any long press checks.
    601         if (mStylusEventHelper.onMotionEvent(event)) {
    602             mLongPressHelper.cancelLongPress();
    603             return true;
    604         }
    605 
    606         switch (event.getAction()) {
    607             case MotionEvent.ACTION_DOWN:
    608                 mLongPressHelper.postCheckForLongPress();
    609                 break;
    610             case MotionEvent.ACTION_CANCEL:
    611             case MotionEvent.ACTION_UP:
    612                 mLongPressHelper.cancelLongPress();
    613                 break;
    614             case MotionEvent.ACTION_MOVE:
    615                 if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
    616                     mLongPressHelper.cancelLongPress();
    617                 }
    618                 break;
    619         }
    620         return result;
    621     }
    622 
    623     @Override
    624     public void cancelLongPress() {
    625         super.cancelLongPress();
    626         mLongPressHelper.cancelLongPress();
    627     }
    628 
    629     public void removeListeners() {
    630         mInfo.removeListener(this);
    631         mInfo.removeListener(mFolder);
    632     }
    633 
    634     public void clearLeaveBehindIfExists() {
    635         ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
    636         if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
    637             CellLayout cl = (CellLayout) getParent().getParent();
    638             cl.clearFolderLeaveBehind();
    639         }
    640     }
    641 
    642     public void drawLeaveBehindIfExists() {
    643         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
    644         // While the folder is open, the position of the icon cannot change.
    645         lp.canReorder = false;
    646         if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
    647             CellLayout cl = (CellLayout) getParent().getParent();
    648             cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
    649         }
    650     }
    651 
    652     public void onFolderClose(int currentPage) {
    653         mPreviewItemManager.onFolderClose(currentPage);
    654     }
    655 }
    656