• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FolderIcon(Context context, AttributeSet attrs)130     public FolderIcon(Context context, AttributeSet attrs) {
131         super(context, attrs);
132         init();
133     }
134 
FolderIcon(Context context)135     public FolderIcon(Context context) {
136         super(context);
137         init();
138     }
139 
init()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 
fromXml(int resId, Launcher launcher, ViewGroup group, FolderInfo folderInfo)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
onSaveInstanceState()189     protected Parcelable onSaveInstanceState() {
190         sStaticValuesDirty = true;
191         return super.onSaveInstanceState();
192     }
193 
getFolder()194     public Folder getFolder() {
195         return mFolder;
196     }
197 
setFolder(Folder folder)198     private void setFolder(Folder folder) {
199         mFolder = folder;
200         mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
201         updatePreviewItems(false);
202     }
203 
willAcceptItem(ItemInfo item)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 
acceptDrop(ItemInfo dragInfo)212     public boolean acceptDrop(ItemInfo dragInfo) {
213         return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
214     }
215 
addItem(ShortcutInfo item)216     public void addItem(ShortcutInfo item) {
217         addItem(item, true);
218     }
219 
addItem(ShortcutInfo item, boolean animate)220     public void addItem(ShortcutInfo item, boolean animate) {
221         mInfo.add(item, animate);
222     }
223 
removeItem(ShortcutInfo item, boolean animate)224     public void removeItem(ShortcutInfo item, boolean animate) {
225         mInfo.remove(item, animate);
226     }
227 
onDragEnter(ItemInfo dragInfo)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 
prepareCreateAnimation(final View destView)250     public Drawable prepareCreateAnimation(final View destView) {
251         return mPreviewItemManager.prepareCreateAnimation(destView);
252     }
253 
performCreateAnimation(final ShortcutInfo destInfo, final View destView, final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect, float scaleRelativeToDragLayer)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 
performDestroyAnimation(Runnable onCompleteRunnable)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 
onDragExit()275     public void onDragExit() {
276         mBackground.animateToRest();
277         mOpenAlarm.cancelAlarm();
278     }
279 
onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect, float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop)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