blob: 75be83638603e6dffeec5fe11a1fcca9037aef1a [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;
Joe Onorato0589f0f2010-02-08 13:44:00 -080032import android.graphics.drawable.Drawable;
Sunny Goyal34942622014-08-29 17:20:55 -070033import android.text.TextUtils;
Chris Wren6d0dde02014-02-10 12:16:54 -050034import android.util.Log;
Joe Onorato0589f0f2010-02-08 13:44:00 -080035
Kenny Guyed131872014-04-30 03:02:21 +010036import com.android.launcher3.compat.LauncherActivityInfoCompat;
37import com.android.launcher3.compat.LauncherAppsCompat;
38import com.android.launcher3.compat.UserHandleCompat;
39import com.android.launcher3.compat.UserManagerCompat;
40
Chris Wren6d0dde02014-02-10 12:16:54 -050041import java.io.ByteArrayOutputStream;
42import java.io.File;
43import java.io.FileInputStream;
44import java.io.FileNotFoundException;
45import java.io.FileOutputStream;
46import java.io.IOException;
Joe Onorato0589f0f2010-02-08 13:44:00 -080047import java.util.HashMap;
Chris Wren6d0dde02014-02-10 12:16:54 -050048import java.util.HashSet;
Adam Cohenb6d33df2013-10-15 10:18:02 -070049import java.util.Iterator;
50import java.util.Map.Entry;
Joe Onorato0589f0f2010-02-08 13:44:00 -080051
52/**
53 * Cache of application icons. Icons can be made from any thread.
54 */
55public class IconCache {
Sunny Goyal0fc1be12014-08-11 17:05:23 -070056
Joe Onorato0589f0f2010-02-08 13:44:00 -080057 private static final String TAG = "Launcher.IconCache";
58
59 private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
Chris Wren6d0dde02014-02-10 12:16:54 -050060 private static final String RESOURCE_FILE_PREFIX = "icon_";
61
Sunny Goyal0fc1be12014-08-11 17:05:23 -070062 // Empty class name is used for storing package default entry.
63 private static final String EMPTY_CLASS_NAME = ".";
64
Chris Wren6d0dde02014-02-10 12:16:54 -050065 private static final boolean DEBUG = true;
Joe Onorato0589f0f2010-02-08 13:44:00 -080066
67 private static class CacheEntry {
68 public Bitmap icon;
Kenny Guyd6fe5262014-07-21 17:11:41 +010069 public CharSequence title;
70 public CharSequence contentDescription;
Joe Onorato0589f0f2010-02-08 13:44:00 -080071 }
72
Kenny Guyed131872014-04-30 03:02:21 +010073 private static class CacheKey {
74 public ComponentName componentName;
75 public UserHandleCompat user;
76
77 CacheKey(ComponentName componentName, UserHandleCompat user) {
78 this.componentName = componentName;
79 this.user = user;
80 }
81
82 @Override
83 public int hashCode() {
84 return componentName.hashCode() + user.hashCode();
85 }
86
87 @Override
88 public boolean equals(Object o) {
89 CacheKey other = (CacheKey) o;
90 return other.componentName.equals(componentName) && other.user.equals(user);
91 }
92 }
93
94 private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons =
95 new HashMap<UserHandleCompat, Bitmap>();
Daniel Sandlercc8befa2013-06-11 14:45:48 -040096 private final Context mContext;
Romain Guya28fd3f2010-03-15 14:44:42 -070097 private final PackageManager mPackageManager;
Kenny Guyed131872014-04-30 03:02:21 +010098 private final UserManagerCompat mUserManager;
99 private final LauncherAppsCompat mLauncherApps;
100 private final HashMap<CacheKey, CacheEntry> mCache =
101 new HashMap<CacheKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
Michael Jurkac9a96192010-11-01 11:52:08 -0700102 private int mIconDpi;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800103
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400104 public IconCache(Context context) {
Winson Chungd83f5f42012-02-13 14:27:42 -0800105 ActivityManager activityManager =
106 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
107
Joe Onorato0589f0f2010-02-08 13:44:00 -0800108 mContext = context;
109 mPackageManager = context.getPackageManager();
Kenny Guyed131872014-04-30 03:02:21 +0100110 mUserManager = UserManagerCompat.getInstance(mContext);
111 mLauncherApps = LauncherAppsCompat.getInstance(mContext);
Winson Chungd83f5f42012-02-13 14:27:42 -0800112 mIconDpi = activityManager.getLauncherLargeIconDensity();
113
Michael Jurkac9a96192010-11-01 11:52:08 -0700114 // need to set mIconDpi before getting default icon
Kenny Guyed131872014-04-30 03:02:21 +0100115 UserHandleCompat myUser = UserHandleCompat.myUserHandle();
116 mDefaultIcons.put(myUser, makeDefaultIcon(myUser));
Romain Guya28fd3f2010-03-15 14:44:42 -0700117 }
118
Michael Jurkac9a96192010-11-01 11:52:08 -0700119 public Drawable getFullResDefaultActivityIcon() {
120 return getFullResIcon(Resources.getSystem(),
Michael Jurka8b805b12012-04-18 14:23:14 -0700121 android.R.mipmap.sym_def_app_icon);
Michael Jurkac9a96192010-11-01 11:52:08 -0700122 }
123
Michael Jurka4842ed02011-07-07 15:33:20 -0700124 public Drawable getFullResIcon(Resources resources, int iconId) {
Michael Jurka721d9722011-08-03 11:49:59 -0700125 Drawable d;
Michael Jurka4842ed02011-07-07 15:33:20 -0700126 try {
Michael Jurka721d9722011-08-03 11:49:59 -0700127 d = resources.getDrawableForDensity(iconId, mIconDpi);
Michael Jurka4842ed02011-07-07 15:33:20 -0700128 } catch (Resources.NotFoundException e) {
Michael Jurka721d9722011-08-03 11:49:59 -0700129 d = null;
Michael Jurka4842ed02011-07-07 15:33:20 -0700130 }
Michael Jurka721d9722011-08-03 11:49:59 -0700131
132 return (d != null) ? d : getFullResDefaultActivityIcon();
Michael Jurkac9a96192010-11-01 11:52:08 -0700133 }
134
Winson Chung0b9fcf52011-10-31 13:05:15 -0700135 public Drawable getFullResIcon(String packageName, int iconId) {
Michael Jurkac9a96192010-11-01 11:52:08 -0700136 Resources resources;
137 try {
Winson Chung0b9fcf52011-10-31 13:05:15 -0700138 resources = mPackageManager.getResourcesForApplication(packageName);
139 } catch (PackageManager.NameNotFoundException e) {
140 resources = null;
141 }
142 if (resources != null) {
143 if (iconId != 0) {
144 return getFullResIcon(resources, iconId);
145 }
146 }
147 return getFullResDefaultActivityIcon();
148 }
149
Sunny Goyalffe83f12014-08-14 17:39:34 -0700150 public int getFullResIconDpi() {
151 return mIconDpi;
152 }
153
Winson Chung0b9fcf52011-10-31 13:05:15 -0700154 public Drawable getFullResIcon(ResolveInfo info) {
Michael Jurkadac85912012-05-18 15:04:49 -0700155 return getFullResIcon(info.activityInfo);
156 }
157
158 public Drawable getFullResIcon(ActivityInfo info) {
159
Winson Chung0b9fcf52011-10-31 13:05:15 -0700160 Resources resources;
161 try {
162 resources = mPackageManager.getResourcesForApplication(
Michael Jurkadac85912012-05-18 15:04:49 -0700163 info.applicationInfo);
Michael Jurkac9a96192010-11-01 11:52:08 -0700164 } catch (PackageManager.NameNotFoundException e) {
165 resources = null;
166 }
167 if (resources != null) {
Michael Jurkadac85912012-05-18 15:04:49 -0700168 int iconId = info.getIconResource();
Michael Jurkac9a96192010-11-01 11:52:08 -0700169 if (iconId != 0) {
170 return getFullResIcon(resources, iconId);
171 }
172 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500173
Michael Jurkac9a96192010-11-01 11:52:08 -0700174 return getFullResDefaultActivityIcon();
175 }
176
Kenny Guyed131872014-04-30 03:02:21 +0100177 private Bitmap makeDefaultIcon(UserHandleCompat user) {
178 Drawable unbadged = getFullResDefaultActivityIcon();
179 Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user);
Romain Guya28fd3f2010-03-15 14:44:42 -0700180 Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
181 Math.max(d.getIntrinsicHeight(), 1),
182 Bitmap.Config.ARGB_8888);
183 Canvas c = new Canvas(b);
184 d.setBounds(0, 0, b.getWidth(), b.getHeight());
185 d.draw(c);
Adam Cohenaaf473c2011-08-03 12:02:47 -0700186 c.setBitmap(null);
Romain Guya28fd3f2010-03-15 14:44:42 -0700187 return b;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800188 }
189
190 /**
191 * Remove any records for the supplied ComponentName.
192 */
Kenny Guyed131872014-04-30 03:02:21 +0100193 public void remove(ComponentName componentName, UserHandleCompat user) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800194 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100195 mCache.remove(new CacheKey(componentName, user));
Joe Onorato0589f0f2010-02-08 13:44:00 -0800196 }
197 }
198
199 /**
Chris Wren6d0dde02014-02-10 12:16:54 -0500200 * Remove any records for the supplied package name.
201 */
Kenny Guyed131872014-04-30 03:02:21 +0100202 public void remove(String packageName, UserHandleCompat user) {
203 HashSet<CacheKey> forDeletion = new HashSet<CacheKey>();
204 for (CacheKey key: mCache.keySet()) {
205 if (key.componentName.getPackageName().equals(packageName)
206 && key.user.equals(user)) {
207 forDeletion.add(key);
Chris Wren6d0dde02014-02-10 12:16:54 -0500208 }
209 }
Kenny Guyed131872014-04-30 03:02:21 +0100210 for (CacheKey condemned: forDeletion) {
211 mCache.remove(condemned);
Chris Wren6d0dde02014-02-10 12:16:54 -0500212 }
213 }
214
215 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800216 * Empty out the cache.
217 */
218 public void flush() {
219 synchronized (mCache) {
220 mCache.clear();
221 }
222 }
223
224 /**
Winson Chunge5467dc2013-10-14 17:03:04 -0700225 * Empty out the cache that aren't of the correct grid size
226 */
227 public void flushInvalidIcons(DeviceProfile grid) {
228 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100229 Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator();
Adam Cohenb6d33df2013-10-15 10:18:02 -0700230 while (it.hasNext()) {
231 final CacheEntry e = it.next().getValue();
Winson Chung6e1c0d32013-10-25 15:24:24 -0700232 if (e.icon.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) {
Adam Cohenb6d33df2013-10-15 10:18:02 -0700233 it.remove();
Winson Chunge5467dc2013-10-14 17:03:04 -0700234 }
235 }
236 }
237 }
238
239 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800240 * Fill in "application" with the icon and label for "info."
241 */
Kenny Guyed131872014-04-30 03:02:21 +0100242 public void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info,
Winson Chungc3eecff2011-07-11 17:44:15 -0700243 HashMap<Object, CharSequence> labelCache) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800244 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100245 CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700246 info.getUser(), false);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800247
248 application.title = entry.title;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800249 application.iconBitmap = entry.icon;
Kenny Guyc2bd8102014-06-30 12:30:31 +0100250 application.contentDescription = entry.contentDescription;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800251 }
252 }
253
Kenny Guyed131872014-04-30 03:02:21 +0100254 public Bitmap getIcon(Intent intent, UserHandleCompat user) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700255 return getIcon(intent, null, user, true);
Chris Wren6d0dde02014-02-10 12:16:54 -0500256 }
257
Sunny Goyal34942622014-08-29 17:20:55 -0700258 private Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700259 synchronized (mCache) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700260 ComponentName component = intent.getComponent();
Chris Wren075f9f52014-05-13 16:18:21 -0400261 // null info means not installed, but if we have a component from the intent then
262 // we should still look in the cache for restored app icons.
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700263 if (component == null) {
Kenny Guyed131872014-04-30 03:02:21 +0100264 return getDefaultIcon(user);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700265 }
266
Sunny Goyal34942622014-08-29 17:20:55 -0700267 LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user);
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700268 CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
Chris Wren6d0dde02014-02-10 12:16:54 -0500269 if (title != null) {
270 entry.title = title;
Kenny Guyc2bd8102014-06-30 12:30:31 +0100271 entry.contentDescription = mUserManager.getBadgedLabelForUser(title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500272 }
Joe Onoratofad1fb52010-05-04 12:12:41 -0700273 return entry.icon;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800274 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800275 }
276
Sunny Goyal34942622014-08-29 17:20:55 -0700277 /**
278 * Fill in "shortcutInfo" with the icon and label for "info."
279 */
280 public void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, UserHandleCompat user,
281 boolean usePkgIcon) {
282 synchronized (mCache) {
283 ComponentName component = intent.getComponent();
284 // null info means not installed, but if we have a component from the intent then
285 // we should still look in the cache for restored app icons.
286 if (component == null) {
287 shortcutInfo.setIcon(getDefaultIcon(user));
288 shortcutInfo.title = "";
289 shortcutInfo.usingFallbackIcon = true;
290 } else {
291 LauncherActivityInfoCompat launcherActInfo =
292 mLauncherApps.resolveActivity(intent, user);
293 CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
294
295 shortcutInfo.setIcon(entry.icon);
296 shortcutInfo.title = entry.title;
297 shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user);
298 }
299 }
300 }
301
302
Kenny Guyed131872014-04-30 03:02:21 +0100303 public Bitmap getDefaultIcon(UserHandleCompat user) {
304 if (!mDefaultIcons.containsKey(user)) {
305 mDefaultIcons.put(user, makeDefaultIcon(user));
306 }
307 return mDefaultIcons.get(user);
308 }
309
310 public Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info,
Winson Chungaac01e12011-08-17 10:37:13 -0700311 HashMap<Object, CharSequence> labelCache) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700312 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100313 if (info == null || component == null) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700314 return null;
315 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800316
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700317 CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700318 return entry.icon;
319 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800320 }
321
Kenny Guyed131872014-04-30 03:02:21 +0100322 public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) {
323 return mDefaultIcons.get(user) == icon;
Joe Onoratoddc9c1f2010-08-30 18:30:15 -0700324 }
325
Kenny Guyed131872014-04-30 03:02:21 +0100326 private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700327 HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {
Kenny Guyed131872014-04-30 03:02:21 +0100328 CacheKey cacheKey = new CacheKey(componentName, user);
329 CacheEntry entry = mCache.get(cacheKey);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800330 if (entry == null) {
331 entry = new CacheEntry();
332
Kenny Guyed131872014-04-30 03:02:21 +0100333 mCache.put(cacheKey, entry);
Joe Onorato84f6a8d2010-02-12 17:53:35 -0500334
Chris Wren6d0dde02014-02-10 12:16:54 -0500335 if (info != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100336 ComponentName labelKey = info.getComponentName();
337 if (labelCache != null && labelCache.containsKey(labelKey)) {
338 entry.title = labelCache.get(labelKey).toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500339 } else {
Kenny Guyed131872014-04-30 03:02:21 +0100340 entry.title = info.getLabel().toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500341 if (labelCache != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100342 labelCache.put(labelKey, entry.title);
Chris Wren6d0dde02014-02-10 12:16:54 -0500343 }
344 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500345
Kenny Guyc2bd8102014-06-30 12:30:31 +0100346 entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500347 entry.icon = Utilities.createIconBitmap(
Kenny Guyed131872014-04-30 03:02:21 +0100348 info.getBadgedIcon(mIconDpi), mContext);
Winson Chungc3eecff2011-07-11 17:44:15 -0700349 } else {
Chris Wren6d0dde02014-02-10 12:16:54 -0500350 entry.title = "";
Kenny Guyed131872014-04-30 03:02:21 +0100351 Bitmap preloaded = getPreloadedIcon(componentName, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500352 if (preloaded != null) {
353 if (DEBUG) Log.d(TAG, "using preloaded icon for " +
354 componentName.toShortString());
355 entry.icon = preloaded;
356 } else {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700357 if (usePackageIcon) {
358 CacheEntry packageEntry = getEntryForPackage(
359 componentName.getPackageName(), user);
Sunny Goyal34942622014-08-29 17:20:55 -0700360 if (packageEntry != null) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700361 if (DEBUG) Log.d(TAG, "using package default icon for " +
362 componentName.toShortString());
363 entry.icon = packageEntry.icon;
Sunny Goyal34942622014-08-29 17:20:55 -0700364 entry.title = packageEntry.title;
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700365 }
366 }
367 if (entry.icon == null) {
368 if (DEBUG) Log.d(TAG, "using default icon for " +
369 componentName.toShortString());
370 entry.icon = getDefaultIcon(user);
371 }
Winson Chungc3eecff2011-07-11 17:44:15 -0700372 }
373 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800374 }
375 return entry;
376 }
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400377
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700378 /**
Sunny Goyal34942622014-08-29 17:20:55 -0700379 * Adds a default package entry in the cache. This entry is not persisted and will be removed
380 * when the cache is flushed.
381 */
382 public void cachePackageInstallInfo(String packageName, UserHandleCompat user,
383 Bitmap icon, CharSequence title) {
384 CacheEntry entry = getEntryForPackage(packageName, user);
385 if (!TextUtils.isEmpty(title)) {
386 entry.title = title;
387 }
388 if (icon != null) {
389 entry.icon = Utilities.createIconBitmap(icon, mContext);
390 }
391 }
392
393 /**
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700394 * Gets an entry for the package, which can be used as a fallback entry for various components.
395 */
396 private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
397 ComponentName cn = getPackageComponent(packageName);
398 CacheKey cacheKey = new CacheKey(cn, user);
399 CacheEntry entry = mCache.get(cacheKey);
400 if (entry == null) {
401 entry = new CacheEntry();
Sunny Goyal34942622014-08-29 17:20:55 -0700402 entry.title = "";
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700403 mCache.put(cacheKey, entry);
404
405 try {
406 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
407 entry.title = info.loadLabel(mPackageManager);
408 entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext);
409 } catch (NameNotFoundException e) {
410 if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
411 }
412
413 if (entry.icon == null) {
414 entry.icon = getPreloadedIcon(cn, user);
415 }
416 }
417 return entry;
418 }
419
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400420 public HashMap<ComponentName,Bitmap> getAllIcons() {
421 synchronized (mCache) {
422 HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
Kenny Guyed131872014-04-30 03:02:21 +0100423 for (CacheKey ck : mCache.keySet()) {
424 final CacheEntry e = mCache.get(ck);
425 set.put(ck.componentName, e.icon);
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400426 }
427 return set;
428 }
429 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500430
431 /**
432 * Pre-load an icon into the persistent cache.
433 *
434 * <P>Queries for a component that does not exist in the package manager
435 * will be answered by the persistent cache.
436 *
437 * @param context application context
438 * @param componentName the icon should be returned for this component
439 * @param icon the icon to be persisted
440 * @param dpi the native density of the icon
441 */
442 public static void preloadIcon(Context context, ComponentName componentName, Bitmap icon,
443 int dpi) {
444 // TODO rescale to the correct native DPI
445 try {
446 PackageManager packageManager = context.getPackageManager();
447 packageManager.getActivityIcon(componentName);
448 // component is present on the system already, do nothing
449 return;
450 } catch (PackageManager.NameNotFoundException e) {
451 // pass
452 }
453
454 final String key = componentName.flattenToString();
455 FileOutputStream resourceFile = null;
456 try {
457 resourceFile = context.openFileOutput(getResourceFilename(componentName),
458 Context.MODE_PRIVATE);
459 ByteArrayOutputStream os = new ByteArrayOutputStream();
460 if (icon.compress(android.graphics.Bitmap.CompressFormat.PNG, 75, os)) {
461 byte[] buffer = os.toByteArray();
462 resourceFile.write(buffer, 0, buffer.length);
463 } else {
464 Log.w(TAG, "failed to encode cache for " + key);
465 return;
466 }
467 } catch (FileNotFoundException e) {
468 Log.w(TAG, "failed to pre-load cache for " + key, e);
469 } catch (IOException e) {
470 Log.w(TAG, "failed to pre-load cache for " + key, e);
471 } finally {
472 if (resourceFile != null) {
473 try {
474 resourceFile.close();
475 } catch (IOException e) {
476 Log.d(TAG, "failed to save restored icon for: " + key, e);
477 }
478 }
479 }
480 }
481
482 /**
483 * Read a pre-loaded icon from the persistent icon cache.
484 *
485 * @param componentName the component that should own the icon
486 * @returns a bitmap if one is cached, or null.
487 */
Kenny Guyed131872014-04-30 03:02:21 +0100488 private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) {
Chris Wren6d0dde02014-02-10 12:16:54 -0500489 final String key = componentName.flattenToShortString();
490
Kenny Guyed131872014-04-30 03:02:21 +0100491 // We don't keep icons for other profiles in persistent cache.
492 if (!user.equals(UserHandleCompat.myUserHandle())) {
493 return null;
494 }
495
Chris Wren6d0dde02014-02-10 12:16:54 -0500496 if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key);
497 Bitmap icon = null;
498 FileInputStream resourceFile = null;
499 try {
500 resourceFile = mContext.openFileInput(getResourceFilename(componentName));
501 byte[] buffer = new byte[1024];
502 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
503 int bytesRead = 0;
504 while(bytesRead >= 0) {
505 bytes.write(buffer, 0, bytesRead);
506 bytesRead = resourceFile.read(buffer, 0, buffer.length);
507 }
508 if (DEBUG) Log.d(TAG, "read " + bytes.size());
509 icon = BitmapFactory.decodeByteArray(bytes.toByteArray(), 0, bytes.size());
510 if (icon == null) {
511 Log.w(TAG, "failed to decode pre-load icon for " + key);
512 }
513 } catch (FileNotFoundException e) {
514 if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key, e);
515 } catch (IOException e) {
516 Log.w(TAG, "failed to read pre-load icon for: " + key, e);
517 } finally {
518 if(resourceFile != null) {
519 try {
520 resourceFile.close();
521 } catch (IOException e) {
522 Log.d(TAG, "failed to manage pre-load icon file: " + key, e);
523 }
524 }
525 }
526
Chris Wren6d0dde02014-02-10 12:16:54 -0500527 return icon;
528 }
529
530 /**
531 * Remove a pre-loaded icon from the persistent icon cache.
532 *
533 * @param componentName the component that should own the icon
534 * @returns true on success
535 */
Kenny Guyed131872014-04-30 03:02:21 +0100536 public boolean deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) {
537 // We don't keep icons for other profiles in persistent cache.
538 if (!user.equals(UserHandleCompat.myUserHandle())) {
539 return false;
540 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500541 if (componentName == null) {
542 return false;
543 }
544 if (mCache.remove(componentName) != null) {
545 if (DEBUG) Log.d(TAG, "removed pre-loaded icon from the in-memory cache");
546 }
547 boolean success = mContext.deleteFile(getResourceFilename(componentName));
548 if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache");
549
550 return success;
551 }
552
553 private static String getResourceFilename(ComponentName component) {
554 String resourceName = component.flattenToShortString();
555 String filename = resourceName.replace(File.separatorChar, '_');
556 return RESOURCE_FILE_PREFIX + filename;
557 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700558
559 static ComponentName getPackageComponent(String packageName) {
560 return new ComponentName(packageName, EMPTY_CLASS_NAME);
561 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800562}