Making LauncherIcons thread safe

Creating a pool of LauncherIcons so that they can be used from multiple threads

Change-Id: Idc7b5ddb47b6e338a5389f3c4faa6f63de108c72
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 9796d18..4d1bedc 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -110,7 +110,7 @@
         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 9775955..469b8bb 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -437,9 +437,11 @@
             }
 
             // 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 baa60b0..a5ca3ee 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -193,8 +193,10 @@
     }
 
     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 @@
         }
         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 @@
                 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 @@
             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 @@
                         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 @@
     }
 
     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 c476421..fe8a841 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -498,7 +498,9 @@
                 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 @@
         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 ea4b280..1b169f5 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -620,7 +620,9 @@
             @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 bdfeae1..a658d58 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -469,8 +469,11 @@
         }
         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 5cd90b1..173d0d1 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -137,7 +137,9 @@
             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 0d92d45..18797a4 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -31,8 +31,6 @@
     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 @@
     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 11ff88f..9732261 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -224,8 +224,10 @@
                     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 d4c396a..64f96d5 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -453,9 +453,8 @@
      */
     @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 5ee6a30..bd20c87 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.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 @@
     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 @@
     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 @@
 
         mMatrix = new Matrix();
         mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
-
-        mDir = context.getExternalFilesDir(null);
-        mRandom = new Random();
     }
 
     /**
@@ -148,18 +136,9 @@
         // 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 @@
         // 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 @@
                 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 @@
             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 fdb6313..0c9f4d9 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.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 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 @@
 /**
  * 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 @@
     /**
      * 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 @@
      * 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 @@
      * 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<Bitmap> 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 @@
 
         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 96f2629..5fbf502 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -46,17 +46,13 @@
 
     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 @@
         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 b1d07f1..6378ea1 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -173,7 +173,9 @@
                 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 @@
 
         // 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 b13b48a..883c33d 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -215,9 +215,10 @@
 
     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 @@
                                                     ? 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 18ae61b..089303e 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -192,8 +192,9 @@
                         // 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 0b75e2c..59f3d1c 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -95,8 +95,10 @@
                     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 b033405..9521a9e 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -94,8 +94,9 @@
                     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 4adfb7c..b295bb2 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -148,8 +148,9 @@
                 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 c5cf5e2..aa5b785 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -113,7 +113,9 @@
         } 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);