diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2015-02-18 16:46:50 -0800 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2015-02-26 19:08:44 +0000 |
commit | 4fbc3828c5ae1e8c5789ede974447fa365f3c5a1 (patch) | |
tree | 9e570b85d5550537b17c85fd1d579fcc43b6136f /src | |
parent | ed46c093cf1f34a2056846ac826b19fa2335ec18 (diff) | |
download | android_packages_apps_Trebuchet-4fbc3828c5ae1e8c5789ede974447fa365f3c5a1.tar.gz android_packages_apps_Trebuchet-4fbc3828c5ae1e8c5789ede974447fa365f3c5a1.tar.bz2 android_packages_apps_Trebuchet-4fbc3828c5ae1e8c5789ede974447fa365f3c5a1.zip |
Updating IconCache to maintain a persistent of icons
> IconDB maintains a DB of icons keyed on ComponentName + User
> During loader, icons & labels are loaded first from the DB, and
if the entry doesn't exist, loaded using packageManager
> After the loader completes, IconDB updates any entry which might have
changed, while the launcher was dead.
Change-Id: I7a6021cb6d1ca1e66fa5a0bdd21e1543e0cf66fc
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/launcher3/AllAppsList.java | 13 | ||||
-rw-r--r-- | src/com/android/launcher3/AppInfo.java | 10 | ||||
-rw-r--r-- | src/com/android/launcher3/IconCache.java | 413 | ||||
-rw-r--r-- | src/com/android/launcher3/Launcher.java | 2 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherBackupHelper.java | 25 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherFiles.java | 4 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 234 | ||||
-rw-r--r-- | src/com/android/launcher3/ShortcutInfo.java | 10 | ||||
-rw-r--r-- | src/com/android/launcher3/Utilities.java | 11 | ||||
-rw-r--r-- | src/com/android/launcher3/Workspace.java | 2 |
10 files changed, 358 insertions, 366 deletions
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java index 72c6693b3..5ed7a629a 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/AllAppsList.java @@ -98,14 +98,14 @@ class AllAppsList { user); for (LauncherActivityInfoCompat info : matches) { - add(new AppInfo(context, info, user, mIconCache, null)); + add(new AppInfo(context, info, user, mIconCache)); } } /** * Remove the apps for the given apk identified by packageName. */ - public void removePackage(String packageName, UserHandleCompat user, boolean clearCache) { + public void removePackage(String packageName, UserHandleCompat user) { final List<AppInfo> data = this.data; for (int i = data.size() - 1; i >= 0; i--) { AppInfo info = data.get(i); @@ -115,9 +115,6 @@ class AllAppsList { data.remove(i); } } - if (clearCache) { - mIconCache.remove(packageName, user); - } } /** @@ -137,7 +134,6 @@ class AllAppsList { && packageName.equals(component.getPackageName())) { if (!findActivity(matches, component)) { removed.add(applicationInfo); - mIconCache.remove(component, user); data.remove(i); } } @@ -150,10 +146,9 @@ class AllAppsList { info.getComponentName().getPackageName(), user, info.getComponentName().getClassName()); if (applicationInfo == null) { - add(new AppInfo(context, info, user, mIconCache, null)); + add(new AppInfo(context, info, user, mIconCache)); } else { - mIconCache.remove(applicationInfo.componentName, user); - mIconCache.getTitleAndIcon(applicationInfo, info, null); + mIconCache.getTitleAndIcon(applicationInfo, info); modified.add(applicationInfo); } } diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index a66bac08a..455c6d16d 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -19,19 +19,15 @@ package com.android.launcher3; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.util.Log; import com.android.launcher3.compat.LauncherActivityInfoCompat; -import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.compat.UserManagerCompat; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; /** * Represents an app in AllAppsView. @@ -77,13 +73,13 @@ public class AppInfo extends ItemInfo { * Must not hold the Context. */ public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user, - IconCache iconCache, HashMap<Object, CharSequence> labelCache) { + IconCache iconCache) { this.componentName = info.getComponentName(); this.container = ItemInfo.NO_ID; flags = initFlags(info); firstInstallTime = info.getFirstInstallTime(); - iconCache.getTitleAndIcon(this, info, labelCache); + iconCache.getTitleAndIcon(this, info); intent = makeLaunchIntent(context, info, user); this.user = user; } diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 5a0875b30..91d4aaf21 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -18,15 +18,19 @@ package com.android.launcher3; import android.app.ActivityManager; import android.content.ComponentName; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.text.TextUtils; @@ -37,15 +41,10 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; -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.List; import java.util.Map.Entry; /** @@ -56,7 +55,6 @@ 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_"; // Empty class name is used for storing package default entry. private static final String EMPTY_CLASS_NAME = "."; @@ -98,7 +96,8 @@ public class IconCache { private final LauncherAppsCompat mLauncherApps; private final HashMap<CacheKey, CacheEntry> mCache = new HashMap<CacheKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY); - private int mIconDpi; + private final int mIconDpi; + private final IconDB mIconDb; public IconCache(Context context) { ActivityManager activityManager = @@ -109,13 +108,10 @@ public class IconCache { mUserManager = UserManagerCompat.getInstance(mContext); mLauncherApps = LauncherAppsCompat.getInstance(mContext); mIconDpi = activityManager.getLauncherLargeIconDensity(); - - // need to set mIconDpi before getting default icon - UserHandleCompat myUser = UserHandleCompat.myUserHandle(); - mDefaultIcons.put(myUser, makeDefaultIcon(myUser)); + mIconDb = new IconDB(context); } - public Drawable getFullResDefaultActivityIcon() { + private Drawable getFullResDefaultActivityIcon() { return getFullResIcon(Resources.getSystem(), android.R.mipmap.sym_def_app_icon); } @@ -188,9 +184,9 @@ public class IconCache { } /** - * Remove any records for the supplied package name. + * Remove any records for the supplied package name from memory. */ - public synchronized void remove(String packageName, UserHandleCompat user) { + private void removeFromMemCacheLocked(String packageName, UserHandleCompat user) { HashSet<CacheKey> forDeletion = new HashSet<CacheKey>(); for (CacheKey key: mCache.keySet()) { if (key.componentName.getPackageName().equals(packageName) @@ -204,6 +200,140 @@ public class IconCache { } /** + * Updates the entries related to the given package in memory and persistent DB. + */ + public synchronized void updateIconsForPkg(String packageName, UserHandleCompat user) { + removeIconsForPkg(packageName, user); + try { + PackageInfo info = mPackageManager.getPackageInfo(packageName, + PackageManager.GET_UNINSTALLED_PACKAGES); + long userSerial = mUserManager.getSerialNumberForUser(user); + for (LauncherActivityInfoCompat app : mLauncherApps.getActivityList(packageName, user)) { + addIconToDB(app, info, userSerial); + } + } catch (NameNotFoundException e) { + Log.d(TAG, "Package not found", e); + return; + } + } + + /** + * Removes the entries related to the given package in memory and persistent DB. + */ + public synchronized void removeIconsForPkg(String packageName, UserHandleCompat user) { + removeFromMemCacheLocked(packageName, user); + long userSerial = mUserManager.getSerialNumberForUser(user); + mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME, + IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?", + new String[] {packageName + "/%", Long.toString(userSerial)}); + } + + /** + * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in + * the DB and are updated. + * @return The set of packages for which icons have updated. + */ + public HashSet<String> updateDBIcons(UserHandleCompat user, List<LauncherActivityInfoCompat> apps) { + long userSerial = mUserManager.getSerialNumberForUser(user); + PackageManager pm = mContext.getPackageManager(); + HashMap<String, PackageInfo> pkgInfoMap = new HashMap<String, PackageInfo>(); + for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) { + pkgInfoMap.put(info.packageName, info); + } + + HashMap<ComponentName, LauncherActivityInfoCompat> componentMap = new HashMap<>(); + for (LauncherActivityInfoCompat app : apps) { + componentMap.put(app.getComponentName(), app); + } + + Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME, + new String[] {IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, + IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION}, + IconDB.COLUMN_USER + " = ? ", + new String[] {Long.toString(userSerial)}, + null, null, null); + + final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); + final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); + final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); + final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); + + HashSet<Integer> itemsToRemove = new HashSet<Integer>(); + HashSet<String> updatedPackages = new HashSet<String>(); + + while (c.moveToNext()) { + String cn = c.getString(indexComponent); + ComponentName component = ComponentName.unflattenFromString(cn); + PackageInfo info = pkgInfoMap.get(component.getPackageName()); + if (info == null) { + itemsToRemove.add(c.getInt(rowIndex)); + continue; + } + if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { + // Application is not present + continue; + } + + long updateTime = c.getLong(indexLastUpdate); + int version = c.getInt(indexVersion); + LauncherActivityInfoCompat app = componentMap.remove(component); + if (version == info.versionCode && updateTime == info.lastUpdateTime) { + continue; + } + if (app == null) { + itemsToRemove.add(c.getInt(rowIndex)); + continue; + } + ContentValues values = updateCacheAndGetContentValues(app); + mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values, + IconDB.COLUMN_COMPONENT + " = ?", + new String[] { cn }); + + updatedPackages.add(component.getPackageName()); + } + c.close(); + if (!itemsToRemove.isEmpty()) { + mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME, + IconDB.COLUMN_ROWID + " IN ( " + TextUtils.join(", ", itemsToRemove) +" )", + null); + } + + // Insert remaining apps. + for (LauncherActivityInfoCompat app : componentMap.values()) { + PackageInfo info = pkgInfoMap.get(app.getComponentName().getPackageName()); + if (info == null) { + continue; + } + addIconToDB(app, info, userSerial); + } + return updatedPackages; + } + + private void addIconToDB(LauncherActivityInfoCompat app, PackageInfo info, long userSerial) { + ContentValues values = updateCacheAndGetContentValues(app); + values.put(IconDB.COLUMN_COMPONENT, app.getComponentName().flattenToString()); + values.put(IconDB.COLUMN_USER, userSerial); + values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime); + values.put(IconDB.COLUMN_VERSION, info.versionCode); + mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values, + SQLiteDatabase.CONFLICT_REPLACE); + } + + private ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app) { + CacheEntry entry = new CacheEntry(); + entry.icon = Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext); + entry.title = app.getLabel(); + entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser()); + mCache.put(new CacheKey(app.getComponentName(), app.getUser()), entry); + + ContentValues values = new ContentValues(); + values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(entry.icon)); + values.put(IconDB.COLUMN_LABEL, entry.title.toString()); + return values; + } + + + /** * Empty out the cache. */ public synchronized void flush() { @@ -227,10 +357,8 @@ public class IconCache { /** * Fill in "application" with the icon and label for "info." */ - public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info, - HashMap<Object, CharSequence> labelCache) { - CacheEntry entry = cacheLocked(application.componentName, info, labelCache, - info.getUser(), false); + public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info) { + CacheEntry entry = cacheLocked(application.componentName, info, info.getUser(), false); application.title = entry.title; application.iconBitmap = entry.icon; @@ -246,15 +374,16 @@ public class IconCache { } LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user); - CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, true); + CacheEntry entry = cacheLocked(component, launcherActInfo, user, true); return entry.icon; } /** - * Fill in "shortcutInfo" with the icon and label for "info." + * Fill in {@param shortcutInfo} with the icon and label for {@param intent}. If the + * corresponding activity is not found, it reverts to the package icon. */ public synchronized void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, - UserHandleCompat user, boolean usePkgIcon) { + UserHandleCompat user) { ComponentName component = intent.getComponent(); // null info means not installed, but if we have a component from the intent then // we should still look in the cache for restored app icons. @@ -263,16 +392,22 @@ public class IconCache { shortcutInfo.title = ""; shortcutInfo.usingFallbackIcon = true; } else { - LauncherActivityInfoCompat launcherActInfo = - mLauncherApps.resolveActivity(intent, user); - CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon); - - shortcutInfo.setIcon(entry.icon); - shortcutInfo.title = entry.title; - shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); + LauncherActivityInfoCompat info = mLauncherApps.resolveActivity(intent, user); + getTitleAndIcon(shortcutInfo, component, info, user, true); } } + /** + * Fill in {@param shortcutInfo} with the icon and label for {@param info} + */ + public synchronized void getTitleAndIcon( + ShortcutInfo shortcutInfo, ComponentName component, LauncherActivityInfoCompat info, + UserHandleCompat user, boolean usePkgIcon) { + CacheEntry entry = cacheLocked(component, info, user, usePkgIcon); + shortcutInfo.setIcon(entry.icon); + shortcutInfo.title = entry.title; + shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); + } public synchronized Bitmap getDefaultIcon(UserHandleCompat user) { if (!mDefaultIcons.containsKey(user)) { @@ -281,16 +416,6 @@ public class IconCache { return mDefaultIcons.get(user); } - public synchronized Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info, - HashMap<Object, CharSequence> labelCache) { - if (info == null || component == null) { - return null; - } - - CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false); - return entry.icon; - } - public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) { return mDefaultIcons.get(user) == icon; } @@ -300,35 +425,17 @@ public class IconCache { * This method is not thread safe, it must be called from a synchronized method. */ private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info, - HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) { + UserHandleCompat user, boolean usePackageIcon) { CacheKey cacheKey = new CacheKey(componentName, user); CacheEntry entry = mCache.get(cacheKey); if (entry == null) { entry = new CacheEntry(); - mCache.put(cacheKey, entry); - if (info != null) { - ComponentName labelKey = info.getComponentName(); - if (labelCache != null && labelCache.containsKey(labelKey)) { - entry.title = labelCache.get(labelKey).toString(); - } else { - entry.title = info.getLabel().toString(); - if (labelCache != null) { - labelCache.put(labelKey, entry.title); - } - } - - entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); - entry.icon = Utilities.createIconBitmap( - info.getBadgedIcon(mIconDpi), mContext); - } else { - entry.title = ""; - Bitmap preloaded = getPreloadedIcon(componentName, user); - if (preloaded != null) { - if (DEBUG) Log.d(TAG, "using preloaded icon for " + - componentName.toShortString()); - entry.icon = preloaded; + // Check the DB first. + if (!getEntryFromDB(componentName, user, entry)) { + if (info != null) { + entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext); } else { if (usePackageIcon) { CacheEntry packageEntry = getEntryForPackage( @@ -338,6 +445,7 @@ public class IconCache { componentName.toShortString()); entry.icon = packageEntry.icon; entry.title = packageEntry.title; + entry.contentDescription = packageEntry.contentDescription; } } if (entry.icon == null) { @@ -347,6 +455,11 @@ public class IconCache { } } } + + if (TextUtils.isEmpty(entry.title) && info != null) { + entry.title = info.getLabel().toString(); + entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); + } } return entry; } @@ -357,7 +470,7 @@ public class IconCache { */ public synchronized void cachePackageInstallInfo(String packageName, UserHandleCompat user, Bitmap icon, CharSequence title) { - remove(packageName, user); + removeFromMemCacheLocked(packageName, user); CacheEntry entry = getEntryForPackage(packageName, user); if (!TextUtils.isEmpty(title)) { @@ -379,48 +492,36 @@ public class IconCache { if (entry == null) { entry = new CacheEntry(); entry.title = ""; + entry.contentDescription = ""; mCache.put(cacheKey, entry); try { ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0); - entry.title = info.loadLabel(mPackageManager); entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext); + entry.title = info.loadLabel(mPackageManager); + entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); } catch (NameNotFoundException e) { if (DEBUG) Log.d(TAG, "Application not installed " + packageName); } - - if (entry.icon == null) { - entry.icon = getPreloadedIcon(cn, user); - } } return entry; } - public synchronized HashMap<ComponentName,Bitmap> getAllIcons() { - HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>(); - for (CacheKey ck : mCache.keySet()) { - final CacheEntry e = mCache.get(ck); - set.put(ck.componentName, e.icon); - } - 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) { + public void preloadIcon(ComponentName componentName, Bitmap icon, int dpi, String label, + long userSerial) { // TODO rescale to the correct native DPI try { - PackageManager packageManager = context.getPackageManager(); + PackageManager packageManager = mContext.getPackageManager(); packageManager.getActivityIcon(componentName); // component is present on the system already, do nothing return; @@ -428,100 +529,86 @@ public class IconCache { // pass } - final String key = componentName.flattenToString(); - FileOutputStream resourceFile = null; + ContentValues values = new ContentValues(); + values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString()); + values.put(IconDB.COLUMN_USER, userSerial); + values.put(IconDB.COLUMN_ICON, ItemInfo.flattenBitmap(icon)); + values.put(IconDB.COLUMN_LABEL, label); + mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values, + SQLiteDatabase.CONFLICT_REPLACE); + } + + private boolean getEntryFromDB(ComponentName component, UserHandleCompat user, CacheEntry entry) { + Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME, + new String[] {IconDB.COLUMN_ICON, IconDB.COLUMN_LABEL}, + IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", + new String[] {component.flattenToString(), + Long.toString(mUserManager.getSerialNumberForUser(user))}, + null, null, 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); + if (c.moveToNext()) { + entry.icon = Utilities.createIconBitmap(c, 0, mContext); + entry.title = c.getString(1); + if (entry.title == null) { + entry.title = ""; + entry.contentDescription = ""; + } else { + entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); } + return true; } + } finally { + c.close(); } + return false; } - /** - * 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, UserHandleCompat user) { - final String key = componentName.flattenToShortString(); + private static final class IconDB extends SQLiteOpenHelper { + private final static int DB_VERSION = 1; - // We don't keep icons for other profiles in persistent cache. - if (!user.equals(UserHandleCompat.myUserHandle())) { - return null; + private final static String TABLE_NAME = "icons"; + private final static String COLUMN_ROWID = "rowid"; + private final static String COLUMN_COMPONENT = "componentName"; + private final static String COLUMN_USER = "profileId"; + private final static String COLUMN_LAST_UPDATED = "lastUpdated"; + private final static String COLUMN_VERSION = "version"; + private final static String COLUMN_ICON = "icon"; + private final static String COLUMN_LABEL = "label"; + + public IconDB(Context context) { + super(context, LauncherFiles.APP_ICONS_DB, null, DB_VERSION); } - 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); - } 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); - } - } + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + + COLUMN_COMPONENT + " TEXT NOT NULL, " + + COLUMN_USER + " INTEGER NOT NULL, " + + COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " + + COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " + + COLUMN_ICON + " BLOB, " + + COLUMN_LABEL + " TEXT, " + + "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " + + ");"); } - return icon; - } + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion != newVersion) { + clearDB(db); + } + } - /** - * Remove a pre-loaded icon from the persistent icon cache. - * - * @param componentName the component that should own the icon - */ - public void deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) { - // We don't keep icons for other profiles in persistent cache. - if (!user.equals(UserHandleCompat.myUserHandle()) || componentName == null) { - return; + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion != newVersion) { + clearDB(db); + } } - remove(componentName, user); - boolean success = mContext.deleteFile(getResourceFilename(componentName)); - if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache"); - } - private static String getResourceFilename(ComponentName component) { - String resourceName = component.flattenToShortString(); - String filename = resourceName.replace(File.separatorChar, '_'); - return RESOURCE_FILE_PREFIX + filename; + private void clearDB(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); + onCreate(db); + } } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 58b085480..06a8ab9a2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -5033,7 +5033,7 @@ public class Launcher extends Activity if (activityInfo == null) { return null; } - return new AppInfo(this, activityInfo, myUser, mIconCache, null); + return new AppInfo(this, activityInfo, myUser, mIconCache); } public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption, diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 353bf3f00..97ff32790 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -144,6 +144,7 @@ public class LauncherBackupHelper implements BackupHelper { private final HashSet<String> mExistingKeys; private final ArrayList<Key> mKeys; private final ItemTypeMatcher[] mItemTypeMatchers; + private final long mUserSerial; private IconCache mIconCache; private BackupManager mBackupManager; @@ -161,6 +162,9 @@ public class LauncherBackupHelper implements BackupHelper { mKeys = new ArrayList<Key>(); restoreSuccessful = true; mItemTypeMatchers = new ItemTypeMatcher[CommonAppTypeParser.SUPPORTED_TYPE_COUNT]; + + UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); + mUserSerial = userManager.getSerialNumberForUser(UserHandleCompat.myUserHandle()); } private void dataChanged() { @@ -297,6 +301,12 @@ public class LauncherBackupHelper implements BackupHelper { if (!restoreSuccessful) { return; } + if (!initializeIconCache()) { + // During restore we do not need an initialized instance of IconCache. We can create + // a temporary icon cache here, as the process will be rebooted after restore + // is complete. + mIconCache = new IconCache(mContext); + } int dataSize = data.size(); if (mBuffer.length < dataSize) { @@ -601,7 +611,8 @@ public class LauncherBackupHelper implements BackupHelper { Log.w(TAG, "failed to unpack icon for " + key.name); } if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name); - IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(key.name), icon, res.dpi); + mIconCache.preloadIcon(ComponentName.unflattenFromString(key.name), icon, res.dpi, + "" /* label */, mUserSerial); } /** @@ -693,8 +704,8 @@ public class LauncherBackupHelper implements BackupHelper { if (icon == null) { Log.w(TAG, "failed to unpack widget icon for " + key.name); } else { - IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(widget.provider), - icon, widget.icon.dpi); + mIconCache.preloadIcon(ComponentName.unflattenFromString(widget.provider), + icon, widget.icon.dpi, widget.label, mUserSerial); } } @@ -1145,9 +1156,11 @@ public class LauncherBackupHelper implements BackupHelper { final LauncherAppState appState = LauncherAppState.getInstanceNoCreate(); if (appState == null) { - Throwable stackTrace = new Throwable(); - stackTrace.fillInStackTrace(); - Log.w(TAG, "Failed to get app state during backup/restore", stackTrace); + if (DEBUG) { + Throwable stackTrace = new Throwable(); + stackTrace.fillInStackTrace(); + Log.w(TAG, "Failed to get app state during backup/restore", stackTrace); + } return false; } mIconCache = appState.getIconCache(); diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index fa053650f..cedb3975d 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -25,6 +25,7 @@ public class LauncherFiles { WallpaperCropActivity.class.getName(); public static final String WALLPAPER_IMAGES_DB = "saved_wallpaper_images.db"; public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db"; + public static final String APP_ICONS_DB = "app_icons.db"; public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList( DEFAULT_WALLPAPER_THUMBNAIL, @@ -36,5 +37,6 @@ public class LauncherFiles { STATS_LOG, WALLPAPER_CROP_PREFERENCES_KEY + XML, WALLPAPER_IMAGES_DB, - WIDGET_PREVIEWS_DB)); + WIDGET_PREVIEWS_DB, + APP_ICONS_DB)); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 3983835dc..489e3299d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -36,7 +36,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Rect; import android.net.Uri; import android.os.Environment; @@ -163,9 +162,6 @@ public class LauncherModel extends BroadcastReceiver // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders() static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>(); - // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database - static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>(); - // sBgWorkspaceScreens is the ordered set of workspace screens. static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>(); @@ -1131,7 +1127,6 @@ public class LauncherModel extends BroadcastReceiver break; } sBgItemsIdMap.remove(item.id); - sBgDbIconCache.remove(item); } } } @@ -1204,7 +1199,6 @@ public class LauncherModel extends BroadcastReceiver synchronized (sBgLock) { sBgItemsIdMap.remove(info.id); sBgFolders.remove(info.id); - sBgDbIconCache.remove(info); sBgWorkspaceItems.remove(info); } @@ -1214,7 +1208,6 @@ public class LauncherModel extends BroadcastReceiver synchronized (sBgLock) { for (ItemInfo childInfo : info.contents) { sBgItemsIdMap.remove(childInfo.id); - sBgDbIconCache.remove(childInfo); } } } @@ -1481,12 +1474,9 @@ public class LauncherModel extends BroadcastReceiver private boolean mLoadAndBindStepFinished; private int mFlags; - private HashMap<Object, CharSequence> mLabelCache; - LoaderTask(Context context, boolean isLaunching, int flags) { mContext = context; mIsLaunching = isLaunching; - mLabelCache = new HashMap<Object, CharSequence>(); mFlags = flags; } @@ -1635,15 +1625,6 @@ public class LauncherModel extends BroadcastReceiver } } - // Update the saved icons if necessary - if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons"); - synchronized (sBgLock) { - for (Object key : sBgDbIconCache.keySet()) { - updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key)); - } - sBgDbIconCache.clear(); - } - if (LauncherAppState.isDisableAllApps()) { // Ensure that all the applications that are in the system are // represented on the home screen. @@ -1819,7 +1800,6 @@ public class LauncherModel extends BroadcastReceiver sBgAppWidgets.clear(); sBgFolders.clear(); sBgItemsIdMap.clear(); - sBgDbIconCache.clear(); sBgWorkspaceScreens.clear(); } } @@ -2068,8 +2048,8 @@ public class LauncherModel extends BroadcastReceiver if (itemReplaced) { if (user.equals(UserHandleCompat.myUserHandle())) { - info = getShortcutInfo(manager, intent, user, context, null, - iconIndex, titleIndex, mLabelCache, false); + info = getAppShortcutInfo(manager, intent, user, context, null, + iconIndex, titleIndex, false); } else { // Don't replace items for other profiles. itemsToRemove.add(id); @@ -2089,8 +2069,8 @@ public class LauncherModel extends BroadcastReceiver } } else if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - info = getShortcutInfo(manager, intent, user, context, c, - iconIndex, titleIndex, mLabelCache, allowMissingTarget); + info = getAppShortcutInfo(manager, intent, user, context, c, + iconIndex, titleIndex, allowMissingTarget); } else { info = getShortcutInfo(c, context, iconTypeIndex, iconPackageIndex, iconResourceIndex, iconIndex, @@ -2145,10 +2125,6 @@ public class LauncherModel extends BroadcastReceiver break; } sBgItemsIdMap.put(info.id, info); - - // now that we've loaded everthing re-save it with the - // icon in case it disappears somehow. - queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex); } else { throw new RuntimeException("Unexpected null ShortcutInfo"); } @@ -2842,20 +2818,48 @@ public class LauncherModel extends BroadcastReceiver if (apps == null || apps.isEmpty()) { return; } - // Sort the applications by name - final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; - Collections.sort(apps, - new LauncherModel.ShortcutNameComparator(mLabelCache)); - if (DEBUG_LOADERS) { - Log.d(TAG, "sort took " - + (SystemClock.uptimeMillis()-sortTime) + "ms"); + + // Update icon cache + HashSet<String> updatedPackages = mIconCache.updateDBIcons(user, apps); + + // If any package icon has changed (app was updated while launcher was dead), + // update the corresponding shortcuts. + if (!updatedPackages.isEmpty()) { + final ArrayList<ShortcutInfo> updates = new ArrayList<ShortcutInfo>(); + synchronized (sBgLock) { + for (ItemInfo info : sBgItemsIdMap.values()) { + if (info instanceof ShortcutInfo && user.equals(info.user) + && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + ShortcutInfo si = (ShortcutInfo) info; + ComponentName cn = si.getTargetComponent(); + if (cn != null && updatedPackages.contains(cn.getPackageName())) { + si.updateIcon(mIconCache); + updates.add(si); + } + } + } + } + + if (!updates.isEmpty()) { + final UserHandleCompat userFinal = user; + mHandler.post(new Runnable() { + + public void run() { + Callbacks cb = getCallback(); + if (cb != null) { + cb.bindShortcutsChanged( + updates, new ArrayList<ShortcutInfo>(), userFinal); + } + } + }); + } } // Create the ApplicationInfos for (int i = 0; i < apps.size(); i++) { LauncherActivityInfoCompat app = apps.get(i); // This builds the icon bitmaps. - mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache)); + mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache)); } if (ADD_MANAGED_PROFILE_SHORTCUTS && !user.equals(UserHandleCompat.myUserHandle())) { @@ -2987,7 +2991,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], mUser); + mIconCache.updateIconsForPkg(packages[i], mUser); mBgAllAppsList.addPackage(context, packages[i], mUser); } @@ -3019,6 +3023,7 @@ public class LauncherModel extends BroadcastReceiver case OP_UPDATE: for (int i=0; i<N; i++) { if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]); + mIconCache.updateIconsForPkg(packages[i], mUser); mBgAllAppsList.updatePackage(context, packages[i], mUser); WidgetPreviewLoader.removePackageFromDb( mApp.getWidgetPreviewCacheDb(), packages[i]); @@ -3038,12 +3043,15 @@ public class LauncherModel extends BroadcastReceiver shortcutSet.removeAll(Arrays.asList(mPackages)); prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit(); } + for (int i=0; i<N; i++) { + if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]); + mIconCache.removeIconsForPkg(packages[i], mUser); + } // Fall through case OP_UNAVAILABLE: - boolean clearCache = mOp == OP_REMOVE; for (int i=0; i<N; i++) { if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]); - mBgAllAppsList.removePackage(packages[i], mUser, clearCache); + mBgAllAppsList.removePackage(packages[i], mUser); WidgetPreviewLoader.removePackageFromDb( mApp.getWidgetPreviewCacheDb(), packages[i]); } @@ -3136,7 +3144,6 @@ public class LauncherModel extends BroadcastReceiver AppInfo appInfo = addedOrUpdatedApps.get(cn); if (si.isPromise()) { - mIconCache.deletePreloadedIcon(cn, mUser); if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) { // Auto install icon PackageManager pm = context.getPackageManager(); @@ -3381,7 +3388,7 @@ public class LauncherModel extends BroadcastReceiver int promiseType) { final ShortcutInfo info = new ShortcutInfo(); info.user = UserHandleCompat.myUserHandle(); - mIconCache.getTitleAndIcon(info, intent, info.user, true); + mIconCache.getTitleAndIcon(info, intent, info.user); if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) { String title = (cursor != null) ? cursor.getString(titleIndex) : null; @@ -3424,22 +3431,13 @@ public class LauncherModel extends BroadcastReceiver } /** - * This is called from the code that adds shortcuts from the intent receiver. This - * doesn't have a Cursor, but - */ - public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, - UserHandleCompat user, Context context) { - return getShortcutInfo(manager, intent, user, context, null, -1, -1, null, false); - } - - /** * Make an ShortcutInfo object for a shortcut that is an application. * * If c is not null, then it will be used to fill in missing data like the title and icon. */ - public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, + public ShortcutInfo getAppShortcutInfo(PackageManager manager, Intent intent, UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex, - HashMap<Object, CharSequence> labelCache, boolean allowMissingTarget) { + boolean allowMissingTarget) { if (user == null) { Log.d(TAG, "Null user found in getShortcutInfo"); return null; @@ -3461,48 +3459,22 @@ public class LauncherModel extends BroadcastReceiver } final ShortcutInfo info = new ShortcutInfo(); - - // the resource -- This may implicitly give us back the fallback icon, - // but don't worry about that. All we're doing with usingFallbackIcon is - // to avoid saving lots of copies of that in the database, and most apps - // have icons anyway. - Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache); - - // the db - if (icon == null) { - if (c != null) { - icon = getIconFromCursor(c, iconIndex, context); - } - } - // the fallback icon - if (icon == null) { - icon = mIconCache.getDefaultIcon(user); - info.usingFallbackIcon = true; - } - info.setIcon(icon); - - // From the cache. - if (labelCache != null) { - info.title = labelCache.get(componentName); + mIconCache.getTitleAndIcon(info, componentName, lai, user, false); + if (mIconCache.isDefaultIcon(info.getIcon(mIconCache), user) && c != null) { + Bitmap icon = Utilities.createIconBitmap(c, iconIndex, context); + info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon); } - // from the resource - if (info.title == null && lai != null) { - info.title = lai.getLabel(); - if (labelCache != null) { - labelCache.put(componentName, info.title); - } - } // from the db - if (info.title == null) { - if (c != null) { - info.title = c.getString(titleIndex); - } + if (TextUtils.isEmpty(info.title) && c != null) { + info.title = c.getString(titleIndex); } + // fall back to the class name of the activity if (info.title == null) { info.title = componentName.getClassName(); } + info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; info.user = user; info.contentDescription = mUserManager.getBadgedLabelForUser( @@ -3581,7 +3553,7 @@ public class LauncherModel extends BroadcastReceiver icon = Utilities.createIconBitmap(packageName, resourceName, mIconCache, context); // the db if (icon == null) { - icon = getIconFromCursor(c, iconIndex, context); + icon = Utilities.createIconBitmap(c, iconIndex, context); } // the fallback icon if (icon == null) { @@ -3590,7 +3562,7 @@ public class LauncherModel extends BroadcastReceiver } break; case LauncherSettings.Favorites.ICON_TYPE_BITMAP: - icon = getIconFromCursor(c, iconIndex, context); + icon = Utilities.createIconBitmap(c, iconIndex, context); if (icon == null) { icon = mIconCache.getDefaultIcon(info.user); info.customIcon = false; @@ -3609,22 +3581,6 @@ public class LauncherModel extends BroadcastReceiver return info; } - Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) { - @SuppressWarnings("all") // suppress dead code warning - final boolean debug = false; - if (debug) { - Log.d(TAG, "getIconFromCursor app=" - + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE))); - } - byte[] data = c.getBlob(iconIndex); - try { - return Utilities.createIconBitmap( - BitmapFactory.decodeByteArray(data, 0, data.length), context); - } catch (Exception e) { - return null; - } - } - ShortcutInfo infoFromShortcutIntent(Context context, Intent data) { Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); @@ -3673,45 +3629,6 @@ public class LauncherModel extends BroadcastReceiver return info; } - boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c, - int iconIndex) { - // If apps can't be on SD, don't even bother. - if (!mAppsCanBeOnRemoveableStorage) { - return false; - } - // If this icon doesn't have a custom icon, check to see - // what's stored in the DB, and if it doesn't match what - // we're going to show, store what we are going to show back - // into the DB. We do this so when we're loading, if the - // package manager can't find an icon (for example because - // the app is on SD) then we can use that instead. - if (!info.customIcon && !info.usingFallbackIcon) { - cache.put(info, c.getBlob(iconIndex)); - return true; - } - return false; - } - void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) { - boolean needSave = false; - try { - if (data != null) { - Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length); - Bitmap loaded = info.getIcon(mIconCache); - needSave = !saved.sameAs(loaded); - } else { - needSave = true; - } - } catch (Exception e) { - needSave = true; - } - if (needSave) { - Log.d(TAG, "going to save icon bitmap for info=" + info); - // This is slower than is ideal, but this only happens once - // or when the app is updated with a new icon. - updateItemInDatabase(context, info); - } - } - /** * Return an existing FolderInfo object if we have encountered this ID previously, * or make a new one. @@ -3761,38 +3678,7 @@ public class LauncherModel extends BroadcastReceiver return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name); } } - public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> { - private Collator mCollator; - private HashMap<Object, CharSequence> mLabelCache; - ShortcutNameComparator(PackageManager pm) { - mLabelCache = new HashMap<Object, CharSequence>(); - mCollator = Collator.getInstance(); - } - ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) { - mLabelCache = labelCache; - mCollator = Collator.getInstance(); - } - public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) { - String labelA, labelB; - ComponentName keyA = a.getComponentName(); - ComponentName keyB = b.getComponentName(); - if (mLabelCache.containsKey(keyA)) { - labelA = mLabelCache.get(keyA).toString(); - } else { - labelA = a.getLabel().toString().trim(); - - mLabelCache.put(keyA, labelA); - } - if (mLabelCache.containsKey(keyB)) { - labelB = mLabelCache.get(keyB).toString(); - } else { - labelB = b.getLabel().toString().trim(); - mLabelCache.put(keyB, labelB); - } - return mCollator.compare(labelA, labelB); - } - }; public static class WidgetAndShortcutNameComparator implements Comparator<Object> { private final AppWidgetManagerCompat mManager; private final PackageManager mPackageManager; diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 15d6a3e1c..08ffaa299 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.util.Log; +import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.UserHandleCompat; import java.util.ArrayList; @@ -190,8 +191,9 @@ public class ShortcutInfo extends ItemInfo { } public void updateIcon(IconCache iconCache) { - mIcon = iconCache.getIcon(promisedIntent != null ? promisedIntent : intent, user); - usingFallbackIcon = iconCache.isDefaultIcon(mIcon, user); + if (itemType == Favorites.ITEM_TYPE_APPLICATION) { + iconCache.getTitleAndIcon(this, promisedIntent != null ? promisedIntent : intent, user); + } } @Override @@ -213,9 +215,9 @@ public class ShortcutInfo extends ItemInfo { if (!usingFallbackIcon) { writeBitmap(values, mIcon); } - values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, - LauncherSettings.BaseLauncherColumns.ICON_TYPE_RESOURCE); if (iconResource != null) { + values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, + LauncherSettings.BaseLauncherColumns.ICON_TYPE_RESOURCE); values.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE, iconResource.packageName); values.put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE, diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 1a9b9a16c..497b43874 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -31,7 +31,9 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; +import android.database.Cursor; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; @@ -112,6 +114,15 @@ public final class Utilities { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } + static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { + byte[] data = c.getBlob(iconIndex); + try { + return createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), context); + } catch (Exception e) { + return null; + } + } + /** * Returns a bitmap suitable for the all apps view. If the package or the resource do not * exist, it returns null. diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 626154e7c..b9c1f4d3d 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4772,7 +4772,7 @@ public class Workspace extends SmoothPagedView if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) { // For auto install apps update the icon as well as label. mIconCache.getTitleAndIcon(shortcutInfo, - shortcutInfo.promisedIntent, user, true); + shortcutInfo.promisedIntent, user); } else { // Only update the icon for restored apps. shortcutInfo.updateIcon(mIconCache); |