diff options
author | Chris Wren <cwren@android.com> | 2014-02-10 12:16:54 -0500 |
---|---|---|
committer | Danesh Mondegarian <daneshm90@gmail.com> | 2014-06-07 06:55:58 -0700 |
commit | 953e0828b29215bb96b029ec01e6e6a3ae841b1e (patch) | |
tree | ffad76b7438c25f7fbd931e22cf267f1a3f57cec /src/com/android/launcher3 | |
parent | db4940837d20e4b45c0722a91b7fd10fd9b6b9b0 (diff) | |
download | android_packages_apps_Trebuchet-953e0828b29215bb96b029ec01e6e6a3ae841b1e.tar.gz android_packages_apps_Trebuchet-953e0828b29215bb96b029ec01e6e6a3ae841b1e.tar.bz2 android_packages_apps_Trebuchet-953e0828b29215bb96b029ec01e6e6a3ae841b1e.zip |
use restored icon for restored app shortcuts
Bug: 10778992
Change-Id: Ie430a6587d49dc0d78b87b81582c0cef7c281017
Diffstat (limited to 'src/com/android/launcher3')
-rw-r--r-- | src/com/android/launcher3/AllAppsList.java | 5 | ||||
-rw-r--r-- | src/com/android/launcher3/IconCache.java | 214 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherBackupHelper.java | 48 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 12 |
4 files changed, 237 insertions, 42 deletions
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index b641eb5b3..89b291f28 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -32,7 +32,7 @@ import java.util.List; */ class AllAppsList { public static final int DEFAULT_APPLICATIONS_NUMBER = 42; - + /** The list off all apps. */ public ArrayList<AppInfo> data = new ArrayList<AppInfo>(DEFAULT_APPLICATIONS_NUMBER); @@ -115,8 +115,7 @@ class AllAppsList { data.remove(i); } } - // This is more aggressive than it needs to be. - mIconCache.flush(); + mIconCache.remove(packageName); } /** diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 300bb544f..a6b02aeb0 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import com.android.launcher3.backup.BackupProtos; + import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; @@ -25,11 +27,21 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.text.TextUtils; - +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.Map.Entry; @@ -43,6 +55,10 @@ public class IconCache { private static final String TAG = "Launcher.IconCache"; private static final int INITIAL_ICON_CACHE_CAPACITY = 50; + private static final String RESOURCE_FILE_PREFIX = "icon_"; + + private static final boolean DEBUG = true; + private IconPackHelper mIconPackHelper; private static class CacheEntry { @@ -129,6 +145,7 @@ public class IconCache { return getFullResIcon(resources, iconId); } } + return getFullResDefaultActivityIcon(); } @@ -164,6 +181,21 @@ public class IconCache { } /** + * Remove any records for the supplied package name. + */ + public void remove(String packageName) { + HashSet<ComponentName> forDeletion = new HashSet<ComponentName>(); + for (ComponentName componentName: mCache.keySet()) { + if (componentName.getPackageName().equals(packageName)) { + forDeletion.add(componentName); + } + } + for (ComponentName condemned: forDeletion) { + remove(condemned); + } + } + + /** * Empty out the cache. */ public void flush() { @@ -202,15 +234,22 @@ public class IconCache { } public Bitmap getIcon(Intent intent) { + return getIcon(intent, null); + } + + public Bitmap getIcon(Intent intent, String title) { synchronized (mCache) { final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0); ComponentName component = intent.getComponent(); - if (resolveInfo == null || component == null) { + if (component == null) { return mDefaultIcon; } CacheEntry entry = cacheLocked(component, resolveInfo, null); + if (title != null) { + entry.title = title; + } return entry.icon; } } @@ -239,21 +278,35 @@ public class IconCache { mCache.put(componentName, entry); - ComponentName key = LauncherModel.getComponentNameFromResolveInfo(info); - if (labelCache != null && labelCache.containsKey(key)) { - entry.title = labelCache.get(key).toString(); + if (info != null) { + ComponentName key = LauncherModel.getComponentNameFromResolveInfo(info); + if (labelCache != null && labelCache.containsKey(key)) { + entry.title = labelCache.get(key).toString(); + } else { + entry.title = info.loadLabel(mPackageManager).toString(); + if (labelCache != null) { + labelCache.put(key, entry.title); + } + } + if (entry.title == null) { + entry.title = info.activityInfo.name; + } + + entry.icon = Utilities.createIconBitmap( + getFullResIcon(info), mContext); } else { - entry.title = info.loadLabel(mPackageManager).toString(); - if (labelCache != null) { - labelCache.put(key, entry.title); + entry.title = ""; + Bitmap preloaded = getPreloadedIcon(componentName); + if (preloaded != null) { + if (DEBUG) Log.d(TAG, "using preloaded icon for " + + componentName.toShortString()); + entry.icon = preloaded; + } else { + if (DEBUG) Log.d(TAG, "using default icon for " + + componentName.toShortString()); + entry.icon = mDefaultIcon; } } - if (entry.title == null) { - entry.title = info.activityInfo.name; - } - - entry.icon = Utilities.createIconBitmap( - getFullResIcon(info), mContext); } return entry; } @@ -268,4 +321,137 @@ public class IconCache { return set; } } + + /** + * Pre-load an icon into the persistent cache. + * + * <P>Queries for a component that does not exist in the package manager + * will be answered by the persistent cache. + * + * @param context application context + * @param componentName the icon should be returned for this component + * @param icon the icon to be persisted + * @param dpi the native density of the icon + */ + public static void preloadIcon(Context context, ComponentName componentName, Bitmap icon, + int dpi) { + // TODO rescale to the correct native DPI + try { + PackageManager packageManager = context.getPackageManager(); + packageManager.getActivityIcon(componentName); + // component is present on the system already, do nothing + return; + } catch (PackageManager.NameNotFoundException e) { + // pass + } + + final String key = componentName.flattenToString(); + FileOutputStream resourceFile = null; + try { + resourceFile = context.openFileOutput(getResourceFilename(componentName), + Context.MODE_PRIVATE); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + if (icon.compress(android.graphics.Bitmap.CompressFormat.PNG, 75, os)) { + byte[] buffer = os.toByteArray(); + resourceFile.write(buffer, 0, buffer.length); + } else { + Log.w(TAG, "failed to encode cache for " + key); + return; + } + } catch (FileNotFoundException e) { + Log.w(TAG, "failed to pre-load cache for " + key, e); + } catch (IOException e) { + Log.w(TAG, "failed to pre-load cache for " + key, e); + } finally { + if (resourceFile != null) { + try { + resourceFile.close(); + } catch (IOException e) { + Log.d(TAG, "failed to save restored icon for: " + key, e); + } + } + } + } + + /** + * Read a pre-loaded icon from the persistent icon cache. + * + * @param componentName the component that should own the icon + * @returns a bitmap if one is cached, or null. + */ + private Bitmap getPreloadedIcon(ComponentName componentName) { + final String key = componentName.flattenToShortString(); + + if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key); + Bitmap icon = null; + FileInputStream resourceFile = null; + try { + resourceFile = mContext.openFileInput(getResourceFilename(componentName)); + byte[] buffer = new byte[1024]; + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + int bytesRead = 0; + while(bytesRead >= 0) { + bytes.write(buffer, 0, bytesRead); + bytesRead = resourceFile.read(buffer, 0, buffer.length); + } + if (DEBUG) Log.d(TAG, "read " + bytes.size()); + icon = BitmapFactory.decodeByteArray(bytes.toByteArray(), 0, bytes.size()); + if (icon == null) { + Log.w(TAG, "failed to decode pre-load icon for " + key); + } + } catch (FileNotFoundException e) { + if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key, e); + } catch (IOException e) { + Log.w(TAG, "failed to read pre-load icon for: " + key, e); + } finally { + if(resourceFile != null) { + try { + resourceFile.close(); + } catch (IOException e) { + Log.d(TAG, "failed to manage pre-load icon file: " + key, e); + } + } + } + + if (icon != null) { + // TODO: handle alpha mask in the view layer + Bitmap b = Bitmap.createBitmap(Math.max(icon.getWidth(), 1), + Math.max(icon.getHeight(), 1), + Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + Paint paint = new Paint(); + paint.setAlpha(127); + c.drawBitmap(icon, 0, 0, paint); + c.setBitmap(null); + icon.recycle(); + icon = b; + } + + return icon; + } + + /** + * Remove a pre-loaded icon from the persistent icon cache. + * + * @param componentName the component that should own the icon + * @returns true on success + */ + public boolean deletePreloadedIcon(ComponentName componentName) { + if (componentName == null) { + return false; + } + if (mCache.remove(componentName) != null) { + if (DEBUG) Log.d(TAG, "removed pre-loaded icon from the in-memory cache"); + } + boolean success = mContext.deleteFile(getResourceFilename(componentName)); + if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache"); + + return success; + } + + private static String getResourceFilename(ComponentName component) { + String resourceName = component.flattenToShortString(); + String filename = resourceName.replace(File.separatorChar, '_'); + return RESOURCE_FILE_PREFIX + filename; + } } diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index b77bf9fa3..62e6f3102 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -30,9 +30,8 @@ import com.android.launcher3.backup.BackupProtos.Screen; import com.android.launcher3.backup.BackupProtos.Widget; import android.app.backup.BackupDataInputStream; -import android.app.backup.BackupHelper; -import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.app.backup.BackupHelper; import android.app.backup.BackupManager; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; @@ -448,14 +447,12 @@ public class LauncherBackupHelper implements BackupHelper { private void backupIcons(Journal in, BackupDataOutput data, Journal out, ArrayList<Key> keys) throws IOException { // persist icons that haven't been persisted yet - final LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); - if (appState == null) { + if (!initializeIconCache()) { dataChanged(); // try again later if (DEBUG) Log.d(TAG, "Launcher is not initialized, delaying icon backup"); return; } final ContentResolver cr = mContext.getContentResolver(); - final IconCache iconCache = appState.getIconCache(); final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; // read the old ID set @@ -494,9 +491,9 @@ public class LauncherBackupHelper implements BackupHelper { if (DEBUG) Log.d(TAG, "I can count this high: " + out.rows); if ((out.rows - startRows) < MAX_ICONS_PER_PASS) { if (VERBOSE) Log.v(TAG, "saving icon " + backupKey); - Bitmap icon = iconCache.getIcon(intent); + Bitmap icon = mIconCache.getIcon(intent); keys.add(key); - if (icon != null && !iconCache.isDefaultIcon(icon)) { + if (icon != null && !mIconCache.isDefaultIcon(icon)) { byte[] blob = packIcon(dpi, icon); writeRowToBackup(key, blob, out, data); } @@ -537,25 +534,33 @@ public class LauncherBackupHelper implements BackupHelper { if (VERBOSE) Log.v(TAG, "unpacking icon " + key.id); if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " + Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP)); + try { Resource res = unpackIcon(buffer, 0, dataSize); - if (DEBUG) Log.d(TAG, "unpacked " + res.dpi + " dpi icon"); - if (DEBUG_PAYLOAD) Log.d(TAG, "read " + - Base64.encodeToString(res.data, 0, res.data.length, - Base64.NO_WRAP)); + if (DEBUG) { + Log.d(TAG, "unpacked " + res.dpi + " dpi icon"); + } + if (DEBUG_PAYLOAD) { + Log.d(TAG, "read " + + Base64.encodeToString(res.data, 0, res.data.length, + Base64.NO_WRAP)); + } Bitmap icon = BitmapFactory.decodeByteArray(res.data, 0, res.data.length); if (icon == null) { Log.w(TAG, "failed to unpack icon for " + key.name); } if (!mRestoreEnabled) { - if (VERBOSE) Log.v(TAG, "restore not enabled: skipping database mutation"); + if (VERBOSE) { + Log.v(TAG, "restore not enabled: skipping database mutation"); + } return; } else { - // future site of icon cache mutation + IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(key.name), + icon, res.dpi); } - } catch (InvalidProtocolBufferNanoException e) { - Log.e(TAG, "failed to decode icon", e); + } catch (IOException e) { + Log.d(TAG, "failed to save restored icon for: " + key.name, e); } } @@ -573,15 +578,13 @@ public class LauncherBackupHelper implements BackupHelper { ArrayList<Key> keys) throws IOException { // persist static widget info that hasn't been persisted yet final LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); - if (appState == null) { - dataChanged(); // try again later - if (DEBUG) Log.d(TAG, "Launcher is not initialized, delaying widget backup"); + if (appState == null || !initializeIconCache()) { + Log.w(TAG, "Failed to get icon cache during restore"); return; } final ContentResolver cr = mContext.getContentResolver(); final WidgetPreviewLoader previewLoader = new WidgetPreviewLoader(mContext); final PagedViewCellLayout widgetSpacingLayout = new PagedViewCellLayout(mContext); - final IconCache iconCache = appState.getIconCache(); final int dpi = mContext.getResources().getDisplayMetrics().densityDpi; final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile(); if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx); @@ -624,7 +627,7 @@ public class LauncherBackupHelper implements BackupHelper { if (VERBOSE) Log.v(TAG, "saving widget " + backupKey); previewLoader.setPreviewSize(spanX * profile.cellWidthPx, spanY * profile.cellHeightPx, widgetSpacingLayout); - byte[] blob = packWidget(dpi, previewLoader, iconCache, provider); + byte[] blob = packWidget(dpi, previewLoader, mIconCache, provider); keys.add(key); writeRowToBackup(key, blob, out, data); @@ -889,7 +892,7 @@ public class LauncherBackupHelper implements BackupHelper { } /** Deserialize an icon resource from persistence, after verifying checksum wrapper. */ - private Resource unpackIcon(byte[] buffer, int offset, int dataSize) + private static Resource unpackIcon(byte[] buffer, int offset, int dataSize) throws InvalidProtocolBufferNanoException { Resource res = new Resource(); MessageNano.mergeFrom(res, readCheckedBytes(buffer, offset, dataSize)); @@ -1087,7 +1090,7 @@ public class LauncherBackupHelper implements BackupHelper { } /** Unwrap a proto message from a CheckedMessage, verifying the checksum. */ - private byte[] readCheckedBytes(byte[] buffer, int offset, int dataSize) + private static byte[] readCheckedBytes(byte[] buffer, int offset, int dataSize) throws InvalidProtocolBufferNanoException { CheckedMessage wrapper = new CheckedMessage(); MessageNano.mergeFrom(wrapper, buffer, offset, dataSize); @@ -1111,6 +1114,7 @@ public class LauncherBackupHelper implements BackupHelper { return mWidgetMap.get(component); } + private boolean initializeIconCache() { if (mIconCache != null) { return true; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index a42f35751..46bb6960f 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -326,10 +326,16 @@ public class LauncherModel extends BroadcastReceiver { public void run() { Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; if (callbacks == cb && cb != null) { - callbacks.bindAppsAdded(null, null, null, allAppsApps); if (!restoredAppsFinal.isEmpty()) { + for (AppInfo info : restoredAppsFinal) { + final Intent intent = info.getIntent(); + if (intent != null) { + mIconCache.deletePreloadedIcon(intent.getComponent()); + } + } callbacks.bindAppsUpdated(restoredAppsFinal); } + callbacks.bindAppsAdded(null, null, null, allAppsApps); } } }); @@ -2812,6 +2818,7 @@ public class LauncherModel extends BroadcastReceiver { case OP_ADD: for (int i=0; i<N; i++) { if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]); + mIconCache.remove(packages[i]); mBgAllAppsList.addPackage(context, packages[i]); } break; @@ -3007,13 +3014,12 @@ public class LauncherModel extends BroadcastReceiver { */ public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent) { final ShortcutInfo info = new ShortcutInfo(); - info.usingFallbackIcon = true; - info.setIcon(getFallbackIcon()); if (cursor != null) { info.title = cursor.getString(titleIndex); } else { info.title = ""; } + info.setIcon(mIconCache.getIcon(intent, info.title.toString())); info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; info.restoredIntent = intent; return info; |