From 18a4e5aefbd1bd562e9161f8f10b5d3825e7b69d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 9 Jan 2018 15:34:38 -0800 Subject: [PATCH] Making LauncherIcons thread safe Creating a pool of LauncherIcons so that they can be used from multiple threads Change-Id: Idc7b5ddb47b6e338a5389f3c4faa6f63de108c72 --- res/layout/user_folder.xml | 74 ---- src/com/android/launcher3/AppInfo.java | 2 +- .../android/launcher3/AutoInstallsLayout.java | 6 +- src/com/android/launcher3/IconCache.java | 37 +- .../launcher3/InstallShortcutReceiver.java | 11 +- src/com/android/launcher3/LauncherModel.java | 4 +- .../launcher3/WidgetPreviewLoader.java | 7 +- .../compat/LauncherAppsCompatVO.java | 4 +- .../android/launcher3/config/BaseFlags.java | 6 - .../android/launcher3/dragndrop/DragView.java | 4 +- src/com/android/launcher3/folder/Folder.java | 5 +- .../launcher3/graphics/IconNormalizer.java | 62 +-- .../launcher3/graphics/LauncherIcons.java | 382 +++++++++--------- .../launcher3/graphics/ShadowGenerator.java | 18 +- .../android/launcher3/model/LoaderCursor.java | 9 +- .../android/launcher3/model/LoaderTask.java | 11 +- .../launcher3/model/PackageUpdatedTask.java | 5 +- .../launcher3/model/ShortcutsChangedTask.java | 6 +- .../model/UserLockStateChangedTask.java | 5 +- .../launcher3/popup/PopupPopulator.java | 5 +- .../widget/PendingItemDragHelper.java | 4 +- 21 files changed, 276 insertions(+), 391 deletions(-) delete mode 100644 res/layout/user_folder.xml diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml deleted file mode 100644 index afa19b8970..0000000000 --- a/res/layout/user_folder.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index 9796d18f1f..4d1bedcf31 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -110,7 +110,7 @@ public class AppInfo extends ItemInfoWithIcon { info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES; - if (FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO + if (Utilities.ATLEAST_OREO && appInfo.targetSdkVersion >= Build.VERSION_CODES.O && Process.myUserHandle().equals(lai.getUser())) { // The icon for a non-primary user is badged, hence it's not exactly an adaptive icon. diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index 9775955f83..469b8bbcea 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -437,9 +437,11 @@ public class AutoInstallsLayout { } // Auto installs should always support the current platform version. + LauncherIcons li = LauncherIcons.obtain(mContext); mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap( - LauncherIcons.createBadgedIconBitmap( - icon, Process.myUserHandle(), mContext, VERSION.SDK_INT).icon)); + li.createBadgedIconBitmap(icon, Process.myUserHandle(), VERSION.SDK_INT).icon)); + li.recycle(); + mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId)); mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId)); diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index baa60b0f2a..a5ca3eece1 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -193,8 +193,10 @@ public class IconCache { } protected BitmapInfo makeDefaultIcon(UserHandle user) { - Drawable unbadged = getFullResDefaultActivityIcon(); - return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext, VERSION.SDK_INT); + try (LauncherIcons li = LauncherIcons.obtain(mContext)) { + return li.createBadgedIconBitmap( + getFullResDefaultActivityIcon(), user, VERSION.SDK_INT); + } } /** @@ -378,8 +380,10 @@ public class IconCache { } if (entry == null) { entry = new CacheEntry(); - LauncherIcons.createBadgedIconBitmap(getFullResIcon(app), app.getUser(), - mContext, app.getApplicationInfo().targetSdkVersion).applyTo(entry); + LauncherIcons li = LauncherIcons.obtain(mContext); + li.createBadgedIconBitmap(getFullResIcon(app), app.getUser(), + app.getApplicationInfo().targetSdkVersion).applyTo(entry); + li.recycle(); } entry.title = app.getLabel(); entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser()); @@ -535,9 +539,10 @@ public class IconCache { providerFetchedOnce = true; if (info != null) { - LauncherIcons.createBadgedIconBitmap( - getFullResIcon(info), info.getUser(), mContext, + LauncherIcons li = LauncherIcons.obtain(mContext); + li.createBadgedIconBitmap(getFullResIcon(info), info.getUser(), info.getApplicationInfo().targetSdkVersion).applyTo(entry); + li.recycle(); } else { if (usePackageIcon) { CacheEntry packageEntry = getEntryForPackageLocked( @@ -596,7 +601,9 @@ public class IconCache { entry.title = title; } if (icon != null) { - LauncherIcons.createIconBitmap(icon, mContext).applyTo(entry); + LauncherIcons li = LauncherIcons.obtain(mContext); + li.createIconBitmap(icon).applyTo(entry); + li.recycle(); } if (!TextUtils.isEmpty(title) && entry.icon != null) { mCache.put(cacheKey, entry); @@ -633,14 +640,17 @@ public class IconCache { throw new NameNotFoundException("ApplicationInfo is null"); } + LauncherIcons li = LauncherIcons.obtain(mContext); // Load the full res icon for the application, but if useLowResIcon is set, then // only keep the low resolution icon instead of the larger full-sized icon - BitmapInfo iconInfo = LauncherIcons.createBadgedIconBitmap( - appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion); + BitmapInfo iconInfo = li.createBadgedIconBitmap( + appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion); if (mInstantAppResolver.isInstantApp(appInfo)) { - LauncherIcons.badgeWithDrawable(iconInfo.icon, - mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext); + li.badgeWithDrawable(iconInfo.icon, + mContext.getDrawable(R.drawable.ic_instant_app_badge)); } + li.recycle(); + Bitmap lowResIcon = generateLowResIcon(iconInfo.icon); entry.title = appInfo.loadLabel(mPackageManager); entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); @@ -776,10 +786,7 @@ public class IconCache { } private static final class IconDB extends SQLiteCacheHelper { - private final static int DB_VERSION = 18; - - private final static int RELEASE_VERSION = DB_VERSION + - (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1); + private final static int RELEASE_VERSION = 20; private final static String TABLE_NAME = "icons"; private final static String COLUMN_ROWID = "rowid"; diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index c476421573..fe8a841be9 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -498,7 +498,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver { return Pair.create((ItemInfo) si, (Object) activityInfo); } else if (shortcutInfo != null) { ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext); - LauncherIcons.createShortcutIcon(shortcutInfo, mContext).applyTo(si); + LauncherIcons li = LauncherIcons.obtain(mContext); + li.createShortcutIcon(shortcutInfo).applyTo(si); + li.recycle(); return Pair.create((ItemInfo) si, (Object) shortcutInfo); } else if (providerInfo != null) { LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo @@ -643,15 +645,18 @@ public class InstallShortcutReceiver extends BroadcastReceiver { info.user = Process.myUserHandle(); BitmapInfo iconInfo = null; + LauncherIcons li = LauncherIcons.obtain(app.getContext()); if (bitmap instanceof Bitmap) { - iconInfo = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext()); + iconInfo = li.createIconBitmap((Bitmap) bitmap); } else { Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); if (extra instanceof Intent.ShortcutIconResource) { info.iconResource = (Intent.ShortcutIconResource) extra; - iconInfo = LauncherIcons.createIconBitmap(info.iconResource, app.getContext()); + iconInfo = li.createIconBitmap(info.iconResource); } } + li.recycle(); + if (iconInfo == null) { iconInfo = app.getIconCache().getDefaultIcon(info.user); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index ea4b280961..1b169f5304 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -620,7 +620,9 @@ public class LauncherModel extends BroadcastReceiver @Override public ShortcutInfo get() { si.updateFromDeepShortcutInfo(info, mApp.getContext()); - LauncherIcons.createShortcutIcon(info, mApp.getContext()).applyTo(si); + LauncherIcons li = LauncherIcons.obtain(mApp.getContext()); + li.createShortcutIcon(info).applyTo(si); + li.recycle(); return si; } }); diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index bdfeae1627..a658d585c0 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -469,8 +469,11 @@ public class WidgetPreviewLoader { } RectF boxRect = drawBoxWithShadow(c, size, size); - Bitmap icon = LauncherIcons.createScaledBitmapWithoutShadow( - mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext, 0); + LauncherIcons li = LauncherIcons.obtain(mContext); + Bitmap icon = li.createScaledBitmapWithoutShadow( + mutateOnMainThread(info.getFullResIcon(mIconCache)), 0); + li.recycle(); + Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight()); boxRect.set(0, 0, iconSize, iconSize); diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java index 5cd90b10e5..173d0d1f3b 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java @@ -137,7 +137,9 @@ public class LauncherAppsCompatVO extends LauncherAppsCompatVL { ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo()); ShortcutInfo info = new ShortcutInfo(compat, context); // Apply the unbadged icon and fetch the actual icon asynchronously. - LauncherIcons.createShortcutIcon(compat, context, false /* badged */).applyTo(info); + LauncherIcons li = LauncherIcons.obtain(context); + li.createShortcutIcon(compat, false /* badged */).applyTo(info); + li.recycle(); LauncherAppState.getInstance(context).getModel() .updateAndBindShortcutInfo(info, compat); return info; diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 0d92d45ff5..18797a40a4 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -31,8 +31,6 @@ abstract class BaseFlags { public static final boolean IS_DOGFOOD_BUILD = false; public static final String AUTHORITY = "com.android.launcher3.settings".intern(); - // Custom flags go below this - public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false; // When enabled allows to use any point on the fast scrollbar to start dragging. public static final boolean LAUNCHER3_DIRECT_SCROLL = true; // When enabled the promise icon is visible in all apps while installation an app. @@ -46,10 +44,6 @@ abstract class BaseFlags { public static final boolean QSB_ON_FIRST_SCREEN = true; // When enabled the all-apps icon is not added to the hotseat. public static final boolean NO_ALL_APPS_ICON = true; - // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}. - public static final boolean LEGACY_ICON_TREATMENT = true; - // When enabled, adaptive icons would have shadows baked when being stored to icon cache. - public static final boolean ADAPTIVE_ICON_SHADOW = true; // When enabled, app discovery will be enabled if service is implemented public static final boolean DISCOVERY_ENABLED = false; diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 11ff88f12d..9732261174 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -224,8 +224,10 @@ public class DragView extends View { mBadge = getBadge(info, appState, outObj[0]); mBadge.setBounds(badgeBounds); + LauncherIcons li = LauncherIcons.obtain(mLauncher); Utilities.scaleRectAboutCenter(bounds, - IconNormalizer.getInstance(mLauncher).getScale(dr, null, null, null)); + li.getNormalizer().getScale(dr, null, null, null)); + li.recycle(); AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr; // Shrink very tiny bit so that the clip path is smaller than the original bitmap diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index d4c396a112..64f96d5176 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -453,9 +453,8 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC */ @SuppressLint("InflateParams") static Folder fromXml(Launcher launcher) { - return (Folder) launcher.getLayoutInflater().inflate( - FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION - ? R.layout.user_folder : R.layout.user_folder_icon_normalized, null); + return (Folder) launcher.getLayoutInflater() + .inflate(R.layout.user_folder_icon_normalized, null); } private void startAnimation(final AnimatorSet a) { diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java index 5ee6a30b76..bd20c87ff0 100644 --- a/src/com/android/launcher3/graphics/IconNormalizer.java +++ b/src/com/android/launcher3/graphics/IconNormalizer.java @@ -37,10 +37,7 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; -import java.io.File; -import java.io.FileOutputStream; import java.nio.ByteBuffer; -import java.util.Random; public class IconNormalizer { @@ -64,9 +61,6 @@ public class IconNormalizer { private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f; private static final float SCALE_NOT_INITIALIZED = 0; - private static final Object LOCK = new Object(); - private static IconNormalizer sIconNormalizer; - private final int mMaxSize; private final Bitmap mBitmap; private final Bitmap mBitmapARGB; @@ -88,11 +82,8 @@ public class IconNormalizer { private final Paint mPaintIcon; private final Canvas mCanvasARGB; - private final File mDir; - private int mFileId; - private final Random mRandom; - - private IconNormalizer(Context context) { + /** package private **/ + IconNormalizer(Context context) { // Use twice the icon size as maximum size to avoid scaling down twice. mMaxSize = LauncherAppState.getIDP(context).iconBitmapSize * 2; mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8); @@ -124,9 +115,6 @@ public class IconNormalizer { mMatrix = new Matrix(); mAdaptiveIconScale = SCALE_NOT_INITIALIZED; - - mDir = context.getExternalFilesDir(null); - mRandom = new Random(); } /** @@ -148,18 +136,9 @@ public class IconNormalizer { // Condition 2: // Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation // should generate transparent image, if the actual icon is equivalent to the shape. - mFileId = mRandom.nextInt(); mBitmapARGB.eraseColor(Color.TRANSPARENT); mCanvasARGB.drawBitmap(mBitmap, 0, 0, mPaintIcon); - if (DEBUG) { - final File beforeFile = new File(mDir, "isShape" + mFileId + "_before.png"); - try { - mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100, - new FileOutputStream(beforeFile)); - } catch (Exception e) {} - } - // Fit the shape within the icon's bounding box mMatrix.reset(); mMatrix.setScale(mBounds.width(), mBounds.height()); @@ -172,24 +151,8 @@ public class IconNormalizer { // DST_OUT operation around the mask path outline mCanvasARGB.drawPath(maskPath, mPaintMaskShapeOutline); - boolean isTrans = isTransparentBitmap(mBitmapARGB); - if (DEBUG) { - final File afterFile = new File(mDir, - "isShape" + mFileId + "_after_" + isTrans + ".png"); - try { - mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100, - new FileOutputStream(afterFile)); - } catch (Exception e) {} - } - // Check if the result is almost transparent - if (!isTrans) { - if (DEBUG) { - Log.d(TAG, "Not same as mask shape"); - } - return false; - } - return true; + return isTransparentBitmap(mBitmapARGB); } /** @@ -203,19 +166,13 @@ public class IconNormalizer { mBounds.left, mBounds.top, w, h); int sum = 0; - for (int i = 0; i < w * h; i++) { + for (int i = w * h - 1; i >= 0; i--) { if(Color.alpha(mPixelsARGB[i]) > MIN_VISIBLE_ALPHA) { sum++; } } float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height()); - boolean transparentImage = percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD; - if (DEBUG) { - Log.d(TAG, - "Total # pixel that is different (id=" + mFileId + "):" + percentageDiffPixels - + "=" + sum + "/" + mBounds.width() * mBounds.height()); - } - return transparentImage; + return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD; } /** @@ -416,13 +373,4 @@ public class IconNormalizer { last = i; } } - - public static IconNormalizer getInstance(Context context) { - synchronized (LOCK) { - if (sIconNormalizer == null) { - sIconNormalizer = new IconNormalizer(context); - } - } - return sIconNormalizer; - } } diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java index fdb631397f..0c9f4d9f05 100644 --- a/src/com/android/launcher3/graphics/LauncherIcons.java +++ b/src/com/android/launcher3/graphics/LauncherIcons.java @@ -16,7 +16,11 @@ package com.android.launcher3.graphics; -import android.annotation.TargetApi; +import static android.graphics.Paint.DITHER_FLAG; +import static android.graphics.Paint.FILTER_BITMAP_FLAG; + +import static com.android.launcher3.graphics.ShadowGenerator.BLUR_FACTOR; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -25,7 +29,6 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.Rect; import android.graphics.RectF; @@ -41,11 +44,11 @@ import android.support.annotation.Nullable; import com.android.launcher3.AppInfo; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.IconCache; +import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutInfoCompat; @@ -56,32 +59,95 @@ import com.android.launcher3.util.Themes; /** * Helper methods for generating various launcher icons */ -public class LauncherIcons { +public class LauncherIcons implements AutoCloseable { - private static final Rect sOldBounds = new Rect(); - private static final Canvas sCanvas = new Canvas(); + public static final Object sPoolSync = new Object(); + private static LauncherIcons sPool; - static { - sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, - Paint.FILTER_BITMAP_FLAG)); + /** + * Return a new Message instance from the global pool. Allows us to + * avoid allocating new objects in many cases. + */ + public static LauncherIcons obtain(Context context) { + synchronized (sPoolSync) { + if (sPool != null) { + LauncherIcons m = sPool; + sPool = m.next; + m.next = null; + return m; + } + } + return new LauncherIcons(context); + } + + /** + * Recycles a LauncherIcons that may be in-use. + */ + public void recycle() { + synchronized (sPoolSync) { + next = sPool; + sPool = this; + } + } + + @Override + public void close() { + recycle(); + } + + private final Rect mOldBounds = new Rect(); + private final Context mContext; + private final Canvas mCanvas; + private final PackageManager mPm; + + private final int mFillResIconDpi; + private final int mIconBitmapSize; + + private IconNormalizer mNormalizer; + private ShadowGenerator mShadowGenerator; + + // sometimes we store linked lists of these things + private LauncherIcons next; + + private LauncherIcons(Context context) { + mContext = context.getApplicationContext(); + mPm = mContext.getPackageManager(); + + InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext); + mFillResIconDpi = idp.fillResIconDpi; + mIconBitmapSize = idp.iconBitmapSize; + + mCanvas = new Canvas(); + mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG)); + } + + public ShadowGenerator getShadowGenerator() { + if (mShadowGenerator == null) { + mShadowGenerator = new ShadowGenerator(mContext); + } + return mShadowGenerator; + } + + public IconNormalizer getNormalizer() { + if (mNormalizer == null) { + mNormalizer = new IconNormalizer(mContext); + } + return mNormalizer; } /** * Returns a bitmap suitable for the all apps view. If the package or the resource do not * exist, it returns null. */ - public static BitmapInfo createIconBitmap(ShortcutIconResource iconRes, Context context) { - PackageManager packageManager = context.getPackageManager(); - // the resource + public BitmapInfo createIconBitmap(ShortcutIconResource iconRes) { try { - Resources resources = packageManager.getResourcesForApplication(iconRes.packageName); + Resources resources = mPm.getResourcesForApplication(iconRes.packageName); if (resources != null) { final int id = resources.getIdentifier(iconRes.resourceName, null, null); // do not stamp old legacy shortcuts as the app may have already forgotten about it - return createBadgedIconBitmap(resources.getDrawableForDensity( - id, LauncherAppState.getIDP(context).fillResIconDpi), + return createBadgedIconBitmap( + resources.getDrawableForDensity(id, mFillResIconDpi), Process.myUserHandle() /* only available on primary user */, - context, 0 /* do not apply legacy treatment */); } } catch (Exception e) { @@ -93,13 +159,12 @@ public class LauncherIcons { /** * Returns a bitmap which is of the appropriate size to be displayed as an icon */ - public static BitmapInfo createIconBitmap(Bitmap icon, Context context) { - final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize; - if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) { + public BitmapInfo createIconBitmap(Bitmap icon) { + if (mIconBitmapSize == icon.getWidth() && mIconBitmapSize == icon.getHeight()) { return BitmapInfo.fromBitmap(icon); } return BitmapInfo.fromBitmap( - createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f)); + createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f)); } /** @@ -107,51 +172,24 @@ public class LauncherIcons { * view or workspace. The icon is badged for {@param user}. * The bitmap is also visually normalized with other icons. */ - public static BitmapInfo createBadgedIconBitmap( - Drawable icon, UserHandle user, Context context, int iconAppTargetSdk) { - - IconNormalizer normalizer; - float scale = 1f; - if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) { - normalizer = IconNormalizer.getInstance(context); - if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) { - boolean[] outShape = new boolean[1]; - AdaptiveIconDrawable dr = (AdaptiveIconDrawable) - context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate(); - dr.setBounds(0, 0, 1, 1); - scale = normalizer.getScale(icon, null, dr.getIconMask(), outShape); - if (FeatureFlags.LEGACY_ICON_TREATMENT && - !outShape[0]){ - Drawable wrappedIcon = wrapToAdaptiveIconDrawable(context, icon, scale); - if (wrappedIcon != icon) { - icon = wrappedIcon; - scale = normalizer.getScale(icon, null, null, null); - } - } - } else { - scale = normalizer.getScale(icon, null, null, null); - } - } - Bitmap bitmap = createIconBitmap(icon, context, scale); - if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO && - icon instanceof AdaptiveIconDrawable) { - synchronized (sCanvas) { - sCanvas.setBitmap(bitmap); - ShadowGenerator.getInstance(context).recreateIcon( - Bitmap.createBitmap(bitmap), sCanvas); - sCanvas.setBitmap(null); - } + public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk) { + float[] scale = new float[1]; + icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, null, scale); + Bitmap bitmap = createIconBitmap(icon, scale[0]); + if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { + mCanvas.setBitmap(bitmap); + getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas); + mCanvas.setBitmap(null); } final Bitmap result; if (user != null && !Process.myUserHandle().equals(user)) { BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap); - Drawable badged = context.getPackageManager().getUserBadgedIcon( - drawable, user); + Drawable badged = mPm.getUserBadgedIcon(drawable, user); if (badged instanceof BitmapDrawable) { result = ((BitmapDrawable) badged).getBitmap(); } else { - result = createIconBitmap(badged, context, 1f); + result = createIconBitmap(badged, 1f); } } else { result = bitmap; @@ -163,170 +201,134 @@ public class LauncherIcons { * Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually * normalized with other icons and has enough spacing to add shadow. */ - public static Bitmap createScaledBitmapWithoutShadow( - Drawable icon, Context context, int iconAppTargetSdk) { + public Bitmap createScaledBitmapWithoutShadow(Drawable icon, int iconAppTargetSdk) { RectF iconBounds = new RectF(); - IconNormalizer normalizer; + float[] scale = new float[1]; + icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, iconBounds, scale); + return createIconBitmap(icon, + Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds))); + } + + private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, int iconAppTargetSdk, + RectF outIconBounds, float[] outScale) { float scale = 1f; - if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) { - normalizer = IconNormalizer.getInstance(context); - if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) { - boolean[] outShape = new boolean[1]; - AdaptiveIconDrawable dr = (AdaptiveIconDrawable) - context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate(); - dr.setBounds(0, 0, 1, 1); - scale = normalizer.getScale(icon, iconBounds, dr.getIconMask(), outShape); - if (Utilities.ATLEAST_OREO && FeatureFlags.LEGACY_ICON_TREATMENT && - !outShape[0]) { - Drawable wrappedIcon = wrapToAdaptiveIconDrawable(context, icon, scale); - if (wrappedIcon != icon) { - icon = wrappedIcon; - scale = normalizer.getScale(icon, iconBounds, null, null); - } - } - } else { - scale = normalizer.getScale(icon, iconBounds, null, null); + if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) { + boolean[] outShape = new boolean[1]; + AdaptiveIconDrawable dr = (AdaptiveIconDrawable) + mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate(); + dr.setBounds(0, 0, 1, 1); + scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape); + if (Utilities.ATLEAST_OREO && !outShape[0] && !(icon instanceof AdaptiveIconDrawable)) { + FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground()); + fsd.setDrawable(icon); + fsd.setScale(scale); + icon = dr; + scale = getNormalizer().getScale(icon, outIconBounds, null, null); } - + } else { + scale = getNormalizer().getScale(icon, outIconBounds, null, null); } - scale = Math.min(scale, ShadowGenerator.getScaleForBounds(iconBounds)); - return createIconBitmap(icon, context, scale); + + outScale[0] = scale; + return icon; } /** * Adds the {@param badge} on top of {@param target} using the badge dimensions. */ - public static void badgeWithDrawable(Bitmap target, Drawable badge, Context context) { - synchronized (sCanvas) { - sCanvas.setBitmap(target); - badgeWithDrawable(sCanvas, badge, context); - sCanvas.setBitmap(null); - } + public void badgeWithDrawable(Bitmap target, Drawable badge) { + mCanvas.setBitmap(target); + badgeWithDrawable(mCanvas, badge); + mCanvas.setBitmap(null); } /** * Adds the {@param badge} on top of {@param target} using the badge dimensions. */ - private static void badgeWithDrawable(Canvas target, Drawable badge, Context context) { - int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size); - int iconSize = LauncherAppState.getIDP(context).iconBitmapSize; - badge.setBounds(iconSize - badgeSize, iconSize - badgeSize, iconSize, iconSize); + private void badgeWithDrawable(Canvas target, Drawable badge) { + int badgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.profile_badge_size); + badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize, + mIconBitmapSize, mIconBitmapSize); badge.draw(target); } /** * @param scale the scale to apply before drawing {@param icon} on the canvas */ - private static Bitmap createIconBitmap(Drawable icon, Context context, float scale) { - synchronized (sCanvas) { - final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize; - int width = iconBitmapSize; - int height = iconBitmapSize; + private Bitmap createIconBitmap(Drawable icon, float scale) { + int width = mIconBitmapSize; + int height = mIconBitmapSize; - if (icon instanceof PaintDrawable) { - PaintDrawable painter = (PaintDrawable) icon; - painter.setIntrinsicWidth(width); - painter.setIntrinsicHeight(height); - } else if (icon instanceof BitmapDrawable) { - // Ensure the bitmap has a density. - BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; - Bitmap bitmap = bitmapDrawable.getBitmap(); - if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) { - bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics()); - } + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(width); + painter.setIntrinsicHeight(height); + } else if (icon instanceof BitmapDrawable) { + // Ensure the bitmap has a density. + BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; + Bitmap bitmap = bitmapDrawable.getBitmap(); + if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) { + bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics()); } - - int sourceWidth = icon.getIntrinsicWidth(); - int sourceHeight = icon.getIntrinsicHeight(); - if (sourceWidth > 0 && sourceHeight > 0) { - // Scale the icon proportionally to the icon dimensions - final float ratio = (float) sourceWidth / sourceHeight; - if (sourceWidth > sourceHeight) { - height = (int) (width / ratio); - } else if (sourceHeight > sourceWidth) { - width = (int) (height * ratio); - } - } - // no intrinsic size --> use default size - int textureWidth = iconBitmapSize; - int textureHeight = iconBitmapSize; - - Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, - Bitmap.Config.ARGB_8888); - final Canvas canvas = sCanvas; - canvas.setBitmap(bitmap); - - final int left = (textureWidth-width) / 2; - final int top = (textureHeight-height) / 2; - - sOldBounds.set(icon.getBounds()); - if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { - int offset = Math.max((int)(ShadowGenerator.BLUR_FACTOR * iconBitmapSize), - Math.min(left, top)); - int size = Math.max(width, height); - icon.setBounds(offset, offset, size, size); - } else { - icon.setBounds(left, top, left+width, top+height); - } - canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2); - icon.draw(canvas); - canvas.restore(); - icon.setBounds(sOldBounds); - canvas.setBitmap(null); - - return bitmap; - } - } - - /** - * If the platform is running O but the app is not providing AdaptiveIconDrawable, then - * shrink the legacy icon and set it as foreground. Use color drawable as background to - * create AdaptiveIconDrawable. - */ - @TargetApi(Build.VERSION_CODES.O) - private static Drawable wrapToAdaptiveIconDrawable( - Context context, Drawable drawable, float scale) { - if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO)) { - return drawable; } - try { - if (!(drawable instanceof AdaptiveIconDrawable)) { - AdaptiveIconDrawable iconWrapper = (AdaptiveIconDrawable) - context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate(); - FixedScaleDrawable fsd = ((FixedScaleDrawable) iconWrapper.getForeground()); - fsd.setDrawable(drawable); - fsd.setScale(scale); - return iconWrapper; + int sourceWidth = icon.getIntrinsicWidth(); + int sourceHeight = icon.getIntrinsicHeight(); + if (sourceWidth > 0 && sourceHeight > 0) { + // Scale the icon proportionally to the icon dimensions + final float ratio = (float) sourceWidth / sourceHeight; + if (sourceWidth > sourceHeight) { + height = (int) (width / ratio); + } else if (sourceHeight > sourceWidth) { + width = (int) (height * ratio); } - } catch (Exception e) { - return drawable; } - return drawable; + // no intrinsic size --> use default size + int textureWidth = mIconBitmapSize; + int textureHeight = mIconBitmapSize; + + Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, + Bitmap.Config.ARGB_8888); + mCanvas.setBitmap(bitmap); + + final int left = (textureWidth-width) / 2; + final int top = (textureHeight-height) / 2; + + mOldBounds.set(icon.getBounds()); + if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { + int offset = Math.max((int)(BLUR_FACTOR * textureWidth), Math.min(left, top)); + int size = Math.max(width, height); + icon.setBounds(offset, offset, size, size); + } else { + icon.setBounds(left, top, left+width, top+height); + } + mCanvas.save(Canvas.MATRIX_SAVE_FLAG); + mCanvas.scale(scale, scale, textureWidth / 2, textureHeight / 2); + icon.draw(mCanvas); + mCanvas.restore(); + icon.setBounds(mOldBounds); + mCanvas.setBitmap(null); + + return bitmap; } - public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context) { - return createShortcutIcon(shortcutInfo, context, true /* badged */); + public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo) { + return createShortcutIcon(shortcutInfo, true /* badged */); } - public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context, - boolean badged) { - return createShortcutIcon(shortcutInfo, context, badged, null); + public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, boolean badged) { + return createShortcutIcon(shortcutInfo, badged, null); } - public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context, + public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, boolean badged, @Nullable Provider fallbackIconProvider) { - LauncherAppState app = LauncherAppState.getInstance(context); - Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context) - .getShortcutIconDrawable(shortcutInfo, - app.getInvariantDeviceProfile().fillResIconDpi); - IconCache cache = app.getIconCache(); + Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext) + .getShortcutIconDrawable(shortcutInfo, mFillResIconDpi); + IconCache cache = LauncherAppState.getInstance(mContext).getIconCache(); Bitmap unbadgedBitmap = null; if (unbadgedDrawable != null) { - unbadgedBitmap = LauncherIcons.createScaledBitmapWithoutShadow( - unbadgedDrawable, context, 0); + unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0); } else { if (fallbackIconProvider != null) { unbadgedBitmap = fallbackIconProvider.get(); @@ -338,20 +340,18 @@ public class LauncherIcons { BitmapInfo result = new BitmapInfo(); if (!badged) { - result.color = Themes.getColorAccent(context); + result.color = Themes.getColorAccent(mContext); result.icon = unbadgedBitmap; return result; } - int size = app.getInvariantDeviceProfile().iconBitmapSize; - final Bitmap unbadgedfinal = unbadgedBitmap; final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache); result.color = badge.iconColor; - result.icon = UiFactory.createFromRenderer(size, size, false, (c) -> { - ShadowGenerator.getInstance(context).recreateIcon(unbadgedfinal, c); - badgeWithDrawable(c, new FastBitmapDrawable(badge), context); + result.icon = UiFactory.createFromRenderer(mIconBitmapSize, mIconBitmapSize, false, (c) -> { + getShadowGenerator().recreateIcon(unbadgedfinal, c); + badgeWithDrawable(c, new FastBitmapDrawable(badge)); }); return result; } diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java index 96f26292f7..5fbf502aab 100644 --- a/src/com/android/launcher3/graphics/ShadowGenerator.java +++ b/src/com/android/launcher3/graphics/ShadowGenerator.java @@ -46,17 +46,13 @@ public class ShadowGenerator { private static final int AMBIENT_SHADOW_ALPHA = 30; - private static final Object LOCK = new Object(); - // Singleton object guarded by {@link #LOCK} - private static ShadowGenerator sShadowGenerator; - private final int mIconSize; private final Paint mBlurPaint; private final Paint mDrawPaint; private final BlurMaskFilter mDefaultBlurMaskFilter; - private ShadowGenerator(Context context) { + public ShadowGenerator(Context context) { mIconSize = LauncherAppState.getIDP(context).iconBitmapSize; mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); @@ -86,18 +82,6 @@ public class ShadowGenerator { out.drawBitmap(icon, 0, 0, mDrawPaint); } - public static ShadowGenerator getInstance(Context context) { - // TODO: This currently fails as the system default icon also needs a shadow as it - // uses adaptive icon. - // Preconditions.assertNonUiThread(); - synchronized (LOCK) { - if (sShadowGenerator == null) { - sShadowGenerator = new ShadowGenerator(context); - } - } - return sShadowGenerator; - } - /** * Returns the minimum amount by which an icon with {@param bounds} should be scaled * so that the shadows do not get clipped. diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index b1d07f190b..6378ea1804 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -173,7 +173,9 @@ public class LoaderCursor extends CursorWrapper { info.iconResource = new ShortcutIconResource(); info.iconResource.packageName = packageName; info.iconResource.resourceName = resourceName; - BitmapInfo iconInfo = LauncherIcons.createIconBitmap(info.iconResource, mContext); + LauncherIcons li = LauncherIcons.obtain(mContext); + BitmapInfo iconInfo = li.createIconBitmap(info.iconResource); + li.recycle(); if (iconInfo != null) { iconInfo.applyTo(info); return true; @@ -183,9 +185,8 @@ public class LoaderCursor extends CursorWrapper { // Failed to load from resource, try loading from DB. byte[] data = getBlob(iconIndex); - try { - LauncherIcons.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), - mContext).applyTo(info); + try (LauncherIcons li = LauncherIcons.obtain(mContext)) { + li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)).applyTo(info); return true; } catch (Exception e) { Log.e(TAG, "Failed to load icon for info " + info, e); diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index b13b48a25b..883c33d5a3 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -215,9 +215,10 @@ public class LoaderTask implements Runnable { public void loadUiResources() { if (Utilities.ATLEAST_OREO) { - ClickShadowView.setAdaptiveIconScaleFactor( - IconNormalizer.getInstance(mApp.getContext()).getScale( - new AdaptiveIconDrawable(null, null), null, null, null)); + LauncherIcons li = LauncherIcons.obtain(mApp.getContext()); + ClickShadowView.setAdaptiveIconScaleFactor(li.getNormalizer() + .getScale(new AdaptiveIconDrawable(null, null), null, null, null)); + li.recycle(); } } @@ -476,8 +477,10 @@ public class LoaderTask implements Runnable { ? finalInfo.iconBitmap : null; } }; - LauncherIcons.createShortcutIcon(pinnedShortcut, context, + LauncherIcons li = LauncherIcons.obtain(context); + li.createShortcutIcon(pinnedShortcut, true /* badged */, fallbackIconProvider).applyTo(info); + li.recycle(); if (pmHelper.isAppSuspended( pinnedShortcut.getPackage(), info.user)) { info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 18ae61b1fa..089303ecea 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -192,8 +192,9 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { // Update shortcuts which use iconResource. if ((si.iconResource != null) && packageSet.contains(si.iconResource.packageName)) { - BitmapInfo iconInfo = - LauncherIcons.createIconBitmap(si.iconResource, context); + LauncherIcons li = LauncherIcons.obtain(context); + BitmapInfo iconInfo = li.createIconBitmap(si.iconResource); + li.recycle(); if (iconInfo != null) { iconInfo.applyTo(si); infoUpdated = true; diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java index 0b75e2c88c..59f3d1c60a 100644 --- a/src/com/android/launcher3/model/ShortcutsChangedTask.java +++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java @@ -95,8 +95,10 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask { shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context); // If the shortcut is pinned but no longer has an icon in the system, // keep the current icon instead of reverting to the default icon. - LauncherIcons.createShortcutIcon(fullDetails, context, true, - Provider.of(shortcutInfo.iconBitmap)).applyTo(shortcutInfo); + LauncherIcons li = LauncherIcons.obtain(context); + li.createShortcutIcon(fullDetails, true, Provider.of(shortcutInfo.iconBitmap)) + .applyTo(shortcutInfo); + li.recycle(); updatedShortcutInfos.add(shortcutInfo); } } diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java index b033405e8b..9521a9e5b5 100644 --- a/src/com/android/launcher3/model/UserLockStateChangedTask.java +++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java @@ -94,8 +94,9 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask { si.updateFromDeepShortcutInfo(shortcut, context); // If the shortcut is pinned but no longer has an icon in the system, // keep the current icon instead of reverting to the default icon. - LauncherIcons.createShortcutIcon(shortcut, context, true, - Provider.of(si.iconBitmap)).applyTo(si); + LauncherIcons li = LauncherIcons.obtain(context); + li.createShortcutIcon(shortcut, true, Provider.of(si.iconBitmap)).applyTo(si); + li.recycle(); } else { si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER; } diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java index 4adfb7c0e6..b295bb2aea 100644 --- a/src/com/android/launcher3/popup/PopupPopulator.java +++ b/src/com/android/launcher3/popup/PopupPopulator.java @@ -148,8 +148,9 @@ public class PopupPopulator { final ShortcutInfoCompat shortcut = shortcuts.get(i); final ShortcutInfo si = new ShortcutInfo(shortcut, launcher); // Use unbadged icon for the menu. - LauncherIcons.createShortcutIcon(shortcut, launcher, false /* badged */) - .applyTo(si); + LauncherIcons li = LauncherIcons.obtain(launcher); + li.createShortcutIcon(shortcut, false /* badged */).applyTo(si); + li.recycle(); si.rank = i; final DeepShortcutView view = shortcutViews.get(i); diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java index c5cf5e23a4..aa5b7855c6 100644 --- a/src/com/android/launcher3/widget/PendingItemDragHelper.java +++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java @@ -113,7 +113,9 @@ public class PendingItemDragHelper extends DragPreviewProvider { } else { PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo; Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache()); - preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, 0); + LauncherIcons li = LauncherIcons.obtain(launcher); + preview = li.createScaledBitmapWithoutShadow(icon, 0); + li.recycle(); scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth(); dragOffset = new Point(previewPadding / 2, previewPadding / 2);