• 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;
18 
19 import android.animation.ObjectAnimator;
20 import android.content.Context;
21 import android.content.res.ColorStateList;
22 import android.content.res.TypedArray;
23 import android.graphics.Canvas;
24 import android.graphics.Color;
25 import android.graphics.Paint;
26 import android.graphics.Point;
27 import android.graphics.Rect;
28 import android.graphics.drawable.ColorDrawable;
29 import android.graphics.drawable.Drawable;
30 import android.support.v4.graphics.ColorUtils;
31 import android.text.TextUtils.TruncateAt;
32 import android.util.AttributeSet;
33 import android.util.Property;
34 import android.util.TypedValue;
35 import android.view.KeyEvent;
36 import android.view.MotionEvent;
37 import android.view.View;
38 import android.view.ViewConfiguration;
39 import android.view.ViewDebug;
40 import android.widget.TextView;
41 
42 import com.android.launcher3.IconCache.IconLoadRequest;
43 import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
44 import com.android.launcher3.Launcher.OnResumeCallback;
45 import com.android.launcher3.badge.BadgeInfo;
46 import com.android.launcher3.badge.BadgeRenderer;
47 import com.android.launcher3.folder.FolderIcon;
48 import com.android.launcher3.graphics.DrawableFactory;
49 import com.android.launcher3.graphics.IconPalette;
50 import com.android.launcher3.graphics.PreloadIconDrawable;
51 import com.android.launcher3.model.PackageItemInfo;
52 
53 import java.text.NumberFormat;
54 
55 /**
56  * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
57  * because we want to make the bubble taller than the text and TextView's clip is
58  * too aggressive.
59  */
60 public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback {
61 
62     private static final int DISPLAY_WORKSPACE = 0;
63     private static final int DISPLAY_ALL_APPS = 1;
64     private static final int DISPLAY_FOLDER = 2;
65 
66     private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
67 
68 
69     private static final Property<BubbleTextView, Float> BADGE_SCALE_PROPERTY
70             = new Property<BubbleTextView, Float>(Float.TYPE, "badgeScale") {
71         @Override
72         public Float get(BubbleTextView bubbleTextView) {
73             return bubbleTextView.mBadgeScale;
74         }
75 
76         @Override
77         public void set(BubbleTextView bubbleTextView, Float value) {
78             bubbleTextView.mBadgeScale = value;
79             bubbleTextView.invalidate();
80         }
81     };
82 
83     public static final Property<BubbleTextView, Float> TEXT_ALPHA_PROPERTY
84             = new Property<BubbleTextView, Float>(Float.class, "textAlpha") {
85         @Override
86         public Float get(BubbleTextView bubbleTextView) {
87             return bubbleTextView.mTextAlpha;
88         }
89 
90         @Override
91         public void set(BubbleTextView bubbleTextView, Float alpha) {
92             bubbleTextView.setTextAlpha(alpha);
93         }
94     };
95 
96     private final BaseDraggingActivity mActivity;
97     private Drawable mIcon;
98     private final boolean mCenterVertically;
99 
100     private final CheckLongPressHelper mLongPressHelper;
101     private final StylusEventHelper mStylusEventHelper;
102     private final float mSlop;
103 
104     private final boolean mLayoutHorizontal;
105     private final int mIconSize;
106 
107     @ViewDebug.ExportedProperty(category = "launcher")
108     private boolean mIsIconVisible = true;
109     @ViewDebug.ExportedProperty(category = "launcher")
110     private int mTextColor;
111     @ViewDebug.ExportedProperty(category = "launcher")
112     private float mTextAlpha = 1;
113 
114     private BadgeInfo mBadgeInfo;
115     private BadgeRenderer mBadgeRenderer;
116     private int mBadgeColor;
117     private float mBadgeScale;
118     private boolean mForceHideBadge;
119     private Point mTempSpaceForBadgeOffset = new Point();
120     private Rect mTempIconBounds = new Rect();
121 
122     @ViewDebug.ExportedProperty(category = "launcher")
123     private boolean mStayPressed;
124     @ViewDebug.ExportedProperty(category = "launcher")
125     private boolean mIgnorePressedStateChange;
126     @ViewDebug.ExportedProperty(category = "launcher")
127     private boolean mDisableRelayout = false;
128 
129     private IconLoadRequest mIconLoadRequest;
130 
BubbleTextView(Context context)131     public BubbleTextView(Context context) {
132         this(context, null, 0);
133     }
134 
BubbleTextView(Context context, AttributeSet attrs)135     public BubbleTextView(Context context, AttributeSet attrs) {
136         this(context, attrs, 0);
137     }
138 
BubbleTextView(Context context, AttributeSet attrs, int defStyle)139     public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
140         super(context, attrs, defStyle);
141         mActivity = BaseDraggingActivity.fromContext(context);
142         DeviceProfile grid = mActivity.getDeviceProfile();
143         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
144 
145         TypedArray a = context.obtainStyledAttributes(attrs,
146                 R.styleable.BubbleTextView, defStyle, 0);
147         mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
148 
149         int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
150         int defaultIconSize = grid.iconSizePx;
151         if (display == DISPLAY_WORKSPACE) {
152             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
153             setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
154         } else if (display == DISPLAY_ALL_APPS) {
155             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
156             setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
157             defaultIconSize = grid.allAppsIconSizePx;
158         } else if (display == DISPLAY_FOLDER) {
159             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
160             setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
161             defaultIconSize = grid.folderChildIconSizePx;
162         }
163         mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
164 
165         mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,
166                 defaultIconSize);
167         a.recycle();
168 
169         mLongPressHelper = new CheckLongPressHelper(this);
170         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
171 
172         setEllipsize(TruncateAt.END);
173         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
174         setTextAlpha(1f);
175     }
176 
177     @Override
onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)178     protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
179         // Disable marques when not focused to that, so that updating text does not cause relayout.
180         setEllipsize(focused ? TruncateAt.MARQUEE : TruncateAt.END);
181         super.onFocusChanged(focused, direction, previouslyFocusedRect);
182     }
183 
184     /**
185      * Resets the view so it can be recycled.
186      */
reset()187     public void reset() {
188         mBadgeInfo = null;
189         mBadgeColor = Color.TRANSPARENT;
190         mBadgeScale = 0f;
191         mForceHideBadge = false;
192     }
193 
applyFromShortcutInfo(ShortcutInfo info)194     public void applyFromShortcutInfo(ShortcutInfo info) {
195         applyFromShortcutInfo(info, false);
196     }
197 
applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged)198     public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
199         applyIconAndLabel(info);
200         setTag(info);
201         if (promiseStateChanged || (info.hasPromiseIconUi())) {
202             applyPromiseState(promiseStateChanged);
203         }
204 
205         applyBadgeState(info, false /* animate */);
206     }
207 
applyFromApplicationInfo(AppInfo info)208     public void applyFromApplicationInfo(AppInfo info) {
209         applyIconAndLabel(info);
210 
211         // We don't need to check the info since it's not a ShortcutInfo
212         super.setTag(info);
213 
214         // Verify high res immediately
215         verifyHighRes();
216 
217         if (info instanceof PromiseAppInfo) {
218             PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
219             applyProgressLevel(promiseAppInfo.level);
220         }
221         applyBadgeState(info, false /* animate */);
222     }
223 
applyFromPackageItemInfo(PackageItemInfo info)224     public void applyFromPackageItemInfo(PackageItemInfo info) {
225         applyIconAndLabel(info);
226         // We don't need to check the info since it's not a ShortcutInfo
227         super.setTag(info);
228 
229         // Verify high res immediately
230         verifyHighRes();
231     }
232 
applyIconAndLabel(ItemInfoWithIcon info)233     private void applyIconAndLabel(ItemInfoWithIcon info) {
234         FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
235         mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);
236 
237         setIcon(iconDrawable);
238         setText(info.title);
239         if (info.contentDescription != null) {
240             setContentDescription(info.isDisabled()
241                     ? getContext().getString(R.string.disabled_app_label, info.contentDescription)
242                     : info.contentDescription);
243         }
244     }
245 
246     /**
247      * Overrides the default long press timeout.
248      */
setLongPressTimeout(int longPressTimeout)249     public void setLongPressTimeout(int longPressTimeout) {
250         mLongPressHelper.setLongPressTimeout(longPressTimeout);
251     }
252 
253     @Override
setTag(Object tag)254     public void setTag(Object tag) {
255         if (tag != null) {
256             LauncherModel.checkItemInfo((ItemInfo) tag);
257         }
258         super.setTag(tag);
259     }
260 
261     @Override
refreshDrawableState()262     public void refreshDrawableState() {
263         if (!mIgnorePressedStateChange) {
264             super.refreshDrawableState();
265         }
266     }
267 
268     @Override
onCreateDrawableState(int extraSpace)269     protected int[] onCreateDrawableState(int extraSpace) {
270         final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
271         if (mStayPressed) {
272             mergeDrawableStates(drawableState, STATE_PRESSED);
273         }
274         return drawableState;
275     }
276 
277     /** Returns the icon for this view. */
getIcon()278     public Drawable getIcon() {
279         return mIcon;
280     }
281 
282     @Override
onTouchEvent(MotionEvent event)283     public boolean onTouchEvent(MotionEvent event) {
284         // Call the superclass onTouchEvent first, because sometimes it changes the state to
285         // isPressed() on an ACTION_UP
286         boolean result = super.onTouchEvent(event);
287 
288         // Check for a stylus button press, if it occurs cancel any long press checks.
289         if (mStylusEventHelper.onMotionEvent(event)) {
290             mLongPressHelper.cancelLongPress();
291             result = true;
292         }
293 
294         switch (event.getAction()) {
295             case MotionEvent.ACTION_DOWN:
296                 // If we're in a stylus button press, don't check for long press.
297                 if (!mStylusEventHelper.inStylusButtonPressed()) {
298                     mLongPressHelper.postCheckForLongPress();
299                 }
300                 break;
301             case MotionEvent.ACTION_CANCEL:
302             case MotionEvent.ACTION_UP:
303                 mLongPressHelper.cancelLongPress();
304                 break;
305             case MotionEvent.ACTION_MOVE:
306                 if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
307                     mLongPressHelper.cancelLongPress();
308                 }
309                 break;
310         }
311         return result;
312     }
313 
setStayPressed(boolean stayPressed)314     void setStayPressed(boolean stayPressed) {
315         mStayPressed = stayPressed;
316         refreshDrawableState();
317     }
318 
319     @Override
onLauncherResume()320     public void onLauncherResume() {
321         // Reset the pressed state of icon that was locked in the press state while activity
322         // was launching
323         setStayPressed(false);
324     }
325 
clearPressedBackground()326     void clearPressedBackground() {
327         setPressed(false);
328         setStayPressed(false);
329     }
330 
331     @Override
onKeyUp(int keyCode, KeyEvent event)332     public boolean onKeyUp(int keyCode, KeyEvent event) {
333         // Unlike touch events, keypress event propagate pressed state change immediately,
334         // without waiting for onClickHandler to execute. Disable pressed state changes here
335         // to avoid flickering.
336         mIgnorePressedStateChange = true;
337         boolean result = super.onKeyUp(keyCode, event);
338         mIgnorePressedStateChange = false;
339         refreshDrawableState();
340         return result;
341     }
342 
343     @SuppressWarnings("wrongcall")
drawWithoutBadge(Canvas canvas)344     protected void drawWithoutBadge(Canvas canvas) {
345         super.onDraw(canvas);
346     }
347 
348     @Override
onDraw(Canvas canvas)349     public void onDraw(Canvas canvas) {
350         super.onDraw(canvas);
351         drawBadgeIfNecessary(canvas);
352     }
353 
354     /**
355      * Draws the icon badge in the top right corner of the icon bounds.
356      * @param canvas The canvas to draw to.
357      */
drawBadgeIfNecessary(Canvas canvas)358     protected void drawBadgeIfNecessary(Canvas canvas) {
359         if (!mForceHideBadge && (hasBadge() || mBadgeScale > 0)) {
360             getIconBounds(mTempIconBounds);
361             mTempSpaceForBadgeOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
362             final int scrollX = getScrollX();
363             final int scrollY = getScrollY();
364             canvas.translate(scrollX, scrollY);
365             mBadgeRenderer.draw(canvas, mBadgeColor, mTempIconBounds, mBadgeScale,
366                     mTempSpaceForBadgeOffset);
367             canvas.translate(-scrollX, -scrollY);
368         }
369     }
370 
forceHideBadge(boolean forceHideBadge)371     public void forceHideBadge(boolean forceHideBadge) {
372         if (mForceHideBadge == forceHideBadge) {
373             return;
374         }
375         mForceHideBadge = forceHideBadge;
376 
377         if (forceHideBadge) {
378             invalidate();
379         } else if (hasBadge()) {
380             ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, 0, 1).start();
381         }
382     }
383 
hasBadge()384     private boolean hasBadge() {
385         return mBadgeInfo != null;
386     }
387 
getIconBounds(Rect outBounds)388     public void getIconBounds(Rect outBounds) {
389         int top = getPaddingTop();
390         int left = (getWidth() - mIconSize) / 2;
391         int right = left + mIconSize;
392         int bottom = top + mIconSize;
393         outBounds.set(left, top, right, bottom);
394     }
395 
396     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)397     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
398         if (mCenterVertically) {
399             Paint.FontMetrics fm = getPaint().getFontMetrics();
400             int cellHeightPx = mIconSize + getCompoundDrawablePadding() +
401                     (int) Math.ceil(fm.bottom - fm.top);
402             int height = MeasureSpec.getSize(heightMeasureSpec);
403             setPadding(getPaddingLeft(), (height - cellHeightPx) / 2, getPaddingRight(),
404                     getPaddingBottom());
405         }
406         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
407     }
408 
409     @Override
setTextColor(int color)410     public void setTextColor(int color) {
411         mTextColor = color;
412         super.setTextColor(getModifiedColor());
413     }
414 
415     @Override
setTextColor(ColorStateList colors)416     public void setTextColor(ColorStateList colors) {
417         mTextColor = colors.getDefaultColor();
418         if (Float.compare(mTextAlpha, 1) == 0) {
419             super.setTextColor(colors);
420         } else {
421             super.setTextColor(getModifiedColor());
422         }
423     }
424 
shouldTextBeVisible()425     public boolean shouldTextBeVisible() {
426         // Text should be visible everywhere but the hotseat.
427         Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
428         ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;
429         return info == null || info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT;
430     }
431 
setTextVisibility(boolean visible)432     public void setTextVisibility(boolean visible) {
433         setTextAlpha(visible ? 1 : 0);
434     }
435 
setTextAlpha(float alpha)436     private void setTextAlpha(float alpha) {
437         mTextAlpha = alpha;
438         super.setTextColor(getModifiedColor());
439     }
440 
getModifiedColor()441     private int getModifiedColor() {
442         if (mTextAlpha == 0) {
443             // Special case to prevent text shadows in high contrast mode
444             return Color.TRANSPARENT;
445         }
446         return ColorUtils.setAlphaComponent(
447                 mTextColor, Math.round(Color.alpha(mTextColor) * mTextAlpha));
448     }
449 
450     /**
451      * Creates an animator to fade the text in or out.
452      * @param fadeIn Whether the text should fade in or fade out.
453      */
createTextAlphaAnimator(boolean fadeIn)454     public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
455         float toAlpha = shouldTextBeVisible() && fadeIn ? 1 : 0;
456         return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, toAlpha);
457     }
458 
459     @Override
cancelLongPress()460     public void cancelLongPress() {
461         super.cancelLongPress();
462 
463         mLongPressHelper.cancelLongPress();
464     }
465 
applyPromiseState(boolean promiseStateChanged)466     public void applyPromiseState(boolean promiseStateChanged) {
467         if (getTag() instanceof ShortcutInfo) {
468             ShortcutInfo info = (ShortcutInfo) getTag();
469             final boolean isPromise = info.hasPromiseIconUi();
470             final int progressLevel = isPromise ?
471                     ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
472                             info.getInstallProgress() : 0)) : 100;
473 
474             PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
475             if (preloadDrawable != null && promiseStateChanged) {
476                 preloadDrawable.maybePerformFinishedAnimation();
477             }
478         }
479     }
480 
applyProgressLevel(int progressLevel)481     public PreloadIconDrawable applyProgressLevel(int progressLevel) {
482         if (getTag() instanceof ItemInfoWithIcon) {
483             ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
484             if (progressLevel >= 100) {
485                 setContentDescription(info.contentDescription != null
486                         ? info.contentDescription : "");
487             } else if (progressLevel > 0) {
488                 setContentDescription(getContext()
489                         .getString(R.string.app_downloading_title, info.title,
490                                 NumberFormat.getPercentInstance().format(progressLevel * 0.01)));
491             } else {
492                 setContentDescription(getContext()
493                         .getString(R.string.app_waiting_download_title, info.title));
494             }
495             if (mIcon != null) {
496                 final PreloadIconDrawable preloadDrawable;
497                 if (mIcon instanceof PreloadIconDrawable) {
498                     preloadDrawable = (PreloadIconDrawable) mIcon;
499                     preloadDrawable.setLevel(progressLevel);
500                 } else {
501                     preloadDrawable = DrawableFactory.get(getContext())
502                             .newPendingIcon(info, getContext());
503                     preloadDrawable.setLevel(progressLevel);
504                     setIcon(preloadDrawable);
505                 }
506                 return preloadDrawable;
507             }
508         }
509         return null;
510     }
511 
applyBadgeState(ItemInfo itemInfo, boolean animate)512     public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
513         if (mIcon instanceof FastBitmapDrawable) {
514             boolean wasBadged = mBadgeInfo != null;
515             mBadgeInfo = mActivity.getBadgeInfoForItem(itemInfo);
516             boolean isBadged = mBadgeInfo != null;
517             float newBadgeScale = isBadged ? 1f : 0;
518             mBadgeRenderer = mActivity.getDeviceProfile().mBadgeRenderer;
519             if (wasBadged || isBadged) {
520                 // Animate when a badge is first added or when it is removed.
521                 if (animate && (wasBadged ^ isBadged) && isShown()) {
522                     ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
523                 } else {
524                     mBadgeScale = newBadgeScale;
525                     invalidate();
526                 }
527             }
528             if (itemInfo.contentDescription != null) {
529                 if (hasBadge()) {
530                     int count = mBadgeInfo.getNotificationCount();
531                     setContentDescription(getContext().getResources().getQuantityString(
532                             R.plurals.badged_app_label, count, itemInfo.contentDescription, count));
533                 } else {
534                     setContentDescription(itemInfo.contentDescription);
535                 }
536             }
537         }
538     }
539 
540     /**
541      * Sets the icon for this view based on the layout direction.
542      */
setIcon(Drawable icon)543     private void setIcon(Drawable icon) {
544         if (mIsIconVisible) {
545             applyCompoundDrawables(icon);
546         }
547         mIcon = icon;
548     }
549 
setIconVisible(boolean visible)550     public void setIconVisible(boolean visible) {
551         mIsIconVisible = visible;
552         Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
553         applyCompoundDrawables(icon);
554     }
555 
applyCompoundDrawables(Drawable icon)556     protected void applyCompoundDrawables(Drawable icon) {
557         // If we had already set an icon before, disable relayout as the icon size is the
558         // same as before.
559         mDisableRelayout = mIcon != null;
560 
561         icon.setBounds(0, 0, mIconSize, mIconSize);
562         if (mLayoutHorizontal) {
563             setCompoundDrawablesRelative(icon, null, null, null);
564         } else {
565             setCompoundDrawables(null, icon, null, null);
566         }
567         mDisableRelayout = false;
568     }
569 
570     @Override
requestLayout()571     public void requestLayout() {
572         if (!mDisableRelayout) {
573             super.requestLayout();
574         }
575     }
576 
577     /**
578      * Applies the item info if it is same as what the view is pointing to currently.
579      */
580     @Override
reapplyItemInfo(ItemInfoWithIcon info)581     public void reapplyItemInfo(ItemInfoWithIcon info) {
582         if (getTag() == info) {
583             mIconLoadRequest = null;
584             mDisableRelayout = true;
585 
586             // Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
587             info.iconBitmap.prepareToDraw();
588 
589             if (info instanceof AppInfo) {
590                 applyFromApplicationInfo((AppInfo) info);
591             } else if (info instanceof ShortcutInfo) {
592                 applyFromShortcutInfo((ShortcutInfo) info);
593                 mActivity.invalidateParent(info);
594             } else if (info instanceof PackageItemInfo) {
595                 applyFromPackageItemInfo((PackageItemInfo) info);
596             }
597 
598             mDisableRelayout = false;
599         }
600     }
601 
602     /**
603      * Verifies that the current icon is high-res otherwise posts a request to load the icon.
604      */
verifyHighRes()605     public void verifyHighRes() {
606         if (mIconLoadRequest != null) {
607             mIconLoadRequest.cancel();
608             mIconLoadRequest = null;
609         }
610         if (getTag() instanceof ItemInfoWithIcon) {
611             ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
612             if (info.usingLowResIcon) {
613                 mIconLoadRequest = LauncherAppState.getInstance(getContext()).getIconCache()
614                         .updateIconInBackground(BubbleTextView.this, info);
615             }
616         }
617     }
618 
getIconSize()619     public int getIconSize() {
620         return mIconSize;
621     }
622 }
623