Loading previews and holographic icons in background thread using AsyncTasks.

- Adding back animation between tabs

Change-Id: I1a49bfca4f85f579e232861aa02d08fb25d0aafc
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index 5c8812d..b3136e6 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -462,7 +462,6 @@
         mApps = list;
         Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
         mFilteredApps = rebuildFilteredApps(mApps);
-        mPageViewIconCache.retainAllApps(list);
         invalidatePageData();
     }
 
@@ -509,7 +508,6 @@
             int removeIndex = findAppByComponent(mApps, info);
             if (removeIndex > -1) {
                 mApps.remove(removeIndex);
-                mPageViewIconCache.removeOutline(new PagedViewIconCache.Key(info));
             }
         }
         mFilteredApps = rebuildFilteredApps(mApps);
@@ -576,6 +574,7 @@
 
     @Override
     public void syncPages() {
+        /*
         // ensure that we have the right number of pages (min of 1, since we have placeholders)
         int numPages = Math.max(1,
                 (int) Math.ceil((float) mFilteredApps.size() / (mCellCountX * mCellCountY)));
@@ -597,10 +596,12 @@
 
         // bound the current page
         setCurrentPage(Math.max(0, Math.min(numPages - 1, getCurrentPage())));
+        */
     }
 
     @Override
     public void syncPageItems(int page) {
+        /*
         // Ensure that we have the right number of items on the pages
         final int numPages = getPageCount();
         final int cellsPerPage = mCellCountX * mCellCountY;
@@ -652,7 +653,7 @@
                 final ApplicationInfo info = mFilteredApps.get(i);
                 PagedViewIcon icon = (PagedViewIcon) layout.getChildOnPageAt(index);
                 icon.applyFromApplicationInfo(
-                        info, mPageViewIconCache, true, createHolographicOutlines);
+                        info, null, true, createHolographicOutlines);
 
                 PagedViewCellLayout.LayoutParams params =
                     (PagedViewCellLayout.LayoutParams) icon.getLayoutParams();
@@ -684,6 +685,7 @@
                     new PagedViewCellLayout.LayoutParams(0, 0, 4, 1));
         }
         layout.createHardwareLayers();
+        */
     }
 
     /*
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index c27e375..1d7b34d 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -33,11 +33,15 @@
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Process;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.LruCache;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -54,6 +58,99 @@
 import java.util.Iterator;
 import java.util.List;
 
+/**
+ * A simple callback interface which also provides the results of the task.
+ */
+interface AsyncTaskCallback {
+    void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data);
+}
+/**
+ * The data needed to perform either of the custom AsyncTasks.
+ */
+class AsyncTaskPageData {
+    AsyncTaskPageData(int p, ArrayList<Object> l, ArrayList<Bitmap> si, AsyncTaskCallback bgR,
+            AsyncTaskCallback postR) {
+        page = p;
+        items = l;
+        srcImages = si;
+        images = new ArrayList<Bitmap>();
+        cellWidth = cellHeight = -1;
+        doInBackgroundCallback = bgR;
+        postExecuteCallback = postR;
+    }
+    AsyncTaskPageData(int p, ArrayList<Object> l, int cw, int ch, AsyncTaskCallback bgR,
+            AsyncTaskCallback postR) {
+        page = p;
+        items = l;
+        images = new ArrayList<Bitmap>();
+        cellWidth = cw;
+        cellHeight = ch;
+        doInBackgroundCallback = bgR;
+        postExecuteCallback = postR;
+    }
+    int page;
+    ArrayList<Object> items;
+    ArrayList<Bitmap> srcImages;
+    ArrayList<Bitmap> images;
+    int cellWidth;
+    int cellHeight;
+    AsyncTaskCallback doInBackgroundCallback;
+    AsyncTaskCallback postExecuteCallback;
+}
+/**
+ * A generic template for an async task used in AppsCustomize.
+ */
+class AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTaskPageData> {
+    AppsCustomizeAsyncTask(int p, AppsCustomizePagedView.ContentType t) {
+        page = p;
+        pageContentType = t;
+        threadPriority = Process.THREAD_PRIORITY_DEFAULT;
+    }
+    @Override
+    protected AsyncTaskPageData doInBackground(AsyncTaskPageData... params) {
+        if (params.length != 1) return null;
+        // Load each of the widget previews in the background
+        params[0].doInBackgroundCallback.run(this, params[0]);
+        return params[0];
+    }
+    @Override
+    protected void onPostExecute(AsyncTaskPageData result) {
+        // All the widget previews are loaded, so we can just callback to inflate the page
+        result.postExecuteCallback.run(this, result);
+    }
+
+    void setThreadPriority(int p) {
+        threadPriority = p;
+    }
+    void syncThreadPriority() {
+        Process.setThreadPriority(threadPriority);
+    }
+
+    // The page that this async task is associated with
+    int page;
+    AppsCustomizePagedView.ContentType pageContentType;
+    int threadPriority;
+}
+/**
+ * An AsyncTask that loads widget previews from package manager in the background.
+ */
+class LoadWidgetPreviewsTask extends AppsCustomizeAsyncTask {
+    LoadWidgetPreviewsTask(int p, AppsCustomizePagedView.ContentType t) {
+        super(p, t);
+    }
+}
+/**
+ * An AsyncTask that generates holgoraphic outlines for a specified set of bitmaps.
+ */
+class GenerateHoloOutlinesTask extends AppsCustomizeAsyncTask {
+    GenerateHoloOutlinesTask(int p, AppsCustomizePagedView.ContentType t) {
+        super(p, t);
+    }
+}
+
+/**
+ * The Apps/Customize page that displays all the applications, widgets, and shortcuts.
+ */
 public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
         AllAppsView, View.OnClickListener, DragSource {
     static final String LOG_TAG = "AppsCustomizePagedView";
@@ -78,9 +175,8 @@
     private List<Object> mWidgets;
 
     // Caching
+    private Canvas mCanvas;
     private Drawable mDefaultWidgetBackground;
-    private final int sWidgetPreviewCacheSize = 1 * 1024 * 1024; // 1 MiB
-    private LruCache<Object, Bitmap> mWidgetPreviewCache;
     private IconCache mIconCache;
 
     // Dimens
@@ -92,10 +188,9 @@
     private final float sWidgetPreviewIconPaddingPercentage = 0.25f;
     private PagedViewCellLayout mWidgetSpacingLayout;
 
-    // Animations
-    private final float ANIMATION_SCALE = 0.5f;
-    private final int TRANSLATE_ANIM_DURATION = 400;
-    private final int DROP_ANIM_DURATION = 200;
+    // Previews & outlines
+    ArrayList<AppsCustomizeAsyncTask> mRunningTasks;
+    private HolographicOutlineHelper mHolographicOutlineHelper;
 
     public AppsCustomizePagedView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -105,11 +200,9 @@
         mApps = new ArrayList<ApplicationInfo>();
         mWidgets = new ArrayList<Object>();
         mIconCache = ((LauncherApplication) context.getApplicationContext()).getIconCache();
-        mWidgetPreviewCache = new LruCache<Object, Bitmap>(sWidgetPreviewCacheSize) {
-            protected int sizeOf(Object key, Bitmap value) {
-                return value.getByteCount();
-            }
-        };
+        mHolographicOutlineHelper = new HolographicOutlineHelper();
+        mCanvas = new Canvas();
+        mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>();
 
         // Save the default widget preview background
         Resources resources = context.getResources();
@@ -288,9 +381,11 @@
         final Drawable icon = tv.getCompoundDrawables()[1];
         Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
                 Bitmap.Config.ARGB_8888);
-        Canvas c = new Canvas(b);
-        c.translate((v.getWidth() - icon.getIntrinsicWidth()) / 2, v.getPaddingTop());
-        icon.draw(c);
+        mCanvas.setBitmap(b);
+        mCanvas.save();
+        mCanvas.translate((v.getWidth() - icon.getIntrinsicWidth()) / 2, v.getPaddingTop());
+        icon.draw(mCanvas);
+        mCanvas.restore();
 
         // Compose the visible rect of the drag image
         Rect dragRect = null;
@@ -334,8 +429,10 @@
             // the shortcut icon) to a new drag bitmap that clips the non-icon space.
             b = Bitmap.createBitmap(mWidgetPreviewIconPaddedDimension,
                     mWidgetPreviewIconPaddedDimension, Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(b);
-            preview.draw(c);
+            mCanvas.setBitmap(b);
+            mCanvas.save();
+            preview.draw(mCanvas);
+            mCanvas.restore();
             createItemInfo.spanX = createItemInfo.spanY = 1;
         }
 
@@ -424,6 +521,10 @@
         invalidatePageData();
     }
 
+    public boolean isContentType(ContentType type) {
+        return (mContentType == type);
+    }
+
     /*
      * Apps PagedView implementation
      */
@@ -468,12 +569,13 @@
         int endIndex = Math.min(startIndex + numCells, mApps.size());
         PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
         layout.removeAllViewsOnPage();
+        ArrayList<Object> items = new ArrayList<Object>();
+        ArrayList<Bitmap> images = new ArrayList<Bitmap>();
         for (int i = startIndex; i < endIndex; ++i) {
             ApplicationInfo info = mApps.get(i);
             PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(
                     R.layout.apps_customize_application, layout, false);
-            icon.applyFromApplicationInfo(
-                    info, mPageViewIconCache, true, (numPages > 1));
+            icon.applyFromApplicationInfo(info, true, mHolographicOutlineHelper);
             icon.setOnClickListener(this);
             icon.setOnLongClickListener(this);
             icon.setOnTouchListener(this);
@@ -482,12 +584,155 @@
             int x = index % mCellCountX;
             int y = index / mCellCountX;
             layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
+
+            items.add(info);
+            images.add(info.iconBitmap);
         }
 
         // Create the hardware layers
         layout.allowHardwareLayerCreation();
         layout.createHardwareLayers();
+
+        prepareGenerateHoloOutlinesTask(page, items, images);
     }
+
+    /**
+     * Return the appropriate thread priority for loading for a given page (we give the current
+     * page much higher priority)
+     */
+    private int getThreadPriorityForPage(int page) {
+        // TODO-APPS_CUSTOMIZE: detect number of cores and set thread priorities accordingly below
+        int pageDiff = Math.abs(page - mCurrentPage);
+        if (pageDiff <= 0) {
+            // return Process.THREAD_PRIORITY_DEFAULT;
+            return Process.THREAD_PRIORITY_MORE_FAVORABLE;
+        } else if (pageDiff <= 1) {
+            // return Process.THREAD_PRIORITY_BACKGROUND;
+            return Process.THREAD_PRIORITY_DEFAULT;
+        } else {
+            // return Process.THREAD_PRIORITY_LOWEST;
+            return Process.THREAD_PRIORITY_DEFAULT;
+        }
+    }
+    /**
+     * Creates and executes a new AsyncTask to load a page of widget previews.
+     */
+    private void prepareLoadWidgetPreviewsTask(int page, ArrayList<Object> widgets,
+            int cellWidth, int cellHeight) {
+        // Prune all tasks that are no longer needed
+        Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
+        while (iter.hasNext()) {
+            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
+            int taskPage = task.page;
+            if (taskPage < (mCurrentPage - 2) || taskPage > (mCurrentPage + 2)) {
+                task.cancel(false);
+                iter.remove();
+            } else {
+                task.setThreadPriority(getThreadPriorityForPage(taskPage));
+            }
+        }
+
+        AsyncTaskPageData pageData = new AsyncTaskPageData(page, widgets, cellWidth, cellHeight,
+            new AsyncTaskCallback() {
+                @Override
+                public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
+                    // Ensure that this task starts running at the correct priority
+                    task.syncThreadPriority();
+
+                    // Load each of the widget/shortcut previews
+                    ArrayList<Object> items = data.items;
+                    ArrayList<Bitmap> images = data.images;
+                    int count = items.size();
+                    int cellWidth = data.cellWidth;
+                    int cellHeight = data.cellHeight;
+                    for (int i = 0; i < count && !task.isCancelled(); ++i) {
+                        // Before work on each item, ensure that this task is running at the correct
+                        // priority
+                        task.syncThreadPriority();
+
+                        Object rawInfo = items.get(i);
+                        if (rawInfo instanceof AppWidgetProviderInfo) {
+                            AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo;
+                            int[] cellSpans = CellLayout.rectToCell(getResources(),
+                                    info.minWidth, info.minHeight, null);
+                            images.add(getWidgetPreviewInBackground(info, cellSpans[0],cellSpans[1],
+                                    cellWidth, cellHeight));
+                        } else if (rawInfo instanceof ResolveInfo) {
+                            // Fill in the shortcuts information
+                            ResolveInfo info = (ResolveInfo) rawInfo;
+                            images.add(getShortcutPreviewInBackground(info, cellWidth, cellHeight));
+                        }
+                    }
+                }
+            },
+            new AsyncTaskCallback() {
+                @Override
+                public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
+                    mRunningTasks.remove(task);
+                    if (task.page > getPageCount()) return;
+                    if (task.pageContentType != mContentType) return;
+                    onSyncWidgetPageItems(data);
+                }
+        });
+
+        // Ensure that the task is appropriately prioritized and runs in parallel
+        LoadWidgetPreviewsTask t = new LoadWidgetPreviewsTask(page, mContentType);
+        t.setThreadPriority(getThreadPriorityForPage(page));
+        t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pageData);
+        mRunningTasks.add(t);
+    }
+    /**
+     * Creates and executes a new AsyncTask to load the outlines for a page of content.
+     */
+    private void prepareGenerateHoloOutlinesTask(int page, ArrayList<Object> items,
+            ArrayList<Bitmap> images) {
+        AsyncTaskPageData pageData = new AsyncTaskPageData(page, items, images,
+            new AsyncTaskCallback() {
+                @Override
+                public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
+                    // Ensure that this task starts running at the correct priority
+                    task.syncThreadPriority();
+
+                    ArrayList<Bitmap> images = data.images;
+                    ArrayList<Bitmap> srcImages = data.srcImages;
+                    int count = srcImages.size();
+                    Canvas c = new Canvas();
+                    for (int i = 0; i < count && !task.isCancelled(); ++i) {
+                        // Before work on each item, ensure that this task is running at the correct
+                        // priority
+                        task.syncThreadPriority();
+
+                        Bitmap b = srcImages.get(i);
+                        Bitmap outline = Bitmap.createBitmap(b.getWidth(), b.getHeight(),
+                                Bitmap.Config.ARGB_8888);
+
+                        c.setBitmap(outline);
+                        c.save();
+                        c.drawBitmap(b, 0, 0, null);
+                        c.restore();
+
+                        images.add(outline);
+                    }
+                }
+            },
+            new AsyncTaskCallback() {
+                @Override
+                public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
+                    mRunningTasks.remove(task);
+                    if (task.page > getPageCount()) return;
+                    if (task.pageContentType != mContentType) return;
+                    onHolographicPageItemsLoaded(data);
+                }
+            });
+
+        // Ensure that the outline task always runs in the background, serially
+        GenerateHoloOutlinesTask t =
+            new GenerateHoloOutlinesTask(page, mContentType);
+        t.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        t.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, pageData);
+        mRunningTasks.add(t);
+    }
+
     /*
      * Widgets PagedView implementation
      */
@@ -506,27 +751,19 @@
         layout.measure(widthSpec, heightSpec);
         setVisibilityOnChildren(layout, View.VISIBLE);
     }
-    private void renderDrawableToBitmap(Drawable d, Bitmap bitmap, int x, int y, int w, int h,
+    private synchronized void renderDrawableToBitmap(Drawable d, Bitmap bitmap, int x, int y, int w, int h,
             float scaleX, float scaleY) {
         if (bitmap != null) {
-            Canvas c = new Canvas();
-            c.setBitmap(bitmap);
-            c.save();
+            Canvas c = new Canvas(bitmap);
             c.scale(scaleX, scaleY);
             Rect oldBounds = d.copyBounds();
             d.setBounds(x, y, x + w, y + h);
             d.draw(c);
             d.setBounds(oldBounds); // Restore the bounds
-            c.restore();
         }
     }
-    private FastBitmapDrawable getShortcutPreview(ResolveInfo info, int cellWidth, int cellHeight) {
-        // Return the cached version if necessary
-        Bitmap cachedBitmap = mWidgetPreviewCache.get(info);
-        if (cachedBitmap != null) {
-            return new FastBitmapDrawable(cachedBitmap);
-        }
-
+    private Bitmap getShortcutPreviewInBackground(ResolveInfo info, int cellWidth,
+            int cellHeight) {
         Resources resources = mLauncher.getResources();
         int iconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size);
         // We only need to make it wide enough so as not allow the preview to be scaled
@@ -537,18 +774,10 @@
         Bitmap preview = Bitmap.createBitmap(expectedWidth, expectedHeight, Config.ARGB_8888);
         Drawable icon = mIconCache.getFullResIcon(info, mPackageManager);
         renderDrawableToBitmap(icon, preview, 0, 0, iconSize, iconSize, 1f, 1f);
-        FastBitmapDrawable iconDrawable = new FastBitmapDrawable(preview);
-        iconDrawable.setBounds(0, 0, expectedWidth, expectedHeight);
-        mWidgetPreviewCache.put(info, preview);
-        return iconDrawable;
+        return preview;
     }
-    private FastBitmapDrawable getWidgetPreview(AppWidgetProviderInfo info, int cellHSpan,
-            int cellVSpan, int cellWidth, int cellHeight) {
-        // Return the cached version if necessary
-        Bitmap cachedBitmap = mWidgetPreviewCache.get(info);
-        if (cachedBitmap != null) {
-            return new FastBitmapDrawable(cachedBitmap);
-        }
+    private Bitmap getWidgetPreviewInBackground(AppWidgetProviderInfo info,
+            int cellHSpan, int cellVSpan, int cellWidth, int cellHeight) {
 
         // Calculate the size of the drawable
         cellHSpan = Math.max(mMinWidgetSpan, Math.min(mMaxWidgetSpan, cellHSpan));
@@ -564,7 +793,7 @@
         // Load the preview image if possible
         String packageName = info.provider.getPackageName();
         Drawable drawable = null;
-        FastBitmapDrawable newDrawable = null;
+        Bitmap preview = null;
         if (info.previewImage != 0) {
             drawable = mPackageManager.getDrawable(packageName, info.previewImage, null);
             if (drawable == null) {
@@ -585,11 +814,8 @@
                     newWidth = (int) (imageWidth * ((float) expectedHeight / imageHeight));
                 }
 
-                Bitmap preview = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888);
+                preview = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888);
                 renderDrawableToBitmap(drawable, preview, 0, 0, newWidth, newHeight, 1f, 1f);
-                newDrawable = new FastBitmapDrawable(preview);
-                newDrawable.setBounds(0, 0, newWidth, newHeight);
-                mWidgetPreviewCache.put(info, preview);
             }
         }
 
@@ -609,7 +835,7 @@
                 expectedHeight = cellWidth;
             }
 
-            Bitmap preview = Bitmap.createBitmap(expectedWidth, expectedHeight, Config.ARGB_8888);
+            preview = Bitmap.createBitmap(expectedWidth, expectedHeight, Config.ARGB_8888);
             renderDrawableToBitmap(mDefaultWidgetBackground, preview, 0, 0, expectedWidth,
                     expectedHeight, 1f,1f);
 
@@ -622,12 +848,8 @@
                 int offset = (int) (iconSize * sWidgetPreviewIconPaddingPercentage);
                 renderDrawableToBitmap(icon, preview, offset, offset, iconSize, iconSize, 1f, 1f);
             } catch (Resources.NotFoundException e) {}
-
-            newDrawable = new FastBitmapDrawable(preview);
-            newDrawable.setBounds(0, 0, expectedWidth, expectedHeight);
-            mWidgetPreviewCache.put(info, preview);
         }
-        return newDrawable;
+        return preview;
     }
     public void syncWidgetPages() {
         // Ensure that we have the right number of pages
@@ -642,19 +864,34 @@
         }
     }
     public void syncWidgetPageItems(int page) {
+        // Calculate the dimensions of each cell we are giving to each widget
+        ArrayList<Object> widgets = new ArrayList<Object>();
+        int cellWidth = ((mWidgetSpacingLayout.getContentWidth()
+                - mPageLayoutPaddingLeft - mPageLayoutPaddingRight
+                - ((mWidgetCountX - 1) * mWidgetWidthGap)) / mWidgetCountX);
+        int cellHeight = ((mWidgetSpacingLayout.getContentHeight()
+                - mPageLayoutPaddingTop - mPageLayoutPaddingBottom
+                - ((mWidgetCountY - 1) * mWidgetHeightGap)) / mWidgetCountY);
+
+        int numWidgetsPerPage = mWidgetCountX * mWidgetCountY;
+        int offset = page * numWidgetsPerPage;
+        for (int i = offset; i < Math.min(offset + numWidgetsPerPage, mWidgets.size()); ++i) {
+            widgets.add(mWidgets.get(i));
+        }
+
+        prepareLoadWidgetPreviewsTask(page, widgets, cellWidth, cellHeight);
+    }
+    private void onSyncWidgetPageItems(AsyncTaskPageData data) {
+        int page = data.page;
         PagedViewGridLayout layout = (PagedViewGridLayout) getChildAt(page);
         layout.removeAllViews();
 
-        // Calculate the dimensions of each cell we are giving to each widget
-        int numWidgetsPerPage = mWidgetCountX * mWidgetCountY;
-        int numPages = (int) Math.ceil(mWidgets.size() / (float) numWidgetsPerPage);
-        int offset = page * numWidgetsPerPage;
-        int cellWidth = ((mWidgetSpacingLayout.getContentWidth() - mPageLayoutWidthGap
-                - ((mWidgetCountX - 1) * mWidgetWidthGap)) / mWidgetCountX);
-        int cellHeight = ((mWidgetSpacingLayout.getContentHeight() - mPageLayoutHeightGap
-                - ((mWidgetCountY - 1) * mWidgetHeightGap)) / mWidgetCountY);
-        for (int i = 0; i < Math.min(numWidgetsPerPage, mWidgets.size() - offset); ++i) {
-            Object rawInfo = mWidgets.get(offset + i);
+        ArrayList<Object> items = data.items;
+        int count = items.size();
+        int cellWidth = data.cellWidth;
+        int cellHeight = data.cellHeight;
+        for (int i = 0; i < count; ++i) {
+            Object rawInfo = items.get(i);
             PendingAddItemInfo createItemInfo = null;
             PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate(
                     R.layout.apps_customize_widget, layout, false);
@@ -662,12 +899,11 @@
                 // Fill in the widget information
                 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo;
                 createItemInfo = new PendingAddWidgetInfo(info, null, null);
-                final int[] cellSpans = CellLayout.rectToCell(getResources(), info.minWidth,
-                        info.minHeight, null);
-                FastBitmapDrawable preview = getWidgetPreview(info, cellSpans[0], cellSpans[1],
-                        cellWidth, cellHeight);
+                int[] cellSpans = CellLayout.rectToCell(getResources(),
+                        info.minWidth, info.minHeight, null);
+                FastBitmapDrawable preview = new FastBitmapDrawable(data.images.get(i));
                 widget.applyFromAppWidgetProviderInfo(info, preview, -1, cellSpans, 
-                        mPageViewIconCache, (numPages > 1));
+                        mHolographicOutlineHelper);
                 widget.setTag(createItemInfo);
             } else if (rawInfo instanceof ResolveInfo) {
                 // Fill in the shortcuts information
@@ -676,9 +912,9 @@
                 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
                 createItemInfo.componentName = new ComponentName(info.activityInfo.packageName,
                         info.activityInfo.name);
-                FastBitmapDrawable preview = getShortcutPreview(info, cellWidth, cellHeight);
-                widget.applyFromResolveInfo(mPackageManager, info, preview, mPageViewIconCache, 
-                        (numPages > 1));
+                FastBitmapDrawable preview = new FastBitmapDrawable(data.images.get(i));
+                widget.applyFromResolveInfo(mPackageManager, info, preview,
+                        mHolographicOutlineHelper);
                 widget.setTag(createItemInfo);
             }
             widget.setOnClickListener(this);
@@ -694,6 +930,31 @@
             lp.topMargin = (iy * cellHeight) + (iy * mWidgetHeightGap);
             layout.addView(widget, lp);
         }
+
+        invalidate();
+        forceUpdateAdjacentPagesAlpha();
+        prepareGenerateHoloOutlinesTask(data.page, data.items, data.images);
+    }
+    private void onHolographicPageItemsLoaded(AsyncTaskPageData data) {
+        // Invalidate early to short-circuit children invalidates
+        invalidate();
+
+        int page = data.page;
+        ViewGroup layout = (ViewGroup) getChildAt(page);
+        if (layout instanceof PagedViewCellLayout) {
+            PagedViewCellLayout cl = (PagedViewCellLayout) layout;
+            int count = cl.getPageChildCount();
+            for (int i = 0; i < count; ++i) {
+                PagedViewIcon icon = (PagedViewIcon) cl.getChildOnPageAt(i);
+                icon.setHolographicOutline(data.images.get(i));
+            }
+        } else {
+            int count = layout.getChildCount();
+            for (int i = 0; i < count; ++i) {
+                View v = layout.getChildAt(i);
+                ((PagedViewWidget) v).setHolographicOutline(data.images.get(i));
+            }
+        }
     }
 
     @Override
@@ -820,7 +1081,6 @@
             int removeIndex = findAppByComponent(mApps, info);
             if (removeIndex > -1) {
                 mApps.remove(removeIndex);
-                mPageViewIconCache.removeOutline(new PagedViewIconCache.Key(info));
             }
         }
     }
@@ -883,4 +1143,16 @@
     protected int getPageWidthForScrollingIndicator() {
         return getPageContentWidth();
     }
+
+    /*
+     * We load an extra page on each side to prevent flashes from scrolling and loading of the
+     * widget previews in the background with the AsyncTasks.
+     */
+    protected int getAssociatedLowerPageBound(int page) {
+        return Math.max(0, page - 2);
+    }
+    protected int getAssociatedUpperPageBound(int page) {
+        final int count = getChildCount();
+        return Math.min(page + 2, count - 1);
+    }
 }
diff --git a/src/com/android/launcher2/AppsCustomizeTabHost.java b/src/com/android/launcher2/AppsCustomizeTabHost.java
index 51db66c..87c154b 100644
--- a/src/com/android/launcher2/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher2/AppsCustomizeTabHost.java
@@ -17,6 +17,8 @@
 package com.android.launcher2;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.AttributeSet;
@@ -24,6 +26,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
 import android.widget.TabHost;
 import android.widget.TabWidget;
 import android.widget.TextView;
@@ -121,7 +124,26 @@
 
     @Override
     public void onTabChanged(String tabId) {
-        mAppsCustomizePane.setContentType(getContentTypeForTabTag(tabId));
+        final AppsCustomizePagedView.ContentType type = getContentTypeForTabTag(tabId);
+        if (!mAppsCustomizePane.isContentType(type)) {
+            // Animate the changing of the tab content by fading pages in and out
+            final Resources res = getResources();
+            final int duration = res.getInteger(R.integer.config_tabTransitionDuration);
+
+            ObjectAnimator anim = ObjectAnimator.ofFloat(mAppsCustomizePane, "alpha", 0f);
+            anim.setDuration(duration);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(android.animation.Animator animation) {
+                    mAppsCustomizePane.setContentType(type);
+
+                    ObjectAnimator anim = ObjectAnimator.ofFloat(mAppsCustomizePane, "alpha", 1f);
+                    anim.setDuration(duration);
+                    anim.start();
+                }
+            });
+            anim.start();
+        }
     }
 
     /**
diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
index b2a7603..c98e885 100644
--- a/src/com/android/launcher2/CustomizePagedView.java
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -249,7 +249,6 @@
         Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
 
         // Update the widgets/shortcuts to reflect changes in the set of available apps
-        mPageViewIconCache.retainAllApps(list);
         invalidatePageData();
     }
 
@@ -291,7 +290,6 @@
             int removeIndex = findAppByComponent(mApps, info);
             if (removeIndex > -1) {
                 mApps.remove(removeIndex);
-                mPageViewIconCache.removeOutline(new PagedViewIconCache.Key(info));
             }
         }
     }
@@ -362,8 +360,6 @@
 
         ArrayList<ResolveInfo> retainShortcutList = new ArrayList<ResolveInfo>(mShortcutList);
         retainShortcutList.addAll(mWallpaperList);
-        mPageViewIconCache.retainAllShortcuts(retainShortcutList);
-        mPageViewIconCache.retainAllAppWidgets(mWidgetList);
         invalidatePageData();
     }
 
@@ -944,6 +940,7 @@
     }
 
     private void syncWidgetPages() {
+        /*
         if (mWidgetList == null) return;
 
         // we need to repopulate with the LinearLayout layout for the widget pages
@@ -959,9 +956,11 @@
                     LinearLayout.LayoutParams.WRAP_CONTENT,
                     LinearLayout.LayoutParams.MATCH_PARENT));
         }
+        */
     }
 
     private void syncWidgetPageItems(int page) {
+        /*
         // ensure that we have the right number of items on the pages
         LinearLayout layout = (LinearLayout) getChildAt(page);
         final ArrayList<AppWidgetProviderInfo> list = mWidgetPages.get(page);
@@ -980,7 +979,7 @@
                     R.layout.customize_paged_view_widget, layout, false);
 
             l.applyFromAppWidgetProviderInfo(info, icon, mMaxWidgetWidth, cellSpans,
-                    mPageViewIconCache, createHolographicOutlines);
+                    null, createHolographicOutlines);
             l.setTag(createItemInfo);
             l.setOnClickListener(this);
             l.setOnTouchListener(this);
@@ -988,9 +987,11 @@
 
             layout.addView(l);
         }
+        */
     }
 
     private void syncWallpaperPages() {
+        /*
         if (mWallpaperList == null) return;
 
         // We need to repopulate the LinearLayout for the wallpaper pages
@@ -1007,9 +1008,11 @@
                     LinearLayout.LayoutParams.WRAP_CONTENT,
                     LinearLayout.LayoutParams.MATCH_PARENT));
         }
+        */
     }
 
     private void syncWallpaperPageItems(int page) {
+        /*
         // Load the items on to the pages
         LinearLayout layout = (LinearLayout) getChildAt(page);
         layout.removeAllViews();
@@ -1026,15 +1029,17 @@
             PagedViewWidget l = (PagedViewWidget) mInflater.inflate(
                     R.layout.customize_paged_view_wallpaper, layout, false);
             l.applyFromWallpaperInfo(info, mPackageManager, icon, mMaxWidgetWidth,
-                    mPageViewIconCache, createHolographicOutlines);
+                    null, createHolographicOutlines);
             l.setTag(info);
             l.setOnClickListener(this);
 
             layout.addView(l);
         }
+        */
     }
 
     private void syncListPages(List<ResolveInfo> list) {
+        /*
         // we need to repopulate with PagedViewCellLayouts
         removeAllViews();
 
@@ -1045,9 +1050,11 @@
             setupPage(layout);
             addView(layout);
         }
+        */
     }
 
     private void syncListPageItems(int page, List<ResolveInfo> list) {
+        /*
         // ensure that we have the right number of items on the pages
         final int numPages = getPageCount();
         final int numCells = mCellCountX * mCellCountY;
@@ -1063,7 +1070,7 @@
 
             PagedViewIcon icon = (PagedViewIcon) mInflater.inflate(
                     R.layout.customize_paged_view_item, layout, false);
-            icon.applyFromResolveInfo(info, mPackageManager, mPageViewIconCache,
+            icon.applyFromResolveInfo(info, mPackageManager, null,
                     ((LauncherApplication) mLauncher.getApplication()).getIconCache(),
                     createHolographicOutlines);
             switch (mCustomizationType) {
@@ -1089,9 +1096,11 @@
             setupPage(layout);
             layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
         }
+        */
     }
 
     private void syncAppPages() {
+        /*
         if (mApps == null) return;
 
         // We need to repopulate with PagedViewCellLayouts
@@ -1104,9 +1113,11 @@
             setupPage(layout);
             addView(layout);
         }
+        */
     }
 
     private void syncAppPageItems(int page) {
+        /*
         if (mApps == null) return;
 
         // ensure that we have the right number of items on the pages
@@ -1123,7 +1134,7 @@
             PagedViewIcon icon = (PagedViewIcon) mInflater.inflate(
                     R.layout.all_apps_paged_view_application, layout, false);
             icon.applyFromApplicationInfo(
-                    info, mPageViewIconCache, true, createHolographicOutlines);
+                    info, null, true, createHolographicOutlines);
             icon.setOnClickListener(this);
             icon.setOnTouchListener(this);
             icon.setOnLongClickListener(this);
@@ -1134,6 +1145,7 @@
             setupPage(layout);
             layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
         }
+        */
     }
 
     @Override
@@ -1148,6 +1160,7 @@
 
     @Override
     public void syncPages() {
+        /*
         boolean enforceMinimumPagedWidths = false;
         boolean centerPagedViewCellLayouts = false;
         switch (mCustomizationType) {
@@ -1203,10 +1216,12 @@
                 forceUpdateAdjacentPagesAlpha();
             }
         });
+        */
     }
 
     @Override
     public void syncPageItems(int page) {
+        /*
         switch (mCustomizationType) {
         case WidgetCustomization:
             syncWidgetPageItems(page);
@@ -1221,6 +1236,7 @@
             syncAppPageItems(page);
             break;
         }
+        */
     }
 
     int getPageContentWidth() {
diff --git a/src/com/android/launcher2/HolographicOutlineHelper.java b/src/com/android/launcher2/HolographicOutlineHelper.java
index 80548fb..d2e1e7e 100644
--- a/src/com/android/launcher2/HolographicOutlineHelper.java
+++ b/src/com/android/launcher2/HolographicOutlineHelper.java
@@ -81,15 +81,15 @@
      * Returns the interpolated holographic highlight alpha for the effect we want when scrolling
      * pages.
      */
-    public float highlightAlphaInterpolator(float r) {
-        float maxAlpha = 0.8f;
+    public static float highlightAlphaInterpolator(float r) {
+        float maxAlpha = 0.6f;
         return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f);
     }
 
     /**
      * Returns the interpolated view alpha for the effect we want when scrolling pages.
      */
-    public float viewAlphaInterpolator(float r) {
+    public static float viewAlphaInterpolator(float r) {
         final float pivot = 0.95f;
         if (r < pivot) {
             return (float) Math.pow(r / pivot, 1.5f);
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index ca0847e..9991d9d 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -139,11 +139,6 @@
     protected int mChoiceMode;
     private ActionMode mActionMode;
 
-    // NOTE: This is a shared icon cache across all the PagedViews.  Currently it is only used in
-    // AllApps and Customize, and allows them to share holographic icons for the application view
-    // (which is in both).
-    protected static PagedViewIconCache mPageViewIconCache = new PagedViewIconCache();
-
     // If true, syncPages and syncPageItems will be called to refresh pages
     protected boolean mContentIsRefreshable = true;
 
@@ -170,6 +165,9 @@
     private static final int sScrollIndicatorFastFadeOutDuration = 50;
     private static final int sScrollIndicatorFadeOutDuration = 650;
 
+    // If set, will defer loading associated pages until the scrolling settles
+    private boolean mDeferLoadAssociatedPagesAfterScroll;
+
     public interface PageSwitchListener {
         void onPageSwitch(View newPage, int newPageIndex);
     }
@@ -375,6 +373,13 @@
             mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
             mNextPage = INVALID_PAGE;
             notifyPageSwitchListener();
+
+            // Load the associated pages if necessary
+            if (mDeferLoadAssociatedPagesAfterScroll) {
+                loadAssociatedPages(mCurrentPage);
+                mDeferLoadAssociatedPagesAfterScroll = false;
+            }
+
             // We don't want to trigger a page end moving unless the page has settled
             // and the user has stopped scrolling
             if (mTouchState == TOUCH_STATE_REST) {
@@ -1380,8 +1385,13 @@
         if (!mScroller.isFinished()) mScroller.abortAnimation();
         mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
 
-        // only load some associated pages
-        loadAssociatedPages(mNextPage);
+        // Load associated pages immediately if someone else is handling the scroll, otherwise defer
+        // loading associated pages until the scroll settles
+        if (mDeferScrollUpdate) {
+            loadAssociatedPages(mNextPage);
+        } else {
+            mDeferLoadAssociatedPagesAfterScroll = true;
+        }
         notifyPageSwitchListener();
         invalidate();
     }
diff --git a/src/com/android/launcher2/PagedViewCellLayout.java b/src/com/android/launcher2/PagedViewCellLayout.java
index d7f8784..9c37c01 100644
--- a/src/com/android/launcher2/PagedViewCellLayout.java
+++ b/src/com/android/launcher2/PagedViewCellLayout.java
@@ -104,8 +104,8 @@
 
     @Override
     public void setAlpha(float alpha) {
-        mChildren.setAlpha(alpha);
-        mHolographicChildren.setAlpha(1.0f - alpha);
+        mChildren.setAlpha(HolographicOutlineHelper.viewAlphaInterpolator(alpha));
+        mHolographicChildren.setAlpha(HolographicOutlineHelper.highlightAlphaInterpolator(alpha));
     }
 
     void destroyHardwareLayers() {
@@ -138,15 +138,6 @@
         }
     }
 
-    /** Syncs the holographic icon views to the child icon views */
-    public void reloadHolographicIcons(boolean createHolographicOutlines) {
-        if (createHolographicOutlines) {
-            mChildren.loadHolographicOutlines();
-        } else {
-            mChildren.clearHolographicOutlines();
-        }
-    }
-
     public boolean addViewToCellLayout(View child, int index, int childId,
             PagedViewCellLayout.LayoutParams params) {
         final PagedViewCellLayout.LayoutParams lp = params;
diff --git a/src/com/android/launcher2/PagedViewCellLayoutChildren.java b/src/com/android/launcher2/PagedViewCellLayoutChildren.java
index 1afdd03..0907c60 100644
--- a/src/com/android/launcher2/PagedViewCellLayoutChildren.java
+++ b/src/com/android/launcher2/PagedViewCellLayoutChildren.java
@@ -168,26 +168,4 @@
             }
         }
     }
-
-    public void loadHolographicOutlines() {
-        int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            View view = getChildAt(i);
-            if (view instanceof PagedViewIcon) {
-                PagedViewIcon icon = (PagedViewIcon) view;
-                icon.loadHolographicIcon();
-            }
-        }
-    }
-
-    public void clearHolographicOutlines() {
-        int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            View view = getChildAt(i);
-            if (view instanceof PagedViewIcon) {
-                PagedViewIcon icon = (PagedViewIcon) view;
-                icon.clearHolographicIcon();
-            }
-        }
-    }
 }
diff --git a/src/com/android/launcher2/PagedViewIcon.java b/src/com/android/launcher2/PagedViewIcon.java
index 30f0b1d..a48a4ef 100644
--- a/src/com/android/launcher2/PagedViewIcon.java
+++ b/src/com/android/launcher2/PagedViewIcon.java
@@ -45,14 +45,10 @@
 
     // holographic outline
     private final Paint mPaint = new Paint();
-    private static HolographicOutlineHelper sHolographicOutlineHelper;
     private Bitmap mCheckedOutline;
     private Bitmap mHolographicOutline;
     private Bitmap mIcon;
 
-    private PagedViewIconCache.Key mIconCacheKey;
-    private PagedViewIconCache mIconCache;
-
     private int mAlpha = 255;
     private int mHolographicAlpha;
 
@@ -62,42 +58,8 @@
     private int mCheckedFadeInDuration;
     private int mCheckedFadeOutDuration;
 
-    // Highlight colors
-    private int mHoloBlurColor;
-    private int mHoloOutlineColor;
-
     HolographicPagedViewIcon mHolographicOutlineView;
-
-    private static final HandlerThread sWorkerThread = new HandlerThread("pagedviewicon-helper");
-    static {
-        sWorkerThread.start();
-    }
-
-    private static final int MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE = 1;
-
-    private static final Handler sWorker = new Handler(sWorkerThread.getLooper()) {
-        private DeferredHandler mHandler = new DeferredHandler();
-        private Paint mPaint = new Paint();
-        public void handleMessage(Message msg) {
-            final PagedViewIcon icon = (PagedViewIcon) msg.obj;
-
-            final Bitmap holographicOutline = Bitmap.createBitmap(
-                    icon.mIcon.getWidth(), icon.mIcon.getHeight(), Bitmap.Config.ARGB_8888);
-            Canvas holographicOutlineCanvas = new Canvas(holographicOutline);
-            holographicOutlineCanvas.drawBitmap(icon.mIcon, 0, 0, mPaint);
-
-            sHolographicOutlineHelper.applyThickExpensiveOutlineWithBlur(holographicOutline,
-                    holographicOutlineCanvas, icon.mHoloBlurColor, icon.mHoloOutlineColor);
-
-            mHandler.post(new Runnable() {
-                public void run() {
-                    icon.mHolographicOutline = holographicOutline;
-                    icon.mIconCache.addOutline(icon.mIconCacheKey, holographicOutline);
-                    icon.getHolographicOutlineView().invalidate();
-                }
-            });
-        }
-    };
+    private HolographicOutlineHelper mHolographicOutlineHelper;
 
     public PagedViewIcon(Context context) {
         this(context, null);
@@ -110,15 +72,6 @@
     public PagedViewIcon(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedViewIcon, defStyle, 0);
-        mHoloBlurColor = a.getColor(R.styleable.PagedViewIcon_blurColor, 0);
-        mHoloOutlineColor = a.getColor(R.styleable.PagedViewIcon_outlineColor, 0);
-        a.recycle();
-
-        if (sHolographicOutlineHelper == null) {
-            sHolographicOutlineHelper = new HolographicOutlineHelper();
-        }
-
         // Set up fade in/out constants
         final Resources r = context.getResources();
         final int alpha = r.getInteger(R.integer.config_dragAppsCustomizeIconFadeAlpha);
@@ -141,69 +94,34 @@
         return mHolographicOutline;
     }
 
-    private boolean queueHolographicOutlineCreation() {
-        // Generate the outline in the background
-        if (mHolographicOutline == null) {
-            Message m = sWorker.obtainMessage(MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE);
-            m.obj = this;
-            sWorker.sendMessage(m);
-            return true;
-        }
-        return false;
-    }
-
-    public void loadHolographicIcon() {
-        if (mHolographicOutline == null) {
-            mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
-            if (!queueHolographicOutlineCreation()) {
-                getHolographicOutlineView().invalidate();
-            }
-        }
-    }
-    public void clearHolographicIcon() {
-        mHolographicOutline = null;
-        getHolographicOutlineView().invalidate();
-    }
-
-    public void applyFromApplicationInfo(ApplicationInfo info, PagedViewIconCache cache,
-            boolean scaleUp, boolean createHolographicOutlines) {
-        mIconCache = cache;
-        mIconCacheKey = new PagedViewIconCache.Key(info);
+    public void applyFromApplicationInfo(ApplicationInfo info, boolean scaleUp,
+            HolographicOutlineHelper holoOutlineHelper) {
+        mHolographicOutlineHelper = holoOutlineHelper;
         mIcon = info.iconBitmap;
         setCompoundDrawablesWithIntrinsicBounds(null, new FastBitmapDrawable(mIcon), null, null);
         setText(info.title);
         setTag(info);
-
-        if (createHolographicOutlines) {
-            mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
-            if (!queueHolographicOutlineCreation()) {
-                getHolographicOutlineView().invalidate();
-            }
-        }
     }
 
     public void applyFromResolveInfo(ResolveInfo info, PackageManager packageManager,
-            PagedViewIconCache cache, IconCache modelIconCache, boolean createHolographicOutlines) {
+            IconCache modelIconCache, HolographicOutlineHelper holoOutlineHelper) {
+        mHolographicOutlineHelper = holoOutlineHelper;
         ComponentName cn = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
         mIcon = modelIconCache.getIcon(cn, info);
         setCompoundDrawablesWithIntrinsicBounds(null, new FastBitmapDrawable(mIcon), null, null);
         setText(info.loadLabel(packageManager));
         setTag(info);
+    }
 
-        if (createHolographicOutlines) {
-            mIconCache = cache;
-            mIconCacheKey = new PagedViewIconCache.Key(info);
-            mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
-            if (!queueHolographicOutlineCreation()) {
-                getHolographicOutlineView().invalidate();
-            }
-        }
+    public void setHolographicOutline(Bitmap holoOutline) {
+        mHolographicOutline = holoOutline;
+        getHolographicOutlineView().invalidate();
     }
 
     @Override
     public void setAlpha(float alpha) {
-        final float viewAlpha = sHolographicOutlineHelper.viewAlphaInterpolator(alpha);
-        final float holographicAlpha = sHolographicOutlineHelper.highlightAlphaInterpolator(alpha);
+        final float viewAlpha = mHolographicOutlineHelper.viewAlphaInterpolator(alpha);
+        final float holographicAlpha = mHolographicOutlineHelper.highlightAlphaInterpolator(alpha);
         int newViewAlpha = (int) (viewAlpha * 255);
         int newHolographicAlpha = (int) (holographicAlpha * 255);
         if ((mAlpha != newViewAlpha) || (mHolographicAlpha != newHolographicAlpha)) {
@@ -247,12 +165,6 @@
     }
 
     @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        sWorker.removeMessages(MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE, this);
-    }
-
-    @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         return FocusHelper.handlePagedViewIconKeyEvent(this, keyCode, event)
                 || super.onKeyDown(keyCode, event);
diff --git a/src/com/android/launcher2/PagedViewWidget.java b/src/com/android/launcher2/PagedViewWidget.java
index 4061f19..433a785 100644
--- a/src/com/android/launcher2/PagedViewWidget.java
+++ b/src/com/android/launcher2/PagedViewWidget.java
@@ -53,80 +53,22 @@
     static final String TAG = "PagedViewWidgetLayout";
 
     private final Paint mPaint = new Paint();
-    private static HolographicOutlineHelper sHolographicOutlineHelper;
     private Bitmap mHolographicOutline;
-    private final Canvas mHolographicOutlineCanvas = new Canvas();
-    private FastBitmapDrawable mPreview;
+    private HolographicOutlineHelper mHolographicOutlineHelper;
     private ImageView mPreviewImageView;
     private final RectF mTmpScaleRect = new RectF();
-    private final Rect mEraseStrokeRect = new Rect();
-    private final Paint mEraseStrokeRectPaint = new Paint();
 
-    private PagedViewIconCache.Key mIconCacheKey;
-    private PagedViewIconCache mIconCache;
     private String mDimensionsFormatString;
 
     private int mAlpha = 255;
     private int mHolographicAlpha;
 
-    // Highlight colors
-    private int mHoloBlurColor;
-    private int mHoloOutlineColor;
-
     private boolean mIsChecked;
     private ObjectAnimator mCheckedAlphaAnimator;
     private float mCheckedAlpha = 1.0f;
     private int mCheckedFadeInDuration;
     private int mCheckedFadeOutDuration;
 
-    private static final HandlerThread sWorkerThread = new HandlerThread("pagedviewwidget-helper");
-    static {
-        sWorkerThread.start();
-    }
-
-    private static final int MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE = 1;
-
-    private static final Handler sWorker = new Handler(sWorkerThread.getLooper()) {
-        private DeferredHandler mHandler = new DeferredHandler();
-        public void handleMessage(Message msg) {
-            final PagedViewWidget widget = (PagedViewWidget) msg.obj;
-            final int prevAlpha = widget.mPreview.getAlpha();
-            final int width = Math.max(widget.mPreview.getIntrinsicWidth(),
-                    widget.getMeasuredWidth());
-            final int height = Math.max(widget.mPreview.getIntrinsicHeight(),
-                    widget.getMeasuredHeight());
-            final Bitmap outline = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-
-            widget.mHolographicOutlineCanvas.setBitmap(outline);
-            widget.mHolographicOutlineCanvas.save();
-            widget.mHolographicOutlineCanvas.translate(widget.mPaddingLeft, widget.mPaddingTop);
-            widget.mPreview.setAlpha(255);
-            widget.mPreview.draw(widget.mHolographicOutlineCanvas);
-            widget.mPreview.setAlpha(prevAlpha);
-            // Temporary workaround to make the default widget outlines visible
-            widget.mHolographicOutlineCanvas.drawColor(Color.argb(156, 0, 0, 0), Mode.SRC_OVER);
-            widget.mHolographicOutlineCanvas.restore();
-
-            // To account for the fact that some previews run up straight to the edge (we subtract
-            // the edge from the holographic preview (before we apply the holograph)
-            widget.mEraseStrokeRect.set(0, 0, width, height);
-            widget.mHolographicOutlineCanvas.drawRect(widget.mEraseStrokeRect,
-                    widget.mEraseStrokeRectPaint);
-
-            sHolographicOutlineHelper.applyThickExpensiveOutlineWithBlur(outline,
-                    widget.mHolographicOutlineCanvas, widget.mHoloBlurColor,
-                    widget.mHoloOutlineColor);
-
-            mHandler.post(new Runnable() {
-                public void run() {
-                    widget.mHolographicOutline = outline;
-                    widget.mIconCache.addOutline(widget.mIconCacheKey, outline);
-                    widget.invalidate();
-                }
-            });
-        }
-    };
-
     public PagedViewWidget(Context context) {
         this(context, null);
     }
@@ -138,21 +80,6 @@
     public PagedViewWidget(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedViewWidget,
-                defStyle, 0);
-        mHoloBlurColor = a.getColor(R.styleable.PagedViewWidget_blurColor, 0);
-        mHoloOutlineColor = a.getColor(R.styleable.PagedViewWidget_outlineColor, 0);
-        mEraseStrokeRectPaint.setStyle(Paint.Style.STROKE);
-        mEraseStrokeRectPaint.setStrokeWidth(HolographicOutlineHelper.MIN_OUTER_BLUR_RADIUS);
-        mEraseStrokeRectPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
-        mEraseStrokeRectPaint.setFilterBitmap(true);
-        mEraseStrokeRectPaint.setAntiAlias(true);
-        a.recycle();
-
-        if (sHolographicOutlineHelper == null) {
-            sHolographicOutlineHelper = new HolographicOutlineHelper();
-        }
-
         // Set up fade in/out constants
         final Resources r = context.getResources();
         final int alpha = r.getInteger(R.integer.config_dragAppsCustomizeIconFadeAlpha);
@@ -169,18 +96,10 @@
         setClipToPadding(false);
     }
 
-    private void queueHolographicOutlineCreation() {
-        // Generate the outline in the background
-        if (mHolographicOutline == null && mPreview != null) {
-            Message m = sWorker.obtainMessage(MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE);
-            m.obj = this;
-            sWorker.sendMessage(m);
-        }
-    }
-
     public void applyFromAppWidgetProviderInfo(AppWidgetProviderInfo info,
             FastBitmapDrawable preview, int maxWidth, int[] cellSpan,
-            PagedViewIconCache cache, boolean createHolographicOutline) {
+            HolographicOutlineHelper holoOutlineHelper) {
+        mHolographicOutlineHelper = holoOutlineHelper;
         final ImageView image = (ImageView) findViewById(R.id.widget_preview);
         if (maxWidth > -1) {
             image.setMaxWidth(maxWidth);
@@ -198,17 +117,11 @@
         if (!LauncherApplication.isScreenLarge()) {
             findViewById(R.id.divider).setVisibility(View.GONE);
         }
-
-        if (createHolographicOutline) {
-            mIconCache = cache;
-            mIconCacheKey = new PagedViewIconCache.Key(info);
-            mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
-            mPreview = preview;
-        }
     }
 
     public void applyFromResolveInfo(PackageManager pm, ResolveInfo info,
-            FastBitmapDrawable preview, PagedViewIconCache cache, boolean createHolographicOutline){
+            FastBitmapDrawable preview, HolographicOutlineHelper holoOutlineHelper) {
+        mHolographicOutlineHelper = holoOutlineHelper;
         final ImageView image = (ImageView) findViewById(R.id.widget_preview);
         image.setImageDrawable(preview);
         mPreviewImageView = image;
@@ -225,18 +138,11 @@
         if (!LauncherApplication.isScreenLarge()) {
             findViewById(R.id.divider).setVisibility(View.GONE);
         }
-
-        if (createHolographicOutline) {
-            mIconCache = cache;
-            mIconCacheKey = new PagedViewIconCache.Key(info);
-            mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
-            mPreview = preview;
-        }
     }
 
     public void applyFromWallpaperInfo(ResolveInfo info, PackageManager packageManager,
-            FastBitmapDrawable preview, int maxWidth, PagedViewIconCache cache,
-            boolean createHolographicOutline) {
+            FastBitmapDrawable preview, int maxWidth, HolographicOutlineHelper holoOutlineHelper) {
+        mHolographicOutlineHelper = holoOutlineHelper;
         ImageView image = (ImageView) findViewById(R.id.wallpaper_preview);
         image.setMaxWidth(maxWidth);
         image.setImageDrawable(preview);
@@ -249,13 +155,11 @@
         if (!LauncherApplication.isScreenLarge()) {
             findViewById(R.id.divider).setVisibility(View.GONE);
         }
+    }
 
-        if (createHolographicOutline) {
-            mIconCache = cache;
-            mIconCacheKey = new PagedViewIconCache.Key(info);
-            mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
-            mPreview = preview;
-        }
+    public void setHolographicOutline(Bitmap holoOutline) {
+        mHolographicOutline = holoOutline;
+        invalidate();
     }
 
     @Override
@@ -307,10 +211,16 @@
         return true;
     }
 
+    private void setChildrenAlpha(float alpha) {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            getChildAt(i).setAlpha(alpha);
+        }
+    }
     @Override
     public void setAlpha(float alpha) {
-        final float viewAlpha = sHolographicOutlineHelper.viewAlphaInterpolator(alpha);
-        final float holographicAlpha = sHolographicOutlineHelper.highlightAlphaInterpolator(alpha);
+        final float viewAlpha = mHolographicOutlineHelper.viewAlphaInterpolator(alpha);
+        final float holographicAlpha = mHolographicOutlineHelper.highlightAlphaInterpolator(alpha);
         int newViewAlpha = (int) (viewAlpha * 255);
         int newHolographicAlpha = (int) (holographicAlpha * 255);
         if ((mAlpha != newViewAlpha) || (mHolographicAlpha != newHolographicAlpha)) {
@@ -321,28 +231,6 @@
         }
     }
 
-    private void setChildrenAlpha(float alpha) {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            getChildAt(i).setAlpha(alpha);
-        }
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        if (w > 0 && h > 0) {
-            queueHolographicOutlineCreation();
-        }
-
-        super.onSizeChanged(w, h, oldw, oldh);
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        sWorker.removeMessages(MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE, this);
-    }
-
     void setChecked(boolean checked, boolean animate) {
         if (mIsChecked != checked) {
             mIsChecked = checked;