blob: 47cca37f174dc79c377f1b70743c86da73836a5d [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
Sunny Goyalbbef77d2014-09-09 16:27:55 -070065 private static final boolean DEBUG = false;
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();
Sunny Goyal876d11e2014-09-15 08:48:12 -0700232 if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx
233 || e.icon.getHeight() < grid.iconSizePx)) {
Adam Cohenb6d33df2013-10-15 10:18:02 -0700234 it.remove();
Winson Chunge5467dc2013-10-14 17:03:04 -0700235 }
236 }
237 }
238 }
239
240 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800241 * Fill in "application" with the icon and label for "info."
242 */
Kenny Guyed131872014-04-30 03:02:21 +0100243 public void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info,
Winson Chungc3eecff2011-07-11 17:44:15 -0700244 HashMap<Object, CharSequence> labelCache) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800245 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100246 CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700247 info.getUser(), false);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800248
249 application.title = entry.title;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800250 application.iconBitmap = entry.icon;
Kenny Guyc2bd8102014-06-30 12:30:31 +0100251 application.contentDescription = entry.contentDescription;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800252 }
253 }
254
Kenny Guyed131872014-04-30 03:02:21 +0100255 public Bitmap getIcon(Intent intent, UserHandleCompat user) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700256 return getIcon(intent, null, user, true);
Chris Wren6d0dde02014-02-10 12:16:54 -0500257 }
258
Sunny Goyal34942622014-08-29 17:20:55 -0700259 private Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700260 synchronized (mCache) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700261 ComponentName component = intent.getComponent();
Chris Wren075f9f52014-05-13 16:18:21 -0400262 // null info means not installed, but if we have a component from the intent then
263 // we should still look in the cache for restored app icons.
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700264 if (component == null) {
Kenny Guyed131872014-04-30 03:02:21 +0100265 return getDefaultIcon(user);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700266 }
267
Sunny Goyal34942622014-08-29 17:20:55 -0700268 LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user);
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700269 CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
Chris Wren6d0dde02014-02-10 12:16:54 -0500270 if (title != null) {
271 entry.title = title;
Kenny Guyc2bd8102014-06-30 12:30:31 +0100272 entry.contentDescription = mUserManager.getBadgedLabelForUser(title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500273 }
Joe Onoratofad1fb52010-05-04 12:12:41 -0700274 return entry.icon;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800275 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800276 }
277
Sunny Goyal34942622014-08-29 17:20:55 -0700278 /**
279 * Fill in "shortcutInfo" with the icon and label for "info."
280 */
281 public void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, UserHandleCompat user,
282 boolean usePkgIcon) {
283 synchronized (mCache) {
284 ComponentName component = intent.getComponent();
285 // null info means not installed, but if we have a component from the intent then
286 // we should still look in the cache for restored app icons.
287 if (component == null) {
288 shortcutInfo.setIcon(getDefaultIcon(user));
289 shortcutInfo.title = "";
290 shortcutInfo.usingFallbackIcon = true;
291 } else {
292 LauncherActivityInfoCompat launcherActInfo =
293 mLauncherApps.resolveActivity(intent, user);
294 CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
295
296 shortcutInfo.setIcon(entry.icon);
297 shortcutInfo.title = entry.title;
298 shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user);
299 }
300 }
301 }
302
303
Kenny Guyed131872014-04-30 03:02:21 +0100304 public Bitmap getDefaultIcon(UserHandleCompat user) {
305 if (!mDefaultIcons.containsKey(user)) {
306 mDefaultIcons.put(user, makeDefaultIcon(user));
307 }
308 return mDefaultIcons.get(user);
309 }
310
311 public Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info,
Winson Chungaac01e12011-08-17 10:37:13 -0700312 HashMap<Object, CharSequence> labelCache) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700313 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100314 if (info == null || component == null) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700315 return null;
316 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800317
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700318 CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700319 return entry.icon;
320 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800321 }
322
Kenny Guyed131872014-04-30 03:02:21 +0100323 public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) {
324 return mDefaultIcons.get(user) == icon;
Joe Onoratoddc9c1f2010-08-30 18:30:15 -0700325 }
326
Kenny Guyed131872014-04-30 03:02:21 +0100327 private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700328 HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {
Kenny Guyed131872014-04-30 03:02:21 +0100329 CacheKey cacheKey = new CacheKey(componentName, user);
330 CacheEntry entry = mCache.get(cacheKey);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800331 if (entry == null) {
332 entry = new CacheEntry();
333
Kenny Guyed131872014-04-30 03:02:21 +0100334 mCache.put(cacheKey, entry);
Joe Onorato84f6a8d2010-02-12 17:53:35 -0500335
Chris Wren6d0dde02014-02-10 12:16:54 -0500336 if (info != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100337 ComponentName labelKey = info.getComponentName();
338 if (labelCache != null && labelCache.containsKey(labelKey)) {
339 entry.title = labelCache.get(labelKey).toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500340 } else {
Kenny Guyed131872014-04-30 03:02:21 +0100341 entry.title = info.getLabel().toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500342 if (labelCache != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100343 labelCache.put(labelKey, entry.title);
Chris Wren6d0dde02014-02-10 12:16:54 -0500344 }
345 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500346
Kenny Guyc2bd8102014-06-30 12:30:31 +0100347 entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500348 entry.icon = Utilities.createIconBitmap(
Kenny Guyed131872014-04-30 03:02:21 +0100349 info.getBadgedIcon(mIconDpi), mContext);
Winson Chungc3eecff2011-07-11 17:44:15 -0700350 } else {
Chris Wren6d0dde02014-02-10 12:16:54 -0500351 entry.title = "";
Kenny Guyed131872014-04-30 03:02:21 +0100352 Bitmap preloaded = getPreloadedIcon(componentName, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500353 if (preloaded != null) {
354 if (DEBUG) Log.d(TAG, "using preloaded icon for " +
355 componentName.toShortString());
356 entry.icon = preloaded;
357 } else {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700358 if (usePackageIcon) {
359 CacheEntry packageEntry = getEntryForPackage(
360 componentName.getPackageName(), user);
Sunny Goyal34942622014-08-29 17:20:55 -0700361 if (packageEntry != null) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700362 if (DEBUG) Log.d(TAG, "using package default icon for " +
363 componentName.toShortString());
364 entry.icon = packageEntry.icon;
Sunny Goyal34942622014-08-29 17:20:55 -0700365 entry.title = packageEntry.title;
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700366 }
367 }
368 if (entry.icon == null) {
369 if (DEBUG) Log.d(TAG, "using default icon for " +
370 componentName.toShortString());
371 entry.icon = getDefaultIcon(user);
372 }
Winson Chungc3eecff2011-07-11 17:44:15 -0700373 }
374 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800375 }
376 return entry;
377 }
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400378
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700379 /**
Sunny Goyal34942622014-08-29 17:20:55 -0700380 * Adds a default package entry in the cache. This entry is not persisted and will be removed
381 * when the cache is flushed.
382 */
383 public void cachePackageInstallInfo(String packageName, UserHandleCompat user,
384 Bitmap icon, CharSequence title) {
Sunny Goyala22666f2014-09-18 13:25:15 -0700385 remove(packageName, user);
386
Sunny Goyal34942622014-08-29 17:20:55 -0700387 CacheEntry entry = getEntryForPackage(packageName, user);
388 if (!TextUtils.isEmpty(title)) {
389 entry.title = title;
390 }
391 if (icon != null) {
Sunny Goyal2fce90c2014-10-07 12:01:58 -0700392 entry.icon = Utilities.createIconBitmap(icon, mContext);
Sunny Goyal34942622014-08-29 17:20:55 -0700393 }
394 }
395
396 /**
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700397 * Gets an entry for the package, which can be used as a fallback entry for various components.
398 */
399 private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
400 ComponentName cn = getPackageComponent(packageName);
401 CacheKey cacheKey = new CacheKey(cn, user);
402 CacheEntry entry = mCache.get(cacheKey);
403 if (entry == null) {
404 entry = new CacheEntry();
Sunny Goyal34942622014-08-29 17:20:55 -0700405 entry.title = "";
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700406 mCache.put(cacheKey, entry);
407
408 try {
409 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
410 entry.title = info.loadLabel(mPackageManager);
411 entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext);
412 } catch (NameNotFoundException e) {
413 if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
414 }
415
416 if (entry.icon == null) {
417 entry.icon = getPreloadedIcon(cn, user);
418 }
419 }
420 return entry;
421 }
422
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400423 public HashMap<ComponentName,Bitmap> getAllIcons() {
424 synchronized (mCache) {
425 HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
Kenny Guyed131872014-04-30 03:02:21 +0100426 for (CacheKey ck : mCache.keySet()) {
427 final CacheEntry e = mCache.get(ck);
428 set.put(ck.componentName, e.icon);
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400429 }
430 return set;
431 }
432 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500433
434 /**
435 * Pre-load an icon into the persistent cache.
436 *
437 * <P>Queries for a component that does not exist in the package manager
438 * will be answered by the persistent cache.
439 *
440 * @param context application context
441 * @param componentName the icon should be returned for this component
442 * @param icon the icon to be persisted
443 * @param dpi the native density of the icon
444 */
445 public static void preloadIcon(Context context, ComponentName componentName, Bitmap icon,
446 int dpi) {
447 // TODO rescale to the correct native DPI
448 try {
449 PackageManager packageManager = context.getPackageManager();
450 packageManager.getActivityIcon(componentName);
451 // component is present on the system already, do nothing
452 return;
453 } catch (PackageManager.NameNotFoundException e) {
454 // pass
455 }
456
457 final String key = componentName.flattenToString();
458 FileOutputStream resourceFile = null;
459 try {
460 resourceFile = context.openFileOutput(getResourceFilename(componentName),
461 Context.MODE_PRIVATE);
462 ByteArrayOutputStream os = new ByteArrayOutputStream();
463 if (icon.compress(android.graphics.Bitmap.CompressFormat.PNG, 75, os)) {
464 byte[] buffer = os.toByteArray();
465 resourceFile.write(buffer, 0, buffer.length);
466 } else {
467 Log.w(TAG, "failed to encode cache for " + key);
468 return;
469 }
470 } catch (FileNotFoundException e) {
471 Log.w(TAG, "failed to pre-load cache for " + key, e);
472 } catch (IOException e) {
473 Log.w(TAG, "failed to pre-load cache for " + key, e);
474 } finally {
475 if (resourceFile != null) {
476 try {
477 resourceFile.close();
478 } catch (IOException e) {
479 Log.d(TAG, "failed to save restored icon for: " + key, e);
480 }
481 }
482 }
483 }
484
485 /**
486 * Read a pre-loaded icon from the persistent icon cache.
487 *
488 * @param componentName the component that should own the icon
489 * @returns a bitmap if one is cached, or null.
490 */
Kenny Guyed131872014-04-30 03:02:21 +0100491 private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) {
Chris Wren6d0dde02014-02-10 12:16:54 -0500492 final String key = componentName.flattenToShortString();
493
Kenny Guyed131872014-04-30 03:02:21 +0100494 // We don't keep icons for other profiles in persistent cache.
495 if (!user.equals(UserHandleCompat.myUserHandle())) {
496 return null;
497 }
498
Chris Wren6d0dde02014-02-10 12:16:54 -0500499 if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key);
500 Bitmap icon = null;
501 FileInputStream resourceFile = null;
502 try {
503 resourceFile = mContext.openFileInput(getResourceFilename(componentName));
504 byte[] buffer = new byte[1024];
505 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
506 int bytesRead = 0;
507 while(bytesRead >= 0) {
508 bytes.write(buffer, 0, bytesRead);
509 bytesRead = resourceFile.read(buffer, 0, buffer.length);
510 }
511 if (DEBUG) Log.d(TAG, "read " + bytes.size());
512 icon = BitmapFactory.decodeByteArray(bytes.toByteArray(), 0, bytes.size());
513 if (icon == null) {
514 Log.w(TAG, "failed to decode pre-load icon for " + key);
515 }
516 } catch (FileNotFoundException e) {
Sunny Goyalbbef77d2014-09-09 16:27:55 -0700517 if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key);
Chris Wren6d0dde02014-02-10 12:16:54 -0500518 } catch (IOException e) {
519 Log.w(TAG, "failed to read pre-load icon for: " + key, e);
520 } finally {
521 if(resourceFile != null) {
522 try {
523 resourceFile.close();
524 } catch (IOException e) {
525 Log.d(TAG, "failed to manage pre-load icon file: " + key, e);
526 }
527 }
528 }
529
Chris Wren6d0dde02014-02-10 12:16:54 -0500530 return icon;
531 }
532
533 /**
534 * Remove a pre-loaded icon from the persistent icon cache.
535 *
536 * @param componentName the component that should own the icon
537 * @returns true on success
538 */
Kenny Guyed131872014-04-30 03:02:21 +0100539 public boolean deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) {
540 // We don't keep icons for other profiles in persistent cache.
541 if (!user.equals(UserHandleCompat.myUserHandle())) {
542 return false;
543 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500544 if (componentName == null) {
545 return false;
546 }
547 if (mCache.remove(componentName) != null) {
548 if (DEBUG) Log.d(TAG, "removed pre-loaded icon from the in-memory cache");
549 }
550 boolean success = mContext.deleteFile(getResourceFilename(componentName));
551 if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache");
552
553 return success;
554 }
555
556 private static String getResourceFilename(ComponentName component) {
557 String resourceName = component.flattenToShortString();
558 String filename = resourceName.replace(File.separatorChar, '_');
559 return RESOURCE_FILE_PREFIX + filename;
560 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700561
562 static ComponentName getPackageComponent(String packageName) {
563 return new ComponentName(packageName, EMPTY_CLASS_NAME);
564 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800565}