blob: 06b77756de5b80f2b7e2892be1f6815282339988 [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
149 public Drawable getFullResIcon(ResolveInfo info) {
Michael Jurkadac85912012-05-18 15:04:49 -0700150 return getFullResIcon(info.activityInfo);
151 }
152
153 public Drawable getFullResIcon(ActivityInfo info) {
154
Winson Chung0b9fcf52011-10-31 13:05:15 -0700155 Resources resources;
156 try {
157 resources = mPackageManager.getResourcesForApplication(
Michael Jurkadac85912012-05-18 15:04:49 -0700158 info.applicationInfo);
Michael Jurkac9a96192010-11-01 11:52:08 -0700159 } catch (PackageManager.NameNotFoundException e) {
160 resources = null;
161 }
162 if (resources != null) {
Michael Jurkadac85912012-05-18 15:04:49 -0700163 int iconId = info.getIconResource();
Michael Jurkac9a96192010-11-01 11:52:08 -0700164 if (iconId != 0) {
165 return getFullResIcon(resources, iconId);
166 }
167 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500168
Michael Jurkac9a96192010-11-01 11:52:08 -0700169 return getFullResDefaultActivityIcon();
170 }
171
Kenny Guyed131872014-04-30 03:02:21 +0100172 private Bitmap makeDefaultIcon(UserHandleCompat user) {
173 Drawable unbadged = getFullResDefaultActivityIcon();
174 Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user);
Romain Guya28fd3f2010-03-15 14:44:42 -0700175 Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
176 Math.max(d.getIntrinsicHeight(), 1),
177 Bitmap.Config.ARGB_8888);
178 Canvas c = new Canvas(b);
179 d.setBounds(0, 0, b.getWidth(), b.getHeight());
180 d.draw(c);
Adam Cohenaaf473c2011-08-03 12:02:47 -0700181 c.setBitmap(null);
Romain Guya28fd3f2010-03-15 14:44:42 -0700182 return b;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800183 }
184
185 /**
186 * Remove any records for the supplied ComponentName.
187 */
Kenny Guyed131872014-04-30 03:02:21 +0100188 public void remove(ComponentName componentName, UserHandleCompat user) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800189 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100190 mCache.remove(new CacheKey(componentName, user));
Joe Onorato0589f0f2010-02-08 13:44:00 -0800191 }
192 }
193
194 /**
Chris Wren6d0dde02014-02-10 12:16:54 -0500195 * Remove any records for the supplied package name.
196 */
Kenny Guyed131872014-04-30 03:02:21 +0100197 public void remove(String packageName, UserHandleCompat user) {
198 HashSet<CacheKey> forDeletion = new HashSet<CacheKey>();
199 for (CacheKey key: mCache.keySet()) {
200 if (key.componentName.getPackageName().equals(packageName)
201 && key.user.equals(user)) {
202 forDeletion.add(key);
Chris Wren6d0dde02014-02-10 12:16:54 -0500203 }
204 }
Kenny Guyed131872014-04-30 03:02:21 +0100205 for (CacheKey condemned: forDeletion) {
206 mCache.remove(condemned);
Chris Wren6d0dde02014-02-10 12:16:54 -0500207 }
208 }
209
210 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800211 * Empty out the cache.
212 */
213 public void flush() {
214 synchronized (mCache) {
215 mCache.clear();
216 }
217 }
218
219 /**
Winson Chunge5467dc2013-10-14 17:03:04 -0700220 * Empty out the cache that aren't of the correct grid size
221 */
222 public void flushInvalidIcons(DeviceProfile grid) {
223 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100224 Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator();
Adam Cohenb6d33df2013-10-15 10:18:02 -0700225 while (it.hasNext()) {
226 final CacheEntry e = it.next().getValue();
Winson Chung6e1c0d32013-10-25 15:24:24 -0700227 if (e.icon.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) {
Adam Cohenb6d33df2013-10-15 10:18:02 -0700228 it.remove();
Winson Chunge5467dc2013-10-14 17:03:04 -0700229 }
230 }
231 }
232 }
233
234 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -0800235 * Fill in "application" with the icon and label for "info."
236 */
Kenny Guyed131872014-04-30 03:02:21 +0100237 public void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info,
Winson Chungc3eecff2011-07-11 17:44:15 -0700238 HashMap<Object, CharSequence> labelCache) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800239 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100240 CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700241 info.getUser(), false);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800242
243 application.title = entry.title;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800244 application.iconBitmap = entry.icon;
Kenny Guyc2bd8102014-06-30 12:30:31 +0100245 application.contentDescription = entry.contentDescription;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800246 }
247 }
248
Kenny Guyed131872014-04-30 03:02:21 +0100249 public Bitmap getIcon(Intent intent, UserHandleCompat user) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700250 return getIcon(intent, null, user, true);
Chris Wren6d0dde02014-02-10 12:16:54 -0500251 }
252
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700253 public Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700254 synchronized (mCache) {
Chris Wren075f9f52014-05-13 16:18:21 -0400255 LauncherActivityInfoCompat launcherActInfo =
Kenny Guyed131872014-04-30 03:02:21 +0100256 mLauncherApps.resolveActivity(intent, user);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700257 ComponentName component = intent.getComponent();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800258
Chris Wren075f9f52014-05-13 16:18:21 -0400259 // 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.
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700261 if (component == null) {
Kenny Guyed131872014-04-30 03:02:21 +0100262 return getDefaultIcon(user);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700263 }
264
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700265 CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
Chris Wren6d0dde02014-02-10 12:16:54 -0500266 if (title != null) {
267 entry.title = title;
Kenny Guyc2bd8102014-06-30 12:30:31 +0100268 entry.contentDescription = mUserManager.getBadgedLabelForUser(title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500269 }
Joe Onoratofad1fb52010-05-04 12:12:41 -0700270 return entry.icon;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800271 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800272 }
273
Kenny Guyed131872014-04-30 03:02:21 +0100274 public Bitmap getDefaultIcon(UserHandleCompat user) {
275 if (!mDefaultIcons.containsKey(user)) {
276 mDefaultIcons.put(user, makeDefaultIcon(user));
277 }
278 return mDefaultIcons.get(user);
279 }
280
281 public Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info,
Winson Chungaac01e12011-08-17 10:37:13 -0700282 HashMap<Object, CharSequence> labelCache) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700283 synchronized (mCache) {
Kenny Guyed131872014-04-30 03:02:21 +0100284 if (info == null || component == null) {
Joe Onoratofad1fb52010-05-04 12:12:41 -0700285 return null;
286 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800287
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700288 CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false);
Joe Onoratofad1fb52010-05-04 12:12:41 -0700289 return entry.icon;
290 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800291 }
292
Kenny Guyed131872014-04-30 03:02:21 +0100293 public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) {
294 return mDefaultIcons.get(user) == icon;
Joe Onoratoddc9c1f2010-08-30 18:30:15 -0700295 }
296
Kenny Guyed131872014-04-30 03:02:21 +0100297 private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700298 HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {
Kenny Guyed131872014-04-30 03:02:21 +0100299 CacheKey cacheKey = new CacheKey(componentName, user);
300 CacheEntry entry = mCache.get(cacheKey);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800301 if (entry == null) {
302 entry = new CacheEntry();
303
Kenny Guyed131872014-04-30 03:02:21 +0100304 mCache.put(cacheKey, entry);
Joe Onorato84f6a8d2010-02-12 17:53:35 -0500305
Chris Wren6d0dde02014-02-10 12:16:54 -0500306 if (info != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100307 ComponentName labelKey = info.getComponentName();
308 if (labelCache != null && labelCache.containsKey(labelKey)) {
309 entry.title = labelCache.get(labelKey).toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500310 } else {
Kenny Guyed131872014-04-30 03:02:21 +0100311 entry.title = info.getLabel().toString();
Chris Wren6d0dde02014-02-10 12:16:54 -0500312 if (labelCache != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100313 labelCache.put(labelKey, entry.title);
Chris Wren6d0dde02014-02-10 12:16:54 -0500314 }
315 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500316
Kenny Guyc2bd8102014-06-30 12:30:31 +0100317 entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500318 entry.icon = Utilities.createIconBitmap(
Kenny Guyed131872014-04-30 03:02:21 +0100319 info.getBadgedIcon(mIconDpi), mContext);
Winson Chungc3eecff2011-07-11 17:44:15 -0700320 } else {
Chris Wren6d0dde02014-02-10 12:16:54 -0500321 entry.title = "";
Kenny Guyed131872014-04-30 03:02:21 +0100322 Bitmap preloaded = getPreloadedIcon(componentName, user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500323 if (preloaded != null) {
324 if (DEBUG) Log.d(TAG, "using preloaded icon for " +
325 componentName.toShortString());
326 entry.icon = preloaded;
327 } else {
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700328 if (usePackageIcon) {
329 CacheEntry packageEntry = getEntryForPackage(
330 componentName.getPackageName(), user);
331 if (packageEntry != null && packageEntry.icon != null) {
332 if (DEBUG) Log.d(TAG, "using package default icon for " +
333 componentName.toShortString());
334 entry.icon = packageEntry.icon;
335 }
336 }
337 if (entry.icon == null) {
338 if (DEBUG) Log.d(TAG, "using default icon for " +
339 componentName.toShortString());
340 entry.icon = getDefaultIcon(user);
341 }
Winson Chungc3eecff2011-07-11 17:44:15 -0700342 }
343 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800344 }
345 return entry;
346 }
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400347
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700348 /**
349 * Gets an entry for the package, which can be used as a fallback entry for various components.
350 */
351 private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
352 ComponentName cn = getPackageComponent(packageName);
353 CacheKey cacheKey = new CacheKey(cn, user);
354 CacheEntry entry = mCache.get(cacheKey);
355 if (entry == null) {
356 entry = new CacheEntry();
357 mCache.put(cacheKey, entry);
358
359 try {
360 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
361 entry.title = info.loadLabel(mPackageManager);
362 entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext);
363 } catch (NameNotFoundException e) {
364 if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
365 }
366
367 if (entry.icon == null) {
368 entry.icon = getPreloadedIcon(cn, user);
369 }
370 }
371 return entry;
372 }
373
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400374 public HashMap<ComponentName,Bitmap> getAllIcons() {
375 synchronized (mCache) {
376 HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
Kenny Guyed131872014-04-30 03:02:21 +0100377 for (CacheKey ck : mCache.keySet()) {
378 final CacheEntry e = mCache.get(ck);
379 set.put(ck.componentName, e.icon);
Daniel Sandler4e1cd232011-05-12 00:06:32 -0400380 }
381 return set;
382 }
383 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500384
385 /**
386 * Pre-load an icon into the persistent cache.
387 *
388 * <P>Queries for a component that does not exist in the package manager
389 * will be answered by the persistent cache.
390 *
391 * @param context application context
392 * @param componentName the icon should be returned for this component
393 * @param icon the icon to be persisted
394 * @param dpi the native density of the icon
395 */
396 public static void preloadIcon(Context context, ComponentName componentName, Bitmap icon,
397 int dpi) {
398 // TODO rescale to the correct native DPI
399 try {
400 PackageManager packageManager = context.getPackageManager();
401 packageManager.getActivityIcon(componentName);
402 // component is present on the system already, do nothing
403 return;
404 } catch (PackageManager.NameNotFoundException e) {
405 // pass
406 }
407
408 final String key = componentName.flattenToString();
409 FileOutputStream resourceFile = null;
410 try {
411 resourceFile = context.openFileOutput(getResourceFilename(componentName),
412 Context.MODE_PRIVATE);
413 ByteArrayOutputStream os = new ByteArrayOutputStream();
414 if (icon.compress(android.graphics.Bitmap.CompressFormat.PNG, 75, os)) {
415 byte[] buffer = os.toByteArray();
416 resourceFile.write(buffer, 0, buffer.length);
417 } else {
418 Log.w(TAG, "failed to encode cache for " + key);
419 return;
420 }
421 } catch (FileNotFoundException e) {
422 Log.w(TAG, "failed to pre-load cache for " + key, e);
423 } catch (IOException e) {
424 Log.w(TAG, "failed to pre-load cache for " + key, e);
425 } finally {
426 if (resourceFile != null) {
427 try {
428 resourceFile.close();
429 } catch (IOException e) {
430 Log.d(TAG, "failed to save restored icon for: " + key, e);
431 }
432 }
433 }
434 }
435
436 /**
437 * Read a pre-loaded icon from the persistent icon cache.
438 *
439 * @param componentName the component that should own the icon
440 * @returns a bitmap if one is cached, or null.
441 */
Kenny Guyed131872014-04-30 03:02:21 +0100442 private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) {
Chris Wren6d0dde02014-02-10 12:16:54 -0500443 final String key = componentName.flattenToShortString();
444
Kenny Guyed131872014-04-30 03:02:21 +0100445 // We don't keep icons for other profiles in persistent cache.
446 if (!user.equals(UserHandleCompat.myUserHandle())) {
447 return null;
448 }
449
Chris Wren6d0dde02014-02-10 12:16:54 -0500450 if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key);
451 Bitmap icon = null;
452 FileInputStream resourceFile = null;
453 try {
454 resourceFile = mContext.openFileInput(getResourceFilename(componentName));
455 byte[] buffer = new byte[1024];
456 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
457 int bytesRead = 0;
458 while(bytesRead >= 0) {
459 bytes.write(buffer, 0, bytesRead);
460 bytesRead = resourceFile.read(buffer, 0, buffer.length);
461 }
462 if (DEBUG) Log.d(TAG, "read " + bytes.size());
463 icon = BitmapFactory.decodeByteArray(bytes.toByteArray(), 0, bytes.size());
464 if (icon == null) {
465 Log.w(TAG, "failed to decode pre-load icon for " + key);
466 }
467 } catch (FileNotFoundException e) {
468 if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key, e);
469 } catch (IOException e) {
470 Log.w(TAG, "failed to read pre-load icon for: " + key, e);
471 } finally {
472 if(resourceFile != null) {
473 try {
474 resourceFile.close();
475 } catch (IOException e) {
476 Log.d(TAG, "failed to manage pre-load icon file: " + key, e);
477 }
478 }
479 }
480
Chris Wren6d0dde02014-02-10 12:16:54 -0500481 return icon;
482 }
483
484 /**
485 * Remove a pre-loaded icon from the persistent icon cache.
486 *
487 * @param componentName the component that should own the icon
488 * @returns true on success
489 */
Kenny Guyed131872014-04-30 03:02:21 +0100490 public boolean deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) {
491 // We don't keep icons for other profiles in persistent cache.
492 if (!user.equals(UserHandleCompat.myUserHandle())) {
493 return false;
494 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500495 if (componentName == null) {
496 return false;
497 }
498 if (mCache.remove(componentName) != null) {
499 if (DEBUG) Log.d(TAG, "removed pre-loaded icon from the in-memory cache");
500 }
501 boolean success = mContext.deleteFile(getResourceFilename(componentName));
502 if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache");
503
504 return success;
505 }
506
507 private static String getResourceFilename(ComponentName component) {
508 String resourceName = component.flattenToShortString();
509 String filename = resourceName.replace(File.separatorChar, '_');
510 return RESOURCE_FILE_PREFIX + filename;
511 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -0700512
513 static ComponentName getPackageComponent(String packageName) {
514 return new ComponentName(packageName, EMPTY_CLASS_NAME);
515 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800516}