blob: bb71d776ca66b5b5c51ab78f781d3046765dd104 [file] [log] [blame]
Joe Onorato0589f0f2010-02-08 13:44:00 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
Joe Onorato0589f0f2010-02-08 13:44:00 -080018
Winson Chungd83f5f42012-02-13 14:27:42 -080019import android.app.ActivityManager;
Joe Onorato0589f0f2010-02-08 13:44:00 -080020import android.content.ComponentName;
Winson Chungd83f5f42012-02-13 14:27:42 -080021import android.content.Context;
Joe Onorato0589f0f2010-02-08 13:44:00 -080022import android.content.Intent;
Michael Jurkadac85912012-05-18 15:04:49 -070023import android.content.pm.ActivityInfo;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070024import android.content.pm.ApplicationInfo;
Joe Onorato0589f0f2010-02-08 13:44:00 -080025import android.content.pm.PackageManager;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070026import android.content.pm.PackageManager.NameNotFoundException;
Joe Onorato0589f0f2010-02-08 13:44:00 -080027import android.content.pm.ResolveInfo;
Michael Jurkac9a96192010-11-01 11:52:08 -070028import android.content.res.Resources;
Joe Onorato0589f0f2010-02-08 13:44:00 -080029import android.graphics.Bitmap;
Chris Wren6d0dde02014-02-10 12:16:54 -050030import android.graphics.BitmapFactory;
Romain Guya28fd3f2010-03-15 14:44:42 -070031import android.graphics.Canvas;
Sunny Goyala22666f2014-09-18 13:25:15 -070032import android.graphics.drawable.BitmapDrawable;
Joe Onorato0589f0f2010-02-08 13:44:00 -080033import android.graphics.drawable.Drawable;
Sunny Goyal34942622014-08-29 17:20:55 -070034import android.text.TextUtils;
Chris Wren6d0dde02014-02-10 12:16:54 -050035import android.util.Log;
Joe Onorato0589f0f2010-02-08 13:44:00 -080036
Kenny Guyed131872014-04-30 03:02:21 +010037import com.android.launcher3.compat.LauncherActivityInfoCompat;
38import com.android.launcher3.compat.LauncherAppsCompat;
39import com.android.launcher3.compat.UserHandleCompat;
40import com.android.launcher3.compat.UserManagerCompat;
41
Chris Wren6d0dde02014-02-10 12:16:54 -050042import java.io.ByteArrayOutputStream;
43import java.io.File;
44import java.io.FileInputStream;
45import java.io.FileNotFoundException;
46import java.io.FileOutputStream;
47import java.io.IOException;
Joe Onorato0589f0f2010-02-08 13:44:00 -080048import java.util.HashMap;
Chris Wren6d0dde02014-02-10 12:16:54 -050049import java.util.HashSet;
Adam Cohenb6d33df2013-10-15 10:18:02 -070050import java.util.Iterator;
51import java.util.Map.Entry;
Joe Onorato0589f0f2010-02-08 13:44:00 -080052
53/**
54 * Cache of application icons. Icons can be made from any thread.
55 */
56public class IconCache {
Sunny Goyal0fc1be12014-08-11 17:05:23 -070057
Joe Onorato0589f0f2010-02-08 13:44:00 -080058 private static final String TAG = "Launcher.IconCache";
59
60 private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
Chris Wren6d0dde02014-02-10 12:16:54 -050061 private static final String RESOURCE_FILE_PREFIX = "icon_";
62
Sunny Goyal0fc1be12014-08-11 17:05:23 -070063 // Empty class name is used for storing package default entry.
64 private static final String EMPTY_CLASS_NAME = ".";
65
Sunny Goyalbbef77d2014-09-09 16:27:55 -070066 private static final boolean DEBUG = false;
Joe Onorato0589f0f2010-02-08 13:44:00 -080067
68 private static class CacheEntry {
69 public Bitmap icon;
Kenny Guyd6fe5262014-07-21 17:11:41 +010070 public CharSequence title;
71 public CharSequence contentDescription;
Joe Onorato0589f0f2010-02-08 13:44:00 -080072 }
73
Kenny Guyed131872014-04-30 03:02:21 +010074 private static class CacheKey {
75 public ComponentName componentName;
76 public UserHandleCompat user;
77
78 CacheKey(ComponentName componentName, UserHandleCompat user) {
79 this.componentName = componentName;
80 this.user = user;
81 }
82
83 @Override
84 public int hashCode() {
85 return componentName.hashCode() + user.hashCode();
86 }
87
88 @Override
89 public boolean equals(Object o) {
90 CacheKey other = (CacheKey) o;
91 return other.componentName.equals(componentName) && other.user.equals(user);
92 }
93 }
94
95 private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons =
96 new HashMap<UserHandleCompat, Bitmap>();
Daniel Sandlercc8befa2013-06-11 14:45:48 -040097 private final Context mContext;
Romain Guya28fd3f2010-03-15 14:44:42 -070098 private final PackageManager mPackageManager;
Kenny Guyed131872014-04-30 03:02:21 +010099 private final UserManagerCompat mUserManager;
100 private final LauncherAppsCompat mLauncherApps;
101 private final HashMap<CacheKey, CacheEntry> mCache =
102 new HashMap<CacheKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
Michael Jurkac9a96192010-11-01 11:52:08 -0700103 private int mIconDpi;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800104
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400105 public IconCache(Context context) {
Winson Chungd83f5f42012-02-13 14:27:42 -0800106 ActivityManager activityManager =
107 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
108
Joe Onorato0589f0f2010-02-08 13:44:00 -0800109 mContext = context;
110 mPackageManager = context.getPackageManager();
Kenny Guyed131872014-04-30 03:02:21 +0100111 mUserManager = UserManagerCompat.getInstance(mContext);
112 mLauncherApps = LauncherAppsCompat.getInstance(mContext);
Winson Chungd83f5f42012-02-13 14:27:42 -0800113 mIconDpi = activityManager.getLauncherLargeIconDensity();
114
Michael Jurkac9a96192010-11-01 11:52:08 -0700115 // need to set mIconDpi before getting default icon
Kenny Guyed131872014-04-30 03:02:21 +0100116 UserHandleCompat myUser = UserHandleCompat.myUserHandle();
117 mDefaultIcons.put(myUser, makeDefaultIcon(myUser));
Romain Guya28fd3f2010-03-15 14:44:42 -0700118 }
119
Michael Jurkac9a96192010-11-01 11:52:08 -0700120 public Drawable getFullResDefaultActivityIcon() {
121 return getFullResIcon(Resources.getSystem(),
Michael Jurka8b805b12012-04-18 14:23:14 -0700122 android.R.mipmap.sym_def_app_icon);
Michael Jurkac9a96192010-11-01 11:52:08 -0700123 }
124
Michael Jurka4842ed02011-07-07 15:33:20 -0700125 public Drawable getFullResIcon(Resources resources, int iconId) {
Michael Jurka721d9722011-08-03 11:49:59 -0700126 Drawable d;
Michael Jurka4842ed02011-07-07 15:33:20 -0700127 try {
Michael Jurka721d9722011-08-03 11:49:59 -0700128 d = resources.getDrawableForDensity(iconId, mIconDpi);
Michael Jurka4842ed02011-07-07 15:33:20 -0700129 } catch (Resources.NotFoundException e) {
Michael Jurka721d9722011-08-03 11:49:59 -0700130 d = null;
Michael Jurka4842ed02011-07-07 15:33:20 -0700131 }
Michael Jurka721d9722011-08-03 11:49:59 -0700132
133 return (d != null) ? d : getFullResDefaultActivityIcon();
Michael Jurkac9a96192010-11-01 11:52:08 -0700134 }
135
Winson Chung0b9fcf52011-10-31 13:05:15 -0700136 public Drawable getFullResIcon(String packageName, int iconId) {
Michael Jurkac9a96192010-11-01 11:52:08 -0700137 Resources resources;
138 try {
Winson Chung0b9fcf52011-10-31 13:05:15 -0700139 resources = mPackageManager.getResourcesForApplication(packageName);
140 } catch (PackageManager.NameNotFoundException e) {
141 resources = null;
142 }
143 if (resources != null) {
144 if (iconId != 0) {
145 return getFullResIcon(resources, iconId);
146 }
147 }
148 return getFullResDefaultActivityIcon();
149 }
150
Sunny Goyalffe83f12014-08-14 17:39:34 -0700151 public int getFullResIconDpi() {
152 return mIconDpi;
153 }
154
Winson Chung0b9fcf52011-10-31 13:05:15 -0700155 public Drawable getFullResIcon(ResolveInfo info) {
Michael Jurkadac85912012-05-18 15:04:49 -0700156 return getFullResIcon(info.activityInfo);
157 }
158
159 public Drawable getFullResIcon(ActivityInfo info) {
160
Winson Chung0b9fcf52011-10-31 13:05:15 -0700161 Resources resources;
162 try {
163 resources = mPackageManager.getResourcesForApplication(
Michael Jurkadac85912012-05-18 15:04:49 -0700164 info.applicationInfo);
Michael Jurkac9a96192010-11-01 11:52:08 -0700165 } catch (PackageManager.NameNotFoundException e) {
166 resources = null;
167 }
168 if (resources != null) {
Michael Jurkadac85912012-05-18 15:04:49 -0700169 int iconId = info.getIconResource();
Michael Jurkac9a96192010-11-01 11:52:08 -0700170 if (iconId != 0) {
171 return getFullResIcon(resources, iconId);
172 }
173 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500174
Michael Jurkac9a96192010-11-01 11:52:08 -0700175 return getFullResDefaultActivityIcon();
176 }
177
Kenny Guyed131872014-04-30 03:02:21 +0100178 private Bitmap makeDefaultIcon(UserHandleCompat user) {
179 Drawable unbadged = getFullResDefaultActivityIcon();
180 Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user);
Romain Guya28fd3f2010-03-15 14:44:42 -0700181 Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
182 Math.max(d.getIntrinsicHeight(), 1),
183 Bitmap.Config.ARGB_8888);
184 Canvas c = new Canvas(b);
185 d.setBounds(0, 0, b.getWidth(), b.getHeight());
186 d.draw(c);
Adam Cohenaaf473c2011-08-03 12:02:47 -0700187 c.setBitmap(null);
Romain Guya28fd3f2010-03-15 14:44:42 -0700188 return b;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800189 }
190
191 /**
192 * Remove any records for the supplied ComponentName.
193 */
Kenny Guyed131872014-04-30 03:02:21 +0100194 public void remove(ComponentName componentName, UserHandleCompat user) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800195 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100196 mCache.remove(new CacheKey(componentName, user));
Joe Onorato0589f0f2010-02-08 13:44:00 -0800197 }
198 }
199
200 /**
Chris Wren6d0dde02014-02-10 12:16:54 -0500201 * Remove any records for the supplied package name.
202 */
Kenny Guyed131872014-04-30 03:02:21 +0100203 public void remove(String packageName, UserHandleCompat user) {
204 HashSet<CacheKey> forDeletion = new HashSet<CacheKey>();
205 for (CacheKey key: mCache.keySet()) {
206 if (key.componentName.getPackageName().equals(packageName)
207 && key.user.equals(user)) {
208 forDeletion.add(key);
Chris Wren6d0dde02014-02-10 12:16:54 -0500209 }
210 }
Kenny Guyed131872014-04-30 03:02:21 +0100211 for (CacheKey condemned: forDeletion) {
212 mCache.remove(condemned);
Chris Wren6d0dde02014-02-10 12:16:54 -0500213 }
214 }
215
216 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800217 * Empty out the cache.
218 */
219 public void flush() {
220 synchronized (mCache) {
221 mCache.clear();
222 }
223 }
224
225 /**
Winson Chunge5467dc2013-10-14 17:03:04 -0700226 * Empty out the cache that aren't of the correct grid size
227 */
228 public void flushInvalidIcons(DeviceProfile grid) {
229 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100230 Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator();
Adam Cohenb6d33df2013-10-15 10:18:02 -0700231 while (it.hasNext()) {
232 final CacheEntry e = it.next().getValue();
Sunny Goyal876d11e2014-09-15 08:48:12 -0700233 if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx
234 || e.icon.getHeight() < grid.iconSizePx)) {
Adam Cohenb6d33df2013-10-15 10:18:02 -0700235 it.remove();
Winson Chunge5467dc2013-10-14 17:03:04 -0700236 }
237 }
238 }
239 }
240
241 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800242 * Fill in "application" with the icon and label for "info."
243 */
Kenny Guyed131872014-04-30 03:02:21 +0100244 public void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info,
Winson Chungc3eecff2011-07-11 17:44:15 -0700245 HashMap<Object, CharSequence> labelCache) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800246 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100247 CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700248 info.getUser(), false);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800249
250 application.title = entry.title;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800251 application.iconBitmap = entry.icon;
Kenny Guyc2bd8102014-06-30 12:30:31 +0100252 application.contentDescription = entry.contentDescription;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800253 }
254 }
255
Kenny Guyed131872014-04-30 03:02:21 +0100256 public Bitmap getIcon(Intent intent, UserHandleCompat user) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700257 return getIcon(intent, null, user, true);
Chris Wren6d0dde02014-02-10 12:16:54 -0500258 }
259
Sunny Goyal34942622014-08-29 17:20:55 -0700260 private Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700261 synchronized (mCache) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700262 ComponentName component = intent.getComponent();
Chris Wren075f9f52014-05-13 16:18:21 -0400263 // null info means not installed, but if we have a component from the intent then
264 // we should still look in the cache for restored app icons.
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700265 if (component == null) {
Kenny Guyed131872014-04-30 03:02:21 +0100266 return getDefaultIcon(user);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700267 }
268
Sunny Goyal34942622014-08-29 17:20:55 -0700269 LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user);
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700270 CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
Chris Wren6d0dde02014-02-10 12:16:54 -0500271 if (title != null) {
272 entry.title = title;
Kenny Guyc2bd8102014-06-30 12:30:31 +0100273 entry.contentDescription = mUserManager.getBadgedLabelForUser(title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500274 }
Joe Onoratofad1fb52010-05-04 12:12:41 -0700275 return entry.icon;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800276 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800277 }
278
Sunny Goyal34942622014-08-29 17:20:55 -0700279 /**
280 * Fill in "shortcutInfo" with the icon and label for "info."
281 */
282 public void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, UserHandleCompat user,
283 boolean usePkgIcon) {
284 synchronized (mCache) {
285 ComponentName component = intent.getComponent();
286 // null info means not installed, but if we have a component from the intent then
287 // we should still look in the cache for restored app icons.
288 if (component == null) {
289 shortcutInfo.setIcon(getDefaultIcon(user));
290 shortcutInfo.title = "";
291 shortcutInfo.usingFallbackIcon = true;
292 } else {
293 LauncherActivityInfoCompat launcherActInfo =
294 mLauncherApps.resolveActivity(intent, user);
295 CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
296
297 shortcutInfo.setIcon(entry.icon);
298 shortcutInfo.title = entry.title;
299 shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user);
300 }
301 }
302 }
303
304
Kenny Guyed131872014-04-30 03:02:21 +0100305 public Bitmap getDefaultIcon(UserHandleCompat user) {
306 if (!mDefaultIcons.containsKey(user)) {
307 mDefaultIcons.put(user, makeDefaultIcon(user));
308 }
309 return mDefaultIcons.get(user);
310 }
311
312 public Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info,
Winson Chungaac01e12011-08-17 10:37:13 -0700313 HashMap<Object, CharSequence> labelCache) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700314 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100315 if (info == null || component == null) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700316 return null;
317 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800318
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700319 CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700320 return entry.icon;
321 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800322 }
323
Kenny Guyed131872014-04-30 03:02:21 +0100324 public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) {
325 return mDefaultIcons.get(user) == icon;
Joe Onoratoddc9c1f2010-08-30 18:30:15 -0700326 }
327
Kenny Guyed131872014-04-30 03:02:21 +0100328 private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700329 HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {
Kenny Guyed131872014-04-30 03:02:21 +0100330 CacheKey cacheKey = new CacheKey(componentName, user);
331 CacheEntry entry = mCache.get(cacheKey);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800332 if (entry == null) {
333 entry = new CacheEntry();
334
Kenny Guyed131872014-04-30 03:02:21 +0100335 mCache.put(cacheKey, entry);
Joe Onorato84f6a8d2010-02-12 17:53:35 -0500336
Chris Wren6d0dde02014-02-10 12:16:54 -0500337 if (info != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100338 ComponentName labelKey = info.getComponentName();
339 if (labelCache != null && labelCache.containsKey(labelKey)) {
340 entry.title = labelCache.get(labelKey).toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500341 } else {
Kenny Guyed131872014-04-30 03:02:21 +0100342 entry.title = info.getLabel().toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500343 if (labelCache != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100344 labelCache.put(labelKey, entry.title);
Chris Wren6d0dde02014-02-10 12:16:54 -0500345 }
346 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500347
Kenny Guyc2bd8102014-06-30 12:30:31 +0100348 entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500349 entry.icon = Utilities.createIconBitmap(
Kenny Guyed131872014-04-30 03:02:21 +0100350 info.getBadgedIcon(mIconDpi), mContext);
Winson Chungc3eecff2011-07-11 17:44:15 -0700351 } else {
Chris Wren6d0dde02014-02-10 12:16:54 -0500352 entry.title = "";
Kenny Guyed131872014-04-30 03:02:21 +0100353 Bitmap preloaded = getPreloadedIcon(componentName, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500354 if (preloaded != null) {
355 if (DEBUG) Log.d(TAG, "using preloaded icon for " +
356 componentName.toShortString());
357 entry.icon = preloaded;
358 } else {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700359 if (usePackageIcon) {
360 CacheEntry packageEntry = getEntryForPackage(
361 componentName.getPackageName(), user);
Sunny Goyal34942622014-08-29 17:20:55 -0700362 if (packageEntry != null) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700363 if (DEBUG) Log.d(TAG, "using package default icon for " +
364 componentName.toShortString());
365 entry.icon = packageEntry.icon;
Sunny Goyal34942622014-08-29 17:20:55 -0700366 entry.title = packageEntry.title;
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700367 }
368 }
369 if (entry.icon == null) {
370 if (DEBUG) Log.d(TAG, "using default icon for " +
371 componentName.toShortString());
372 entry.icon = getDefaultIcon(user);
373 }
Winson Chungc3eecff2011-07-11 17:44:15 -0700374 }
375 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800376 }
377 return entry;
378 }
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400379
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700380 /**
Sunny Goyal34942622014-08-29 17:20:55 -0700381 * Adds a default package entry in the cache. This entry is not persisted and will be removed
382 * when the cache is flushed.
383 */
384 public void cachePackageInstallInfo(String packageName, UserHandleCompat user,
385 Bitmap icon, CharSequence title) {
Sunny Goyala22666f2014-09-18 13:25:15 -0700386 remove(packageName, user);
387
Sunny Goyal34942622014-08-29 17:20:55 -0700388 CacheEntry entry = getEntryForPackage(packageName, user);
389 if (!TextUtils.isEmpty(title)) {
390 entry.title = title;
391 }
392 if (icon != null) {
Sunny Goyala22666f2014-09-18 13:25:15 -0700393 entry.icon = Utilities.createIconBitmap(
394 new BitmapDrawable(mContext.getResources(), icon), mContext);
Sunny Goyal34942622014-08-29 17:20:55 -0700395 }
396 }
397
398 /**
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700399 * Gets an entry for the package, which can be used as a fallback entry for various components.
400 */
401 private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
402 ComponentName cn = getPackageComponent(packageName);
403 CacheKey cacheKey = new CacheKey(cn, user);
404 CacheEntry entry = mCache.get(cacheKey);
405 if (entry == null) {
406 entry = new CacheEntry();
Sunny Goyal34942622014-08-29 17:20:55 -0700407 entry.title = "";
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700408 mCache.put(cacheKey, entry);
409
410 try {
411 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
412 entry.title = info.loadLabel(mPackageManager);
413 entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext);
414 } catch (NameNotFoundException e) {
415 if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
416 }
417
418 if (entry.icon == null) {
419 entry.icon = getPreloadedIcon(cn, user);
420 }
421 }
422 return entry;
423 }
424
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400425 public HashMap<ComponentName,Bitmap> getAllIcons() {
426 synchronized (mCache) {
427 HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
Kenny Guyed131872014-04-30 03:02:21 +0100428 for (CacheKey ck : mCache.keySet()) {
429 final CacheEntry e = mCache.get(ck);
430 set.put(ck.componentName, e.icon);
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400431 }
432 return set;
433 }
434 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500435
436 /**
437 * Pre-load an icon into the persistent cache.
438 *
439 * <P>Queries for a component that does not exist in the package manager
440 * will be answered by the persistent cache.
441 *
442 * @param context application context
443 * @param componentName the icon should be returned for this component
444 * @param icon the icon to be persisted
445 * @param dpi the native density of the icon
446 */
447 public static void preloadIcon(Context context, ComponentName componentName, Bitmap icon,
448 int dpi) {
449 // TODO rescale to the correct native DPI
450 try {
451 PackageManager packageManager = context.getPackageManager();
452 packageManager.getActivityIcon(componentName);
453 // component is present on the system already, do nothing
454 return;
455 } catch (PackageManager.NameNotFoundException e) {
456 // pass
457 }
458
459 final String key = componentName.flattenToString();
460 FileOutputStream resourceFile = null;
461 try {
462 resourceFile = context.openFileOutput(getResourceFilename(componentName),
463 Context.MODE_PRIVATE);
464 ByteArrayOutputStream os = new ByteArrayOutputStream();
465 if (icon.compress(android.graphics.Bitmap.CompressFormat.PNG, 75, os)) {
466 byte[] buffer = os.toByteArray();
467 resourceFile.write(buffer, 0, buffer.length);
468 } else {
469 Log.w(TAG, "failed to encode cache for " + key);
470 return;
471 }
472 } catch (FileNotFoundException e) {
473 Log.w(TAG, "failed to pre-load cache for " + key, e);
474 } catch (IOException e) {
475 Log.w(TAG, "failed to pre-load cache for " + key, e);
476 } finally {
477 if (resourceFile != null) {
478 try {
479 resourceFile.close();
480 } catch (IOException e) {
481 Log.d(TAG, "failed to save restored icon for: " + key, e);
482 }
483 }
484 }
485 }
486
487 /**
488 * Read a pre-loaded icon from the persistent icon cache.
489 *
490 * @param componentName the component that should own the icon
491 * @returns a bitmap if one is cached, or null.
492 */
Kenny Guyed131872014-04-30 03:02:21 +0100493 private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) {
Chris Wren6d0dde02014-02-10 12:16:54 -0500494 final String key = componentName.flattenToShortString();
495
Kenny Guyed131872014-04-30 03:02:21 +0100496 // We don't keep icons for other profiles in persistent cache.
497 if (!user.equals(UserHandleCompat.myUserHandle())) {
498 return null;
499 }
500
Chris Wren6d0dde02014-02-10 12:16:54 -0500501 if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key);
502 Bitmap icon = null;
503 FileInputStream resourceFile = null;
504 try {
505 resourceFile = mContext.openFileInput(getResourceFilename(componentName));
506 byte[] buffer = new byte[1024];
507 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
508 int bytesRead = 0;
509 while(bytesRead >= 0) {
510 bytes.write(buffer, 0, bytesRead);
511 bytesRead = resourceFile.read(buffer, 0, buffer.length);
512 }
513 if (DEBUG) Log.d(TAG, "read " + bytes.size());
514 icon = BitmapFactory.decodeByteArray(bytes.toByteArray(), 0, bytes.size());
515 if (icon == null) {
516 Log.w(TAG, "failed to decode pre-load icon for " + key);
517 }
518 } catch (FileNotFoundException e) {
Sunny Goyalbbef77d2014-09-09 16:27:55 -0700519 if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key);
Chris Wren6d0dde02014-02-10 12:16:54 -0500520 } catch (IOException e) {
521 Log.w(TAG, "failed to read pre-load icon for: " + key, e);
522 } finally {
523 if(resourceFile != null) {
524 try {
525 resourceFile.close();
526 } catch (IOException e) {
527 Log.d(TAG, "failed to manage pre-load icon file: " + key, e);
528 }
529 }
530 }
531
Chris Wren6d0dde02014-02-10 12:16:54 -0500532 return icon;
533 }
534
535 /**
536 * Remove a pre-loaded icon from the persistent icon cache.
537 *
538 * @param componentName the component that should own the icon
539 * @returns true on success
540 */
Kenny Guyed131872014-04-30 03:02:21 +0100541 public boolean deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) {
542 // We don't keep icons for other profiles in persistent cache.
543 if (!user.equals(UserHandleCompat.myUserHandle())) {
544 return false;
545 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500546 if (componentName == null) {
547 return false;
548 }
549 if (mCache.remove(componentName) != null) {
550 if (DEBUG) Log.d(TAG, "removed pre-loaded icon from the in-memory cache");
551 }
552 boolean success = mContext.deleteFile(getResourceFilename(componentName));
553 if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache");
554
555 return success;
556 }
557
558 private static String getResourceFilename(ComponentName component) {
559 String resourceName = component.flattenToShortString();
560 String filename = resourceName.replace(File.separatorChar, '_');
561 return RESOURCE_FILE_PREFIX + filename;
562 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700563
564 static ComponentName getPackageComponent(String packageName) {
565 return new ComponentName(packageName, EMPTY_CLASS_NAME);
566 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800567}