Fixing issues with holographic outline cache in AllApps/Customize.

- Fixing issue where the outline cache was not properly used when changing orientations
- Making the outline cache static, and shared across both the AllApps/Customize (since they
  share an Apps view)
- Making sure that holographic outlines for items on only one page are not created, since
  the holographic outlines will never be shown in that case.
- No longer clearing outline cache as frequently

Change-Id: I291db3802260249d0470d2637d871958baa8ebff
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index a424bc6..f7d9f0e 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -322,7 +322,7 @@
         mApps = list;
         Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
         mFilteredApps = rebuildFilteredApps(mApps);
-        mPageViewIconCache.clear();
+        mPageViewIconCache.retainAllApps(list);
         invalidatePageData();
     }
 
@@ -367,7 +367,7 @@
             int removeIndex = findAppByComponent(mApps, info);
             if (removeIndex > -1) {
                 mApps.remove(removeIndex);
-                mPageViewIconCache.removeOutline(info);
+                mPageViewIconCache.removeOutline(new PagedViewIconCache.Key(info));
             }
         }
         mFilteredApps = rebuildFilteredApps(mApps);
@@ -478,11 +478,12 @@
             }
 
             // Actually reapply to the existing text views
+            final int numPages = getPageCount();
             for (int i = startIndex; i < endIndex; ++i) {
                 final int index = i - startIndex;
                 final ApplicationInfo info = mFilteredApps.get(i);
                 PagedViewIcon icon = (PagedViewIcon) layout.getChildAt(index);
-                icon.applyFromApplicationInfo(info, mPageViewIconCache, true);
+                icon.applyFromApplicationInfo(info, mPageViewIconCache, true, (numPages > 1));
 
                 PagedViewCellLayout.LayoutParams params =
                     (PagedViewCellLayout.LayoutParams) icon.getLayoutParams();
diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
index d2edd38..0b874bc 100644
--- a/src/com/android/launcher2/CustomizePagedView.java
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -180,7 +180,8 @@
         Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
 
         // Update the widgets/shortcuts to reflect changes in the set of available apps
-        invalidatePageDataAndIconCache();
+        mPageViewIconCache.retainAllApps(list);
+        invalidatePageData();
     }
 
     /**
@@ -206,7 +207,7 @@
         addAppsWithoutInvalidate(list);
 
         // Update the widgets/shortcuts to reflect changes in the set of available apps
-        invalidatePageDataAndIconCache();
+        invalidatePageData();
     }
 
     /**
@@ -221,7 +222,7 @@
             int removeIndex = findAppByComponent(mApps, info);
             if (removeIndex > -1) {
                 mApps.remove(removeIndex);
-                mPageViewIconCache.removeOutline(info);
+                mPageViewIconCache.removeOutline(new PagedViewIconCache.Key(info));
             }
         }
     }
@@ -233,7 +234,7 @@
         removeAppsWithoutInvalidate(list);
 
         // Update the widgets/shortcuts to reflect changes in the set of available apps
-        invalidatePageDataAndIconCache();
+        invalidatePageData();
     }
 
     /**
@@ -248,7 +249,7 @@
         addAppsWithoutInvalidate(list);
 
         // Update the widgets/shortcuts to reflect changes in the set of available apps
-        invalidatePageDataAndIconCache();
+        invalidatePageData();
     }
 
     /**
@@ -295,14 +296,10 @@
                 PackageManager.GET_META_DATA);
         Collections.sort(mWallpaperList, resolveInfoComparator);
 
-        invalidatePageDataAndIconCache();
-    }
-
-    private void invalidatePageDataAndIconCache() {
-        // Reset the icon cache
-        mPageViewIconCache.clear();
-
-        // Refresh all the tabs
+        ArrayList<ResolveInfo> retainShortcutList = new ArrayList<ResolveInfo>(mShortcutList);
+        retainShortcutList.addAll(mWallpaperList);
+        mPageViewIconCache.retainAllShortcuts(retainShortcutList);
+        mPageViewIconCache.retainAllAppWidgets(mWidgetList);
         invalidatePageData();
     }
 
@@ -838,6 +835,7 @@
         LinearLayout layout = (LinearLayout) getChildAt(page);
         final ArrayList<AppWidgetProviderInfo> list = mWidgetPages.get(page);
         final int count = list.size();
+        final int numPages = getPageCount();
         layout.removeAllViews();
         for (int i = 0; i < count; ++i) {
             final AppWidgetProviderInfo info = (AppWidgetProviderInfo) list.get(i);
@@ -848,7 +846,8 @@
 
             PagedViewWidget l = (PagedViewWidget) mInflater.inflate(
                     R.layout.customize_paged_view_widget, layout, false);
-            l.applyFromAppWidgetProviderInfo(info, icon, mMaxWidgetWidth, cellSpans);
+            l.applyFromAppWidgetProviderInfo(info, icon, mMaxWidgetWidth, cellSpans,
+                    mPageViewIconCache, (numPages > 1));
             l.setTag(createItemInfo);
             l.setOnClickListener(this);
             l.setOnTouchListener(this);
@@ -882,6 +881,7 @@
         LinearLayout layout = (LinearLayout) getChildAt(page);
         layout.removeAllViews();
         final int count = mWallpaperList.size();
+        final int numPages = getPageCount();
         final int numItemsPerPage = mMaxWallpaperCellHSpan / mWallpaperCellHSpan;
         final int startIndex = page * numItemsPerPage;
         final int endIndex = Math.min(count, startIndex + numItemsPerPage);
@@ -891,7 +891,8 @@
 
             PagedViewWidget l = (PagedViewWidget) mInflater.inflate(
                     R.layout.customize_paged_view_wallpaper, layout, false);
-            l.applyFromWallpaperInfo(info, mPackageManager, icon, mMaxWidgetWidth);
+            l.applyFromWallpaperInfo(info, mPackageManager, icon, mMaxWidgetWidth,
+                    mPageViewIconCache, (numPages > 1));
             l.setTag(info);
             l.setOnClickListener(this);
 
@@ -914,10 +915,11 @@
 
     private void syncListPageItems(int page, List<ResolveInfo> list) {
         // ensure that we have the right number of items on the pages
-        int numCells = mCellCountX * mCellCountY;
-        int startIndex = page * numCells;
-        int endIndex = Math.min(startIndex + numCells, list.size());
-        PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+        final int numPages = getPageCount();
+        final int numCells = mCellCountX * mCellCountY;
+        final int startIndex = page * numCells;
+        final int endIndex = Math.min(startIndex + numCells, list.size());
+        final PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
         // TODO: we can optimize by just re-applying to existing views
         layout.removeAllViews();
         for (int i = startIndex; i < endIndex; ++i) {
@@ -927,7 +929,8 @@
             PagedViewIcon icon = (PagedViewIcon) mInflater.inflate(
                     R.layout.customize_paged_view_item, layout, false);
             icon.applyFromResolveInfo(info, mPackageManager, mPageViewIconCache,
-                    ((LauncherApplication) mLauncher.getApplication()).getIconCache());
+                    ((LauncherApplication) mLauncher.getApplication()).getIconCache(),
+                    (numPages > 1));
             switch (mCustomizationType) {
             case WallpaperCustomization:
                 icon.setOnClickListener(this);
@@ -972,17 +975,18 @@
         if (mApps == null) return;
 
         // ensure that we have the right number of items on the pages
-        int numCells = mCellCountX * mCellCountY;
-        int startIndex = page * numCells;
-        int endIndex = Math.min(startIndex + numCells, mApps.size());
-        PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+        final int numPages = getPageCount();
+        final int numCells = mCellCountX * mCellCountY;
+        final int startIndex = page * numCells;
+        final int endIndex = Math.min(startIndex + numCells, mApps.size());
+        final PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
         // TODO: we can optimize by just re-applying to existing views
         layout.removeAllViews();
         for (int i = startIndex; i < endIndex; ++i) {
             final ApplicationInfo info = mApps.get(i);
             PagedViewIcon icon = (PagedViewIcon) mInflater.inflate(
                     R.layout.all_apps_paged_view_application, layout, false);
-            icon.applyFromApplicationInfo(info, mPageViewIconCache, true);
+            icon.applyFromApplicationInfo(info, mPageViewIconCache, true, (numPages > 1));
             icon.setOnClickListener(this);
             icon.setOnTouchListener(this);
             icon.setOnLongClickListener(this);
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 68833f9..6c9aac1 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -16,7 +16,10 @@
 
 package com.android.launcher2;
 
-import com.android.launcher.R;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.TreeMap;
+import java.util.TreeSet;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -34,14 +37,13 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
-import android.view.animation.Animation.AnimationListener;
 import android.widget.Checkable;
 import android.widget.Scroller;
 
-import java.util.ArrayList;
-import java.util.HashMap;
+import com.android.launcher.R;
 
 /**
  * An abstraction of the original Workspace which supports browsing through a
@@ -132,7 +134,10 @@
     protected int mChoiceMode;
     private ActionMode mActionMode;
 
-    protected PagedViewIconCache mPageViewIconCache;
+    // 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;
@@ -150,28 +155,6 @@
 
     protected boolean mIsPageMoving = false;
 
-    /**
-     * Simple cache mechanism for PagedViewIcon outlines.
-     */
-    class PagedViewIconCache {
-        private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>();
-
-        public void clear() {
-            iconOutlineCache.clear();
-        }
-        public void addOutline(Object key, Bitmap b) {
-            iconOutlineCache.put(key, b);
-        }
-        public void removeOutline(Object key) {
-            if (iconOutlineCache.containsKey(key)) {
-                iconOutlineCache.remove(key);
-            }
-        }
-        public Bitmap getOutline(Object key) {
-            return iconOutlineCache.get(key);
-        }
-    }
-
     public interface PageSwitchListener {
         void onPageSwitch(View newPage, int newPageIndex);
     }
@@ -215,7 +198,6 @@
     protected void init() {
         mDirtyPageContent = new ArrayList<Boolean>();
         mDirtyPageContent.ensureCapacity(32);
-        mPageViewIconCache = new PagedViewIconCache();
         mScroller = new Scroller(getContext(), new ScrollInterpolator());
         mCurrentPage = 0;
         mCenterPagesVertically = true;
diff --git a/src/com/android/launcher2/PagedViewIcon.java b/src/com/android/launcher2/PagedViewIcon.java
index e4049eb..6ce308b 100644
--- a/src/com/android/launcher2/PagedViewIcon.java
+++ b/src/com/android/launcher2/PagedViewIcon.java
@@ -32,7 +32,6 @@
 import android.widget.Checkable;
 
 import com.android.launcher.R;
-import com.android.launcher2.PagedView.PagedViewIconCache;
 
 
 
@@ -50,7 +49,7 @@
     private Bitmap mHolographicOutline;
     private Bitmap mIcon;
 
-    private Object mIconCacheKey;
+    private PagedViewIconCache.Key mIconCacheKey;
     private PagedViewIconCache mIconCache;
 
     private int mAlpha = 255;
@@ -140,26 +139,23 @@
     }
 
     public void applyFromApplicationInfo(ApplicationInfo info, PagedViewIconCache cache,
-            boolean scaleUp) {
-        mIconCache = cache;
-        mIconCacheKey = info;
-        mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
-
+            boolean scaleUp, boolean createHolographicOutlines) {
         mIcon = info.iconBitmap;
         setCompoundDrawablesWithIntrinsicBounds(null, new FastBitmapDrawable(mIcon), null, null);
         setText(info.title);
         buildAndEnableCache();
         setTag(info);
 
-        queueHolographicOutlineCreation();
+        if (createHolographicOutlines) {
+            mIconCache = cache;
+            mIconCacheKey = new PagedViewIconCache.Key(info);
+            mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
+            queueHolographicOutlineCreation();
+        }
     }
 
     public void applyFromResolveInfo(ResolveInfo info, PackageManager packageManager,
-            PagedViewIconCache cache, IconCache modelIconCache) {
-        mIconCache = cache;
-        mIconCacheKey = info;
-        mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
-
+            PagedViewIconCache cache, IconCache modelIconCache, boolean createHolographicOutlines) {
         mIcon = Utilities.createIconBitmap(
                 modelIconCache.getFullResIcon(info, packageManager), mContext);
         setCompoundDrawablesWithIntrinsicBounds(null, new FastBitmapDrawable(mIcon), null, null);
@@ -167,7 +163,12 @@
         buildAndEnableCache();
         setTag(info);
 
-        queueHolographicOutlineCreation();
+        if (createHolographicOutlines) {
+            mIconCache = cache;
+            mIconCacheKey = new PagedViewIconCache.Key(info);
+            mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
+            queueHolographicOutlineCreation();
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher2/PagedViewIconCache.java b/src/com/android/launcher2/PagedViewIconCache.java
new file mode 100644
index 0000000..de05ff1
--- /dev/null
+++ b/src/com/android/launcher2/PagedViewIconCache.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.pm.ComponentInfo;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+
+/**
+ * Simple cache mechanism for PagedView outlines.
+ */
+public class PagedViewIconCache {
+    public static class Key {
+        public enum Type {
+            ApplicationInfoKey,
+            AppWidgetProviderInfoKey,
+            ResolveInfoKey
+        }
+        private final ComponentName mComponentName;
+        private final Type mType;
+
+        public Key(ApplicationInfo info) {
+            mComponentName = info.componentName;
+            mType = Type.ApplicationInfoKey;
+        }
+        public Key(ResolveInfo info) {
+            final ComponentInfo ci = info.activityInfo != null ? info.activityInfo :
+                info.serviceInfo;
+            mComponentName = new ComponentName(ci.packageName, ci.name);
+            mType = Type.AppWidgetProviderInfoKey;
+        }
+        public Key(AppWidgetProviderInfo info) {
+            mComponentName = info.provider;
+            mType = Type.ResolveInfoKey;
+        }
+
+        private ComponentName getComponentName() {
+            return mComponentName;
+        }
+        public boolean isKeyType(Type t) {
+            return (mType == t);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof Key) {
+                Key k = (Key) o;
+                return mComponentName.equals(k.mComponentName);
+            }
+            return super.equals(o);
+        }
+        @Override
+        public int hashCode() {
+            return getComponentName().hashCode();
+        }
+    }
+
+    private final HashMap<Key, Bitmap> mIconOutlineCache = new HashMap<Key, Bitmap>();
+
+    public void clear() {
+        for (Key key : mIconOutlineCache.keySet()) {
+            mIconOutlineCache.get(key).recycle();
+        }
+        mIconOutlineCache.clear();
+    }
+    private void retainAll(HashSet<Key> keysToKeep, Key.Type t) {
+        HashSet<Key> keysToRemove = new HashSet<Key>(mIconOutlineCache.keySet());
+        keysToRemove.removeAll(keysToKeep);
+        for (Key key : keysToRemove) {
+            if (key.isKeyType(t)) {
+                mIconOutlineCache.get(key).recycle();
+                mIconOutlineCache.remove(key);
+            }
+        }
+    }
+    /** Removes all the keys to applications that aren't in the passed in collection */
+    public void retainAllApps(ArrayList<ApplicationInfo> keys) {
+        HashSet<Key> keysSet = new HashSet<Key>();
+        for (ApplicationInfo info : keys) {
+            keysSet.add(new Key(info));
+        }
+        retainAll(keysSet, Key.Type.ApplicationInfoKey);
+    }
+    /** Removes all the keys to shortcuts that aren't in the passed in collection */
+    public void retainAllShortcuts(List<ResolveInfo> keys) {
+        HashSet<Key> keysSet = new HashSet<Key>();
+        for (ResolveInfo info : keys) {
+            keysSet.add(new Key(info));
+        }
+        retainAll(keysSet, Key.Type.ResolveInfoKey);
+    }
+    /** Removes all the keys to widgets that aren't in the passed in collection */
+    public void retainAllAppWidgets(List<AppWidgetProviderInfo> keys) {
+        HashSet<Key> keysSet = new HashSet<Key>();
+        for (AppWidgetProviderInfo info : keys) {
+            keysSet.add(new Key(info));
+        }
+        retainAll(keysSet, Key.Type.AppWidgetProviderInfoKey);
+    }
+    public void addOutline(Key key, Bitmap b) {
+        mIconOutlineCache.put(key, b);
+    }
+    public void removeOutline(Key key) {
+        if (mIconOutlineCache.containsKey(key)) {
+            mIconOutlineCache.get(key).recycle();
+            mIconOutlineCache.remove(key);
+        }
+    }
+    public Bitmap getOutline(Key key) {
+        return mIconOutlineCache.get(key);
+    }
+}
diff --git a/src/com/android/launcher2/PagedViewWidget.java b/src/com/android/launcher2/PagedViewWidget.java
index 8c729b1..07a59ee 100644
--- a/src/com/android/launcher2/PagedViewWidget.java
+++ b/src/com/android/launcher2/PagedViewWidget.java
@@ -52,6 +52,9 @@
     private final Canvas mHolographicOutlineCanvas = new Canvas();
     private FastBitmapDrawable mPreview;
 
+    private PagedViewIconCache.Key mIconCacheKey;
+    private PagedViewIconCache mIconCache;
+
     private int mAlpha = 255;
     private int mHolographicAlpha;
 
@@ -97,6 +100,7 @@
             mHandler.post(new Runnable() {
                 public void run() {
                     widget.mHolographicOutline = outline;
+                    widget.mIconCache.addOutline(widget.mIconCacheKey, outline);
                     widget.invalidate();
                 }
             });
@@ -140,7 +144,7 @@
 
     private void queueHolographicOutlineCreation() {
         // Generate the outline in the background
-        if (mHolographicOutline == null) {
+        if (mHolographicOutline == null && mPreview != null) {
             Message m = sWorker.obtainMessage(MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE);
             m.obj = this;
             sWorker.sendMessage(m);
@@ -148,7 +152,8 @@
     }
 
     public void applyFromAppWidgetProviderInfo(AppWidgetProviderInfo info,
-            FastBitmapDrawable preview, int maxWidth, int[] cellSpan) {
+            FastBitmapDrawable preview, int maxWidth, int[] cellSpan,
+            PagedViewIconCache cache, boolean createHolographicOutline) {
         final ImageView image = (ImageView) findViewById(R.id.widget_preview);
         image.setMaxWidth(maxWidth);
         image.setImageDrawable(preview);
@@ -156,17 +161,30 @@
         name.setText(info.label);
         final TextView dims = (TextView) findViewById(R.id.widget_dims);
         dims.setText(mContext.getString(R.string.widget_dims_format, cellSpan[0], cellSpan[1]));
-        mPreview = preview;
+
+        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) {
+            FastBitmapDrawable preview, int maxWidth, PagedViewIconCache cache,
+            boolean createHolographicOutline) {
         ImageView image = (ImageView) findViewById(R.id.wallpaper_preview);
         image.setMaxWidth(maxWidth);
         image.setImageDrawable(preview);
         TextView name = (TextView) findViewById(R.id.wallpaper_name);
         name.setText(info.loadLabel(packageManager));
-        mPreview = preview;
+
+        if (createHolographicOutline) {
+            mIconCache = cache;
+            mIconCacheKey = new PagedViewIconCache.Key(info);
+            mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
+            mPreview = preview;
+        }
     }
 
     @Override