blob: 76a85caaec93ce8c56ee89e1d9b72a44558f9ee4 [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;
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
Chris Wren6d0dde02014-02-10 12:16:54 -050064 private static final boolean DEBUG = true;
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() {
119 return getFullResIcon(Resources.getSystem(),
Michael Jurka8b805b12012-04-18 14:23:14 -0700120 android.R.mipmap.sym_def_app_icon);
Michael Jurkac9a96192010-11-01 11:52:08 -0700121 }
122
Michael Jurka4842ed02011-07-07 15:33:20 -0700123 public Drawable getFullResIcon(Resources resources, int iconId) {
Michael Jurka721d9722011-08-03 11:49:59 -0700124 Drawable d;
Michael Jurka4842ed02011-07-07 15:33:20 -0700125 try {
Michael Jurka721d9722011-08-03 11:49:59 -0700126 d = resources.getDrawableForDensity(iconId, mIconDpi);
Michael Jurka4842ed02011-07-07 15:33:20 -0700127 } catch (Resources.NotFoundException e) {
Michael Jurka721d9722011-08-03 11:49:59 -0700128 d = null;
Michael Jurka4842ed02011-07-07 15:33:20 -0700129 }
Michael Jurka721d9722011-08-03 11:49:59 -0700130
131 return (d != null) ? d : getFullResDefaultActivityIcon();
Michael Jurkac9a96192010-11-01 11:52:08 -0700132 }
133
Winson Chung0b9fcf52011-10-31 13:05:15 -0700134 public Drawable getFullResIcon(String packageName, int iconId) {
Michael Jurkac9a96192010-11-01 11:52:08 -0700135 Resources resources;
136 try {
Winson Chung0b9fcf52011-10-31 13:05:15 -0700137 resources = mPackageManager.getResourcesForApplication(packageName);
138 } catch (PackageManager.NameNotFoundException e) {
139 resources = null;
140 }
141 if (resources != null) {
142 if (iconId != 0) {
143 return getFullResIcon(resources, iconId);
144 }
145 }
146 return getFullResDefaultActivityIcon();
147 }
148
Sunny Goyalffe83f12014-08-14 17:39:34 -0700149 public int getFullResIconDpi() {
150 return mIconDpi;
151 }
152
Winson Chung0b9fcf52011-10-31 13:05:15 -0700153 public Drawable getFullResIcon(ResolveInfo info) {
Michael Jurkadac85912012-05-18 15:04:49 -0700154 return getFullResIcon(info.activityInfo);
155 }
156
157 public Drawable getFullResIcon(ActivityInfo info) {
158
Winson Chung0b9fcf52011-10-31 13:05:15 -0700159 Resources resources;
160 try {
161 resources = mPackageManager.getResourcesForApplication(
Michael Jurkadac85912012-05-18 15:04:49 -0700162 info.applicationInfo);
Michael Jurkac9a96192010-11-01 11:52:08 -0700163 } catch (PackageManager.NameNotFoundException e) {
164 resources = null;
165 }
166 if (resources != null) {
Michael Jurkadac85912012-05-18 15:04:49 -0700167 int iconId = info.getIconResource();
Michael Jurkac9a96192010-11-01 11:52:08 -0700168 if (iconId != 0) {
169 return getFullResIcon(resources, iconId);
170 }
171 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500172
Michael Jurkac9a96192010-11-01 11:52:08 -0700173 return getFullResDefaultActivityIcon();
174 }
175
Kenny Guyed131872014-04-30 03:02:21 +0100176 private Bitmap makeDefaultIcon(UserHandleCompat user) {
177 Drawable unbadged = getFullResDefaultActivityIcon();
178 Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user);
Romain Guya28fd3f2010-03-15 14:44:42 -0700179 Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
180 Math.max(d.getIntrinsicHeight(), 1),
181 Bitmap.Config.ARGB_8888);
182 Canvas c = new Canvas(b);
183 d.setBounds(0, 0, b.getWidth(), b.getHeight());
184 d.draw(c);
Adam Cohenaaf473c2011-08-03 12:02:47 -0700185 c.setBitmap(null);
Romain Guya28fd3f2010-03-15 14:44:42 -0700186 return b;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800187 }
188
189 /**
190 * Remove any records for the supplied ComponentName.
191 */
Kenny Guyed131872014-04-30 03:02:21 +0100192 public void remove(ComponentName componentName, UserHandleCompat user) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800193 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100194 mCache.remove(new CacheKey(componentName, user));
Joe Onorato0589f0f2010-02-08 13:44:00 -0800195 }
196 }
197
198 /**
Chris Wren6d0dde02014-02-10 12:16:54 -0500199 * Remove any records for the supplied package name.
200 */
Kenny Guyed131872014-04-30 03:02:21 +0100201 public void remove(String packageName, UserHandleCompat user) {
202 HashSet<CacheKey> forDeletion = new HashSet<CacheKey>();
203 for (CacheKey key: mCache.keySet()) {
204 if (key.componentName.getPackageName().equals(packageName)
205 && key.user.equals(user)) {
206 forDeletion.add(key);
Chris Wren6d0dde02014-02-10 12:16:54 -0500207 }
208 }
Kenny Guyed131872014-04-30 03:02:21 +0100209 for (CacheKey condemned: forDeletion) {
210 mCache.remove(condemned);
Chris Wren6d0dde02014-02-10 12:16:54 -0500211 }
212 }
213
214 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800215 * Empty out the cache.
216 */
217 public void flush() {
218 synchronized (mCache) {
219 mCache.clear();
220 }
221 }
222
223 /**
Winson Chunge5467dc2013-10-14 17:03:04 -0700224 * Empty out the cache that aren't of the correct grid size
225 */
226 public void flushInvalidIcons(DeviceProfile grid) {
227 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100228 Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator();
Adam Cohenb6d33df2013-10-15 10:18:02 -0700229 while (it.hasNext()) {
230 final CacheEntry e = it.next().getValue();
Winson Chung6e1c0d32013-10-25 15:24:24 -0700231 if (e.icon.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) {
Adam Cohenb6d33df2013-10-15 10:18:02 -0700232 it.remove();
Winson Chunge5467dc2013-10-14 17:03:04 -0700233 }
234 }
235 }
236 }
237
238 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800239 * Fill in "application" with the icon and label for "info."
240 */
Kenny Guyed131872014-04-30 03:02:21 +0100241 public void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info,
Winson Chungc3eecff2011-07-11 17:44:15 -0700242 HashMap<Object, CharSequence> labelCache) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800243 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100244 CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700245 info.getUser(), false);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800246
247 application.title = entry.title;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800248 application.iconBitmap = entry.icon;
Kenny Guyc2bd8102014-06-30 12:30:31 +0100249 application.contentDescription = entry.contentDescription;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800250 }
251 }
252
Kenny Guyed131872014-04-30 03:02:21 +0100253 public Bitmap getIcon(Intent intent, UserHandleCompat user) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700254 return getIcon(intent, null, user, true);
Chris Wren6d0dde02014-02-10 12:16:54 -0500255 }
256
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700257 public Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700258 synchronized (mCache) {
Chris Wren075f9f52014-05-13 16:18:21 -0400259 LauncherActivityInfoCompat launcherActInfo =
Kenny Guyed131872014-04-30 03:02:21 +0100260 mLauncherApps.resolveActivity(intent, user);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700261 ComponentName component = intent.getComponent();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800262
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 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
Kenny Guyed131872014-04-30 03:02:21 +0100278 public Bitmap getDefaultIcon(UserHandleCompat user) {
279 if (!mDefaultIcons.containsKey(user)) {
280 mDefaultIcons.put(user, makeDefaultIcon(user));
281 }
282 return mDefaultIcons.get(user);
283 }
284
285 public Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info,
Winson Chungaac01e12011-08-17 10:37:13 -0700286 HashMap<Object, CharSequence> labelCache) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700287 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100288 if (info == null || component == null) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700289 return null;
290 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800291
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700292 CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700293 return entry.icon;
294 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800295 }
296
Kenny Guyed131872014-04-30 03:02:21 +0100297 public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) {
298 return mDefaultIcons.get(user) == icon;
Joe Onoratoddc9c1f2010-08-30 18:30:15 -0700299 }
300
Kenny Guyed131872014-04-30 03:02:21 +0100301 private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700302 HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {
Kenny Guyed131872014-04-30 03:02:21 +0100303 CacheKey cacheKey = new CacheKey(componentName, user);
304 CacheEntry entry = mCache.get(cacheKey);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800305 if (entry == null) {
306 entry = new CacheEntry();
307
Kenny Guyed131872014-04-30 03:02:21 +0100308 mCache.put(cacheKey, entry);
Joe Onorato84f6a8d2010-02-12 17:53:35 -0500309
Chris Wren6d0dde02014-02-10 12:16:54 -0500310 if (info != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100311 ComponentName labelKey = info.getComponentName();
312 if (labelCache != null && labelCache.containsKey(labelKey)) {
313 entry.title = labelCache.get(labelKey).toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500314 } else {
Kenny Guyed131872014-04-30 03:02:21 +0100315 entry.title = info.getLabel().toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500316 if (labelCache != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100317 labelCache.put(labelKey, entry.title);
Chris Wren6d0dde02014-02-10 12:16:54 -0500318 }
319 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500320
Kenny Guyc2bd8102014-06-30 12:30:31 +0100321 entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500322 entry.icon = Utilities.createIconBitmap(
Kenny Guyed131872014-04-30 03:02:21 +0100323 info.getBadgedIcon(mIconDpi), mContext);
Winson Chungc3eecff2011-07-11 17:44:15 -0700324 } else {
Chris Wren6d0dde02014-02-10 12:16:54 -0500325 entry.title = "";
Kenny Guyed131872014-04-30 03:02:21 +0100326 Bitmap preloaded = getPreloadedIcon(componentName, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500327 if (preloaded != null) {
328 if (DEBUG) Log.d(TAG, "using preloaded icon for " +
329 componentName.toShortString());
330 entry.icon = preloaded;
331 } else {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700332 if (usePackageIcon) {
333 CacheEntry packageEntry = getEntryForPackage(
334 componentName.getPackageName(), user);
335 if (packageEntry != null && packageEntry.icon != null) {
336 if (DEBUG) Log.d(TAG, "using package default icon for " +
337 componentName.toShortString());
338 entry.icon = packageEntry.icon;
339 }
340 }
341 if (entry.icon == null) {
342 if (DEBUG) Log.d(TAG, "using default icon for " +
343 componentName.toShortString());
344 entry.icon = getDefaultIcon(user);
345 }
Winson Chungc3eecff2011-07-11 17:44:15 -0700346 }
347 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800348 }
349 return entry;
350 }
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400351
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700352 /**
353 * Gets an entry for the package, which can be used as a fallback entry for various components.
354 */
355 private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
356 ComponentName cn = getPackageComponent(packageName);
357 CacheKey cacheKey = new CacheKey(cn, user);
358 CacheEntry entry = mCache.get(cacheKey);
359 if (entry == null) {
360 entry = new CacheEntry();
361 mCache.put(cacheKey, entry);
362
363 try {
364 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
365 entry.title = info.loadLabel(mPackageManager);
366 entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext);
367 } catch (NameNotFoundException e) {
368 if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
369 }
370
371 if (entry.icon == null) {
372 entry.icon = getPreloadedIcon(cn, user);
373 }
374 }
375 return entry;
376 }
377
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400378 public HashMap<ComponentName,Bitmap> getAllIcons() {
379 synchronized (mCache) {
380 HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
Kenny Guyed131872014-04-30 03:02:21 +0100381 for (CacheKey ck : mCache.keySet()) {
382 final CacheEntry e = mCache.get(ck);
383 set.put(ck.componentName, e.icon);
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400384 }
385 return set;
386 }
387 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500388
389 /**
390 * Pre-load an icon into the persistent cache.
391 *
392 * <P>Queries for a component that does not exist in the package manager
393 * will be answered by the persistent cache.
394 *
395 * @param context application context
396 * @param componentName the icon should be returned for this component
397 * @param icon the icon to be persisted
398 * @param dpi the native density of the icon
399 */
400 public static void preloadIcon(Context context, ComponentName componentName, Bitmap icon,
401 int dpi) {
402 // TODO rescale to the correct native DPI
403 try {
404 PackageManager packageManager = context.getPackageManager();
405 packageManager.getActivityIcon(componentName);
406 // component is present on the system already, do nothing
407 return;
408 } catch (PackageManager.NameNotFoundException e) {
409 // pass
410 }
411
412 final String key = componentName.flattenToString();
413 FileOutputStream resourceFile = null;
414 try {
415 resourceFile = context.openFileOutput(getResourceFilename(componentName),
416 Context.MODE_PRIVATE);
417 ByteArrayOutputStream os = new ByteArrayOutputStream();
418 if (icon.compress(android.graphics.Bitmap.CompressFormat.PNG, 75, os)) {
419 byte[] buffer = os.toByteArray();
420 resourceFile.write(buffer, 0, buffer.length);
421 } else {
422 Log.w(TAG, "failed to encode cache for " + key);
423 return;
424 }
425 } catch (FileNotFoundException e) {
426 Log.w(TAG, "failed to pre-load cache for " + key, e);
427 } catch (IOException e) {
428 Log.w(TAG, "failed to pre-load cache for " + key, e);
429 } finally {
430 if (resourceFile != null) {
431 try {
432 resourceFile.close();
433 } catch (IOException e) {
434 Log.d(TAG, "failed to save restored icon for: " + key, e);
435 }
436 }
437 }
438 }
439
440 /**
441 * Read a pre-loaded icon from the persistent icon cache.
442 *
443 * @param componentName the component that should own the icon
444 * @returns a bitmap if one is cached, or null.
445 */
Kenny Guyed131872014-04-30 03:02:21 +0100446 private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) {
Chris Wren6d0dde02014-02-10 12:16:54 -0500447 final String key = componentName.flattenToShortString();
448
Kenny Guyed131872014-04-30 03:02:21 +0100449 // We don't keep icons for other profiles in persistent cache.
450 if (!user.equals(UserHandleCompat.myUserHandle())) {
451 return null;
452 }
453
Chris Wren6d0dde02014-02-10 12:16:54 -0500454 if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key);
455 Bitmap icon = null;
456 FileInputStream resourceFile = null;
457 try {
458 resourceFile = mContext.openFileInput(getResourceFilename(componentName));
459 byte[] buffer = new byte[1024];
460 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
461 int bytesRead = 0;
462 while(bytesRead >= 0) {
463 bytes.write(buffer, 0, bytesRead);
464 bytesRead = resourceFile.read(buffer, 0, buffer.length);
465 }
466 if (DEBUG) Log.d(TAG, "read " + bytes.size());
467 icon = BitmapFactory.decodeByteArray(bytes.toByteArray(), 0, bytes.size());
468 if (icon == null) {
469 Log.w(TAG, "failed to decode pre-load icon for " + key);
470 }
471 } catch (FileNotFoundException e) {
472 if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key, e);
473 } catch (IOException e) {
474 Log.w(TAG, "failed to read pre-load icon for: " + key, e);
475 } finally {
476 if(resourceFile != null) {
477 try {
478 resourceFile.close();
479 } catch (IOException e) {
480 Log.d(TAG, "failed to manage pre-load icon file: " + key, e);
481 }
482 }
483 }
484
Chris Wren6d0dde02014-02-10 12:16:54 -0500485 return icon;
486 }
487
488 /**
489 * Remove a pre-loaded icon from the persistent icon cache.
490 *
491 * @param componentName the component that should own the icon
492 * @returns true on success
493 */
Kenny Guyed131872014-04-30 03:02:21 +0100494 public boolean deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) {
495 // We don't keep icons for other profiles in persistent cache.
496 if (!user.equals(UserHandleCompat.myUserHandle())) {
497 return false;
498 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500499 if (componentName == null) {
500 return false;
501 }
502 if (mCache.remove(componentName) != null) {
503 if (DEBUG) Log.d(TAG, "removed pre-loaded icon from the in-memory cache");
504 }
505 boolean success = mContext.deleteFile(getResourceFilename(componentName));
506 if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache");
507
508 return success;
509 }
510
511 private static String getResourceFilename(ComponentName component) {
512 String resourceName = component.flattenToShortString();
513 String filename = resourceName.replace(File.separatorChar, '_');
514 return RESOURCE_FILE_PREFIX + filename;
515 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700516
517 static ComponentName getPackageComponent(String packageName) {
518 return new ComponentName(packageName, EMPTY_CLASS_NAME);
519 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800520}