Home | History | Annotate | Download | only in launcher3
      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 
    131     public BubbleTextView(Context context) {
    132         this(context, null, 0);
    133     }
    134 
    135     public BubbleTextView(Context context, AttributeSet attrs) {
    136         this(context, attrs, 0);
    137     }
    138 
    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
    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      */
    187     public void reset() {
    188         mBadgeInfo = null;
    189         mBadgeColor = Color.TRANSPARENT;
    190         mBadgeScale = 0f;
    191         mForceHideBadge = false;
    192     }
    193 
    194     public void applyFromShortcutInfo(ShortcutInfo info) {
    195         applyFromShortcutInfo(info, false);
    196     }
    197 
    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 
    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 
    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 
    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      */
    249     public void setLongPressTimeout(int longPressTimeout) {
    250         mLongPressHelper.setLongPressTimeout(longPressTimeout);
    251     }
    252 
    253     @Override
    254     public void setTag(Object tag) {
    255         if (tag != null) {
    256             LauncherModel.checkItemInfo((ItemInfo) tag);
    257         }
    258         super.setTag(tag);
    259     }
    260 
    261     @Override
    262     public void refreshDrawableState() {
    263         if (!mIgnorePressedStateChange) {
    264             super.refreshDrawableState();
    265         }
    266     }
    267 
    268     @Override
    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. */
    278     public Drawable getIcon() {
    279         return mIcon;
    280     }
    281 
    282     @Override
    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 
    314     void setStayPressed(boolean stayPressed) {
    315         mStayPressed = stayPressed;
    316         refreshDrawableState();
    317     }
    318 
    319     @Override
    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 
    326     void clearPressedBackground() {
    327         setPressed(false);
    328         setStayPressed(false);
    329     }
    330 
    331     @Override
    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")
    344     protected void drawWithoutBadge(Canvas canvas) {
    345         super.onDraw(canvas);
    346     }
    347 
    348     @Override
    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      */
    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 
    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 
    384     private boolean hasBadge() {
    385         return mBadgeInfo != null;
    386     }
    387 
    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
    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
    410     public void setTextColor(int color) {
    411         mTextColor = color;
    412         super.setTextColor(getModifiedColor());
    413     }
    414 
    415     @Override
    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 
    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 
    432     public void setTextVisibility(boolean visible) {
    433         setTextAlpha(visible ? 1 : 0);
    434     }
    435 
    436     private void setTextAlpha(float alpha) {
    437         mTextAlpha = alpha;
    438         super.setTextColor(getModifiedColor());
    439     }
    440 
    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      */
    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
    460     public void cancelLongPress() {
    461         super.cancelLongPress();
    462 
    463         mLongPressHelper.cancelLongPress();
    464     }
    465 
    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 
    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 
    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      */
    543     private void setIcon(Drawable icon) {
    544         if (mIsIconVisible) {
    545             applyCompoundDrawables(icon);
    546         }
    547         mIcon = icon;
    548     }
    549 
    550     public void setIconVisible(boolean visible) {
    551         mIsIconVisible = visible;
    552         Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
    553         applyCompoundDrawables(icon);
    554     }
    555 
    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
    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
    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      */
    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 
    619     public int getIconSize() {
    620         return mIconSize;
    621     }
    622 }
    623