blob: 5a0875b3055163b6d306d0d66a473a566fd1d2de [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;
Michael Jurkac9a96192010-11-01 11:52:08 -070027import android.content.res.Resources;
Joe Onorato0589f0f2010-02-08 13:44:00 -080028import android.graphics.Bitmap;
Chris Wren6d0dde02014-02-10 12:16:54 -050029import android.graphics.BitmapFactory;
Romain Guya28fd3f2010-03-15 14:44:42 -070030import android.graphics.Canvas;
Joe Onorato0589f0f2010-02-08 13:44:00 -080031import android.graphics.drawable.Drawable;
Sunny Goyal34942622014-08-29 17:20:55 -070032import android.text.TextUtils;
Chris Wren6d0dde02014-02-10 12:16:54 -050033import android.util.Log;
Joe Onorato0589f0f2010-02-08 13:44:00 -080034
Kenny Guyed131872014-04-30 03:02:21 +010035import com.android.launcher3.compat.LauncherActivityInfoCompat;
36import com.android.launcher3.compat.LauncherAppsCompat;
37import com.android.launcher3.compat.UserHandleCompat;
38import com.android.launcher3.compat.UserManagerCompat;
39
Chris Wren6d0dde02014-02-10 12:16:54 -050040import java.io.ByteArrayOutputStream;
41import java.io.File;
42import java.io.FileInputStream;
43import java.io.FileNotFoundException;
44import java.io.FileOutputStream;
45import java.io.IOException;
Joe Onorato0589f0f2010-02-08 13:44:00 -080046import java.util.HashMap;
Chris Wren6d0dde02014-02-10 12:16:54 -050047import java.util.HashSet;
Adam Cohenb6d33df2013-10-15 10:18:02 -070048import java.util.Iterator;
49import java.util.Map.Entry;
Joe Onorato0589f0f2010-02-08 13:44:00 -080050
51/**
52 * Cache of application icons. Icons can be made from any thread.
53 */
54public class IconCache {
Sunny Goyal0fc1be12014-08-11 17:05:23 -070055
Joe Onorato0589f0f2010-02-08 13:44:00 -080056 private static final String TAG = "Launcher.IconCache";
57
58 private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
Chris Wren6d0dde02014-02-10 12:16:54 -050059 private static final String RESOURCE_FILE_PREFIX = "icon_";
60
Sunny Goyal0fc1be12014-08-11 17:05:23 -070061 // Empty class name is used for storing package default entry.
62 private static final String EMPTY_CLASS_NAME = ".";
63
Sunny Goyalbbef77d2014-09-09 16:27:55 -070064 private static final boolean DEBUG = false;
Joe Onorato0589f0f2010-02-08 13:44:00 -080065
66 private static class CacheEntry {
67 public Bitmap icon;
Kenny Guyd6fe5262014-07-21 17:11:41 +010068 public CharSequence title;
69 public CharSequence contentDescription;
Joe Onorato0589f0f2010-02-08 13:44:00 -080070 }
71
Kenny Guyed131872014-04-30 03:02:21 +010072 private static class CacheKey {
73 public ComponentName componentName;
74 public UserHandleCompat user;
75
76 CacheKey(ComponentName componentName, UserHandleCompat user) {
77 this.componentName = componentName;
78 this.user = user;
79 }
80
81 @Override
82 public int hashCode() {
83 return componentName.hashCode() + user.hashCode();
84 }
85
86 @Override
87 public boolean equals(Object o) {
88 CacheKey other = (CacheKey) o;
89 return other.componentName.equals(componentName) && other.user.equals(user);
90 }
91 }
92
93 private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons =
94 new HashMap<UserHandleCompat, Bitmap>();
Daniel Sandlercc8befa2013-06-11 14:45:48 -040095 private final Context mContext;
Romain Guya28fd3f2010-03-15 14:44:42 -070096 private final PackageManager mPackageManager;
Kenny Guyed131872014-04-30 03:02:21 +010097 private final UserManagerCompat mUserManager;
98 private final LauncherAppsCompat mLauncherApps;
99 private final HashMap<CacheKey, CacheEntry> mCache =
100 new HashMap<CacheKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
Michael Jurkac9a96192010-11-01 11:52:08 -0700101 private int mIconDpi;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800102
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400103 public IconCache(Context context) {
Winson Chungd83f5f42012-02-13 14:27:42 -0800104 ActivityManager activityManager =
105 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
106
Joe Onorato0589f0f2010-02-08 13:44:00 -0800107 mContext = context;
108 mPackageManager = context.getPackageManager();
Kenny Guyed131872014-04-30 03:02:21 +0100109 mUserManager = UserManagerCompat.getInstance(mContext);
110 mLauncherApps = LauncherAppsCompat.getInstance(mContext);
Winson Chungd83f5f42012-02-13 14:27:42 -0800111 mIconDpi = activityManager.getLauncherLargeIconDensity();
112
Michael Jurkac9a96192010-11-01 11:52:08 -0700113 // need to set mIconDpi before getting default icon
Kenny Guyed131872014-04-30 03:02:21 +0100114 UserHandleCompat myUser = UserHandleCompat.myUserHandle();
115 mDefaultIcons.put(myUser, makeDefaultIcon(myUser));
Romain Guya28fd3f2010-03-15 14:44:42 -0700116 }
117
Michael Jurkac9a96192010-11-01 11:52:08 -0700118 public Drawable getFullResDefaultActivityIcon() {
Sunny Goyal736f5af2014-10-16 14:07:29 -0700119 return getFullResIcon(Resources.getSystem(), android.R.mipmap.sym_def_app_icon);
Michael Jurkac9a96192010-11-01 11:52:08 -0700120 }
121
Sunny Goyalb50cc8c2014-10-06 16:23:56 -0700122 private Drawable getFullResIcon(Resources resources, int iconId) {
Michael Jurka721d9722011-08-03 11:49:59 -0700123 Drawable d;
Michael Jurka4842ed02011-07-07 15:33:20 -0700124 try {
Michael Jurka721d9722011-08-03 11:49:59 -0700125 d = resources.getDrawableForDensity(iconId, mIconDpi);
Michael Jurka4842ed02011-07-07 15:33:20 -0700126 } catch (Resources.NotFoundException e) {
Michael Jurka721d9722011-08-03 11:49:59 -0700127 d = null;
Michael Jurka4842ed02011-07-07 15:33:20 -0700128 }
Michael Jurka721d9722011-08-03 11:49:59 -0700129
130 return (d != null) ? d : getFullResDefaultActivityIcon();
Michael Jurkac9a96192010-11-01 11:52:08 -0700131 }
132
Winson Chung0b9fcf52011-10-31 13:05:15 -0700133 public Drawable getFullResIcon(String packageName, int iconId) {
Michael Jurkac9a96192010-11-01 11:52:08 -0700134 Resources resources;
135 try {
Winson Chung0b9fcf52011-10-31 13:05:15 -0700136 resources = mPackageManager.getResourcesForApplication(packageName);
137 } catch (PackageManager.NameNotFoundException e) {
138 resources = null;
139 }
140 if (resources != null) {
141 if (iconId != 0) {
142 return getFullResIcon(resources, iconId);
143 }
144 }
145 return getFullResDefaultActivityIcon();
146 }
147
Sunny Goyalffe83f12014-08-14 17:39:34 -0700148 public int getFullResIconDpi() {
149 return mIconDpi;
150 }
151
Michael Jurkadac85912012-05-18 15:04:49 -0700152 public Drawable getFullResIcon(ActivityInfo info) {
Winson Chung0b9fcf52011-10-31 13:05:15 -0700153 Resources resources;
154 try {
155 resources = mPackageManager.getResourcesForApplication(
Michael Jurkadac85912012-05-18 15:04:49 -0700156 info.applicationInfo);
Michael Jurkac9a96192010-11-01 11:52:08 -0700157 } catch (PackageManager.NameNotFoundException e) {
158 resources = null;
159 }
160 if (resources != null) {
Michael Jurkadac85912012-05-18 15:04:49 -0700161 int iconId = info.getIconResource();
Michael Jurkac9a96192010-11-01 11:52:08 -0700162 if (iconId != 0) {
163 return getFullResIcon(resources, iconId);
164 }
165 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500166
Michael Jurkac9a96192010-11-01 11:52:08 -0700167 return getFullResDefaultActivityIcon();
168 }
169
Kenny Guyed131872014-04-30 03:02:21 +0100170 private Bitmap makeDefaultIcon(UserHandleCompat user) {
171 Drawable unbadged = getFullResDefaultActivityIcon();
172 Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user);
Romain Guya28fd3f2010-03-15 14:44:42 -0700173 Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
174 Math.max(d.getIntrinsicHeight(), 1),
175 Bitmap.Config.ARGB_8888);
176 Canvas c = new Canvas(b);
177 d.setBounds(0, 0, b.getWidth(), b.getHeight());
178 d.draw(c);
Adam Cohenaaf473c2011-08-03 12:02:47 -0700179 c.setBitmap(null);
Romain Guya28fd3f2010-03-15 14:44:42 -0700180 return b;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800181 }
182
183 /**
184 * Remove any records for the supplied ComponentName.
185 */
Sunny Goyal736f5af2014-10-16 14:07:29 -0700186 public synchronized void remove(ComponentName componentName, UserHandleCompat user) {
187 mCache.remove(new CacheKey(componentName, user));
Joe Onorato0589f0f2010-02-08 13:44:00 -0800188 }
189
190 /**
Chris Wren6d0dde02014-02-10 12:16:54 -0500191 * Remove any records for the supplied package name.
192 */
Sunny Goyal736f5af2014-10-16 14:07:29 -0700193 public synchronized void remove(String packageName, UserHandleCompat user) {
Kenny Guyed131872014-04-30 03:02:21 +0100194 HashSet<CacheKey> forDeletion = new HashSet<CacheKey>();
195 for (CacheKey key: mCache.keySet()) {
196 if (key.componentName.getPackageName().equals(packageName)
197 && key.user.equals(user)) {
198 forDeletion.add(key);
Chris Wren6d0dde02014-02-10 12:16:54 -0500199 }
200 }
Kenny Guyed131872014-04-30 03:02:21 +0100201 for (CacheKey condemned: forDeletion) {
202 mCache.remove(condemned);
Chris Wren6d0dde02014-02-10 12:16:54 -0500203 }
204 }
205
206 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800207 * Empty out the cache.
208 */
Sunny Goyal736f5af2014-10-16 14:07:29 -0700209 public synchronized void flush() {
210 mCache.clear();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800211 }
212
213 /**
Winson Chunge5467dc2013-10-14 17:03:04 -0700214 * Empty out the cache that aren't of the correct grid size
215 */
Sunny Goyal736f5af2014-10-16 14:07:29 -0700216 public synchronized void flushInvalidIcons(DeviceProfile grid) {
217 Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator();
218 while (it.hasNext()) {
219 final CacheEntry e = it.next().getValue();
220 if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx
221 || e.icon.getHeight() < grid.iconSizePx)) {
222 it.remove();
Winson Chunge5467dc2013-10-14 17:03:04 -0700223 }
224 }
225 }
226
227 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800228 * Fill in "application" with the icon and label for "info."
229 */
Sunny Goyal736f5af2014-10-16 14:07:29 -0700230 public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info,
Winson Chungc3eecff2011-07-11 17:44:15 -0700231 HashMap<Object, CharSequence> labelCache) {
Sunny Goyal736f5af2014-10-16 14:07:29 -0700232 CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
233 info.getUser(), false);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800234
Sunny Goyal736f5af2014-10-16 14:07:29 -0700235 application.title = entry.title;
236 application.iconBitmap = entry.icon;
237 application.contentDescription = entry.contentDescription;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800238 }
239
Sunny Goyal736f5af2014-10-16 14:07:29 -0700240 public synchronized Bitmap getIcon(Intent intent, UserHandleCompat user) {
241 ComponentName component = intent.getComponent();
242 // null info means not installed, but if we have a component from the intent then
243 // we should still look in the cache for restored app icons.
244 if (component == null) {
245 return getDefaultIcon(user);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800246 }
Sunny Goyal736f5af2014-10-16 14:07:29 -0700247
248 LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user);
249 CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, true);
250 return entry.icon;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800251 }
252
Sunny Goyal34942622014-08-29 17:20:55 -0700253 /**
254 * Fill in "shortcutInfo" with the icon and label for "info."
255 */
Sunny Goyal736f5af2014-10-16 14:07:29 -0700256 public synchronized void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent,
257 UserHandleCompat user, boolean usePkgIcon) {
258 ComponentName component = intent.getComponent();
259 // null info means not installed, but if we have a component from the intent then
260 // we should still look in the cache for restored app icons.
261 if (component == null) {
262 shortcutInfo.setIcon(getDefaultIcon(user));
263 shortcutInfo.title = "";
264 shortcutInfo.usingFallbackIcon = true;
265 } else {
266 LauncherActivityInfoCompat launcherActInfo =
267 mLauncherApps.resolveActivity(intent, user);
268 CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
Sunny Goyal34942622014-08-29 17:20:55 -0700269
Sunny Goyal736f5af2014-10-16 14:07:29 -0700270 shortcutInfo.setIcon(entry.icon);
271 shortcutInfo.title = entry.title;
272 shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user);
Sunny Goyal34942622014-08-29 17:20:55 -0700273 }
274 }
275
276
Sunny Goyal736f5af2014-10-16 14:07:29 -0700277 public synchronized Bitmap getDefaultIcon(UserHandleCompat user) {
Kenny Guyed131872014-04-30 03:02:21 +0100278 if (!mDefaultIcons.containsKey(user)) {
279 mDefaultIcons.put(user, makeDefaultIcon(user));
280 }
281 return mDefaultIcons.get(user);
282 }
283
Sunny Goyal736f5af2014-10-16 14:07:29 -0700284 public synchronized Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info,
Winson Chungaac01e12011-08-17 10:37:13 -0700285 HashMap<Object, CharSequence> labelCache) {
Sunny Goyal736f5af2014-10-16 14:07:29 -0700286 if (info == null || component == null) {
287 return null;
Joe Onoratofad1fb52010-05-04 12:12:41 -0700288 }
Sunny Goyal736f5af2014-10-16 14:07:29 -0700289
290 CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false);
291 return entry.icon;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800292 }
293
Kenny Guyed131872014-04-30 03:02:21 +0100294 public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) {
295 return mDefaultIcons.get(user) == icon;
Joe Onoratoddc9c1f2010-08-30 18:30:15 -0700296 }
297
Sunny Goyal736f5af2014-10-16 14:07:29 -0700298 /**
299 * Retrieves the entry from the cache. If the entry is not present, it creates a new entry.
300 * This method is not thread safe, it must be called from a synchronized method.
301 */
Kenny Guyed131872014-04-30 03:02:21 +0100302 private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700303 HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {
Kenny Guyed131872014-04-30 03:02:21 +0100304 CacheKey cacheKey = new CacheKey(componentName, user);
305 CacheEntry entry = mCache.get(cacheKey);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800306 if (entry == null) {
307 entry = new CacheEntry();
308
Kenny Guyed131872014-04-30 03:02:21 +0100309 mCache.put(cacheKey, entry);
Joe Onorato84f6a8d2010-02-12 17:53:35 -0500310
Chris Wren6d0dde02014-02-10 12:16:54 -0500311 if (info != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100312 ComponentName labelKey = info.getComponentName();
313 if (labelCache != null && labelCache.containsKey(labelKey)) {
314 entry.title = labelCache.get(labelKey).toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500315 } else {
Kenny Guyed131872014-04-30 03:02:21 +0100316 entry.title = info.getLabel().toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500317 if (labelCache != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100318 labelCache.put(labelKey, entry.title);
Chris Wren6d0dde02014-02-10 12:16:54 -0500319 }
320 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500321
Kenny Guyc2bd8102014-06-30 12:30:31 +0100322 entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500323 entry.icon = Utilities.createIconBitmap(
Kenny Guyed131872014-04-30 03:02:21 +0100324 info.getBadgedIcon(mIconDpi), mContext);
Winson Chungc3eecff2011-07-11 17:44:15 -0700325 } else {
Chris Wren6d0dde02014-02-10 12:16:54 -0500326 entry.title = "";
Kenny Guyed131872014-04-30 03:02:21 +0100327 Bitmap preloaded = getPreloadedIcon(componentName, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500328 if (preloaded != null) {
329 if (DEBUG) Log.d(TAG, "using preloaded icon for " +
330 componentName.toShortString());
331 entry.icon = preloaded;
332 } else {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700333 if (usePackageIcon) {
334 CacheEntry packageEntry = getEntryForPackage(
335 componentName.getPackageName(), user);
Sunny Goyal34942622014-08-29 17:20:55 -0700336 if (packageEntry != null) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700337 if (DEBUG) Log.d(TAG, "using package default icon for " +
338 componentName.toShortString());
339 entry.icon = packageEntry.icon;
Sunny Goyal34942622014-08-29 17:20:55 -0700340 entry.title = packageEntry.title;
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700341 }
342 }
343 if (entry.icon == null) {
344 if (DEBUG) Log.d(TAG, "using default icon for " +
345 componentName.toShortString());
346 entry.icon = getDefaultIcon(user);
347 }
Winson Chungc3eecff2011-07-11 17:44:15 -0700348 }
349 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800350 }
351 return entry;
352 }
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400353
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700354 /**
Sunny Goyal34942622014-08-29 17:20:55 -0700355 * Adds a default package entry in the cache. This entry is not persisted and will be removed
356 * when the cache is flushed.
357 */
Sunny Goyal736f5af2014-10-16 14:07:29 -0700358 public synchronized void cachePackageInstallInfo(String packageName, UserHandleCompat user,
Sunny Goyal34942622014-08-29 17:20:55 -0700359 Bitmap icon, CharSequence title) {
Sunny Goyala22666f2014-09-18 13:25:15 -0700360 remove(packageName, user);
361
Sunny Goyal34942622014-08-29 17:20:55 -0700362 CacheEntry entry = getEntryForPackage(packageName, user);
363 if (!TextUtils.isEmpty(title)) {
364 entry.title = title;
365 }
366 if (icon != null) {
Sunny Goyal2fce90c2014-10-07 12:01:58 -0700367 entry.icon = Utilities.createIconBitmap(icon, mContext);
Sunny Goyal34942622014-08-29 17:20:55 -0700368 }
369 }
370
371 /**
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700372 * Gets an entry for the package, which can be used as a fallback entry for various components.
Sunny Goyal736f5af2014-10-16 14:07:29 -0700373 * This method is not thread safe, it must be called from a synchronized method.
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700374 */
375 private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
Sunny Goyal736f5af2014-10-16 14:07:29 -0700376 ComponentName cn = new ComponentName(packageName, EMPTY_CLASS_NAME);;
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700377 CacheKey cacheKey = new CacheKey(cn, user);
378 CacheEntry entry = mCache.get(cacheKey);
379 if (entry == null) {
380 entry = new CacheEntry();
Sunny Goyal34942622014-08-29 17:20:55 -0700381 entry.title = "";
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700382 mCache.put(cacheKey, entry);
383
384 try {
385 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
386 entry.title = info.loadLabel(mPackageManager);
387 entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext);
388 } catch (NameNotFoundException e) {
389 if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
390 }
391
392 if (entry.icon == null) {
393 entry.icon = getPreloadedIcon(cn, user);
394 }
395 }
396 return entry;
397 }
398
Sunny Goyal736f5af2014-10-16 14:07:29 -0700399 public synchronized HashMap<ComponentName,Bitmap> getAllIcons() {
400 HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
401 for (CacheKey ck : mCache.keySet()) {
402 final CacheEntry e = mCache.get(ck);
403 set.put(ck.componentName, e.icon);
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400404 }
Sunny Goyal736f5af2014-10-16 14:07:29 -0700405 return set;
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400406 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500407
408 /**
409 * Pre-load an icon into the persistent cache.
410 *
411 * <P>Queries for a component that does not exist in the package manager
412 * will be answered by the persistent cache.
413 *
414 * @param context application context
415 * @param componentName the icon should be returned for this component
416 * @param icon the icon to be persisted
417 * @param dpi the native density of the icon
418 */
419 public static void preloadIcon(Context context, ComponentName componentName, Bitmap icon,
420 int dpi) {
421 // TODO rescale to the correct native DPI
422 try {
423 PackageManager packageManager = context.getPackageManager();
424 packageManager.getActivityIcon(componentName);
425 // component is present on the system already, do nothing
426 return;
427 } catch (PackageManager.NameNotFoundException e) {
428 // pass
429 }
430
431 final String key = componentName.flattenToString();
432 FileOutputStream resourceFile = null;
433 try {
434 resourceFile = context.openFileOutput(getResourceFilename(componentName),
435 Context.MODE_PRIVATE);
436 ByteArrayOutputStream os = new ByteArrayOutputStream();
437 if (icon.compress(android.graphics.Bitmap.CompressFormat.PNG, 75, os)) {
438 byte[] buffer = os.toByteArray();
439 resourceFile.write(buffer, 0, buffer.length);
440 } else {
441 Log.w(TAG, "failed to encode cache for " + key);
442 return;
443 }
444 } catch (FileNotFoundException e) {
445 Log.w(TAG, "failed to pre-load cache for " + key, e);
446 } catch (IOException e) {
447 Log.w(TAG, "failed to pre-load cache for " + key, e);
448 } finally {
449 if (resourceFile != null) {
450 try {
451 resourceFile.close();
452 } catch (IOException e) {
453 Log.d(TAG, "failed to save restored icon for: " + key, e);
454 }
455 }
456 }
457 }
458
459 /**
460 * Read a pre-loaded icon from the persistent icon cache.
461 *
462 * @param componentName the component that should own the icon
463 * @returns a bitmap if one is cached, or null.
464 */
Kenny Guyed131872014-04-30 03:02:21 +0100465 private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) {
Chris Wren6d0dde02014-02-10 12:16:54 -0500466 final String key = componentName.flattenToShortString();
467
Kenny Guyed131872014-04-30 03:02:21 +0100468 // We don't keep icons for other profiles in persistent cache.
469 if (!user.equals(UserHandleCompat.myUserHandle())) {
470 return null;
471 }
472
Chris Wren6d0dde02014-02-10 12:16:54 -0500473 if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key);
474 Bitmap icon = null;
475 FileInputStream resourceFile = null;
476 try {
477 resourceFile = mContext.openFileInput(getResourceFilename(componentName));
478 byte[] buffer = new byte[1024];
479 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
480 int bytesRead = 0;
481 while(bytesRead >= 0) {
482 bytes.write(buffer, 0, bytesRead);
483 bytesRead = resourceFile.read(buffer, 0, buffer.length);
484 }
485 if (DEBUG) Log.d(TAG, "read " + bytes.size());
486 icon = BitmapFactory.decodeByteArray(bytes.toByteArray(), 0, bytes.size());
487 if (icon == null) {
488 Log.w(TAG, "failed to decode pre-load icon for " + key);
489 }
490 } catch (FileNotFoundException e) {
Sunny Goyalbbef77d2014-09-09 16:27:55 -0700491 if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key);
Chris Wren6d0dde02014-02-10 12:16:54 -0500492 } catch (IOException e) {
493 Log.w(TAG, "failed to read pre-load icon for: " + key, e);
494 } finally {
495 if(resourceFile != null) {
496 try {
497 resourceFile.close();
498 } catch (IOException e) {
499 Log.d(TAG, "failed to manage pre-load icon file: " + key, e);
500 }
501 }
502 }
503
Chris Wren6d0dde02014-02-10 12:16:54 -0500504 return icon;
505 }
506
507 /**
508 * Remove a pre-loaded icon from the persistent icon cache.
509 *
510 * @param componentName the component that should own the icon
Chris Wren6d0dde02014-02-10 12:16:54 -0500511 */
Sunny Goyal736f5af2014-10-16 14:07:29 -0700512 public void deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) {
Kenny Guyed131872014-04-30 03:02:21 +0100513 // We don't keep icons for other profiles in persistent cache.
Sunny Goyal736f5af2014-10-16 14:07:29 -0700514 if (!user.equals(UserHandleCompat.myUserHandle()) || componentName == null) {
515 return;
Kenny Guyed131872014-04-30 03:02:21 +0100516 }
Sunny Goyal736f5af2014-10-16 14:07:29 -0700517 remove(componentName, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500518 boolean success = mContext.deleteFile(getResourceFilename(componentName));
519 if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache");
Chris Wren6d0dde02014-02-10 12:16:54 -0500520 }
521
522 private static String getResourceFilename(ComponentName component) {
523 String resourceName = component.flattenToShortString();
524 String filename = resourceName.replace(File.separatorChar, '_');
525 return RESOURCE_FILE_PREFIX + filename;
526 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800527}