• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.launcher3.icons;
18 
19 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
20 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
21 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
22 import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
23 
24 import static java.util.stream.Collectors.groupingBy;
25 
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.LauncherActivityInfo;
31 import android.content.pm.LauncherApps;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageInstaller;
34 import android.content.pm.PackageManager;
35 import android.content.pm.PackageManager.NameNotFoundException;
36 import android.content.pm.ShortcutInfo;
37 import android.database.Cursor;
38 import android.database.sqlite.SQLiteException;
39 import android.graphics.drawable.Drawable;
40 import android.os.Process;
41 import android.os.Trace;
42 import android.os.UserHandle;
43 import android.text.TextUtils;
44 import android.util.Log;
45 import android.util.SparseArray;
46 
47 import androidx.annotation.NonNull;
48 import androidx.annotation.Nullable;
49 import androidx.core.util.Pair;
50 
51 import com.android.launcher3.InvariantDeviceProfile;
52 import com.android.launcher3.LauncherFiles;
53 import com.android.launcher3.Utilities;
54 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
55 import com.android.launcher3.icons.cache.BaseIconCache;
56 import com.android.launcher3.icons.cache.CachingLogic;
57 import com.android.launcher3.icons.cache.HandlerRunnable;
58 import com.android.launcher3.model.data.AppInfo;
59 import com.android.launcher3.model.data.IconRequestInfo;
60 import com.android.launcher3.model.data.ItemInfoWithIcon;
61 import com.android.launcher3.model.data.PackageItemInfo;
62 import com.android.launcher3.model.data.WorkspaceItemInfo;
63 import com.android.launcher3.pm.InstallSessionHelper;
64 import com.android.launcher3.pm.UserCache;
65 import com.android.launcher3.shortcuts.ShortcutKey;
66 import com.android.launcher3.util.InstantAppResolver;
67 import com.android.launcher3.util.PackageUserKey;
68 import com.android.launcher3.util.Preconditions;
69 import com.android.launcher3.widget.WidgetSections;
70 import com.android.launcher3.widget.WidgetSections.WidgetSection;
71 
72 import java.util.Collections;
73 import java.util.List;
74 import java.util.Map;
75 import java.util.Objects;
76 import java.util.function.Predicate;
77 import java.util.function.Supplier;
78 import java.util.stream.Stream;
79 
80 /**
81  * Cache of application icons.  Icons can be made from any thread.
82  */
83 public class IconCache extends BaseIconCache {
84 
85     // Shortcut extra which can point to a packageName and can be used to indicate an alternate
86     // badge info. Launcher only reads this if the shortcut comes from a system app.
87     public static final String EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE =
88             "extra_shortcut_badge_override_package";
89 
90     private static final String TAG = "Launcher.IconCache";
91 
92     private final Predicate<ItemInfoWithIcon> mIsUsingFallbackOrNonDefaultIconCheck = w ->
93             w.bitmap != null && (w.bitmap.isNullOrLowRes() || !isDefaultIcon(w.bitmap, w.user));
94 
95     private final CachingLogic<ComponentWithLabel> mComponentWithLabelCachingLogic;
96     private final CachingLogic<LauncherActivityInfo> mLauncherActivityInfoCachingLogic;
97     private final CachingLogic<ShortcutInfo> mShortcutCachingLogic;
98 
99     private final LauncherApps mLauncherApps;
100     private final UserCache mUserManager;
101     private final InstantAppResolver mInstantAppResolver;
102     private final IconProvider mIconProvider;
103 
104     private final SparseArray<BitmapInfo> mWidgetCategoryBitmapInfos;
105 
106     private int mPendingIconRequestCount = 0;
107 
IconCache(Context context, InvariantDeviceProfile idp)108     public IconCache(Context context, InvariantDeviceProfile idp) {
109         this(context, idp, LauncherFiles.APP_ICONS_DB, new IconProvider(context));
110     }
111 
IconCache(Context context, InvariantDeviceProfile idp, String dbFileName, IconProvider iconProvider)112     public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName,
113             IconProvider iconProvider) {
114         super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
115                 idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */);
116         mComponentWithLabelCachingLogic = new ComponentCachingLogic(context, false);
117         mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context);
118         mShortcutCachingLogic = new ShortcutCachingLogic();
119         mLauncherApps = mContext.getSystemService(LauncherApps.class);
120         mUserManager = UserCache.INSTANCE.get(mContext);
121         mInstantAppResolver = InstantAppResolver.newInstance(mContext);
122         mIconProvider = iconProvider;
123         mWidgetCategoryBitmapInfos = new SparseArray<>();
124     }
125 
126     @Override
getSerialNumberForUser(@onNull UserHandle user)127     protected long getSerialNumberForUser(@NonNull UserHandle user) {
128         return mUserManager.getSerialNumberForUser(user);
129     }
130 
131     @Override
isInstantApp(@onNull ApplicationInfo info)132     protected boolean isInstantApp(@NonNull ApplicationInfo info) {
133         return mInstantAppResolver.isInstantApp(info);
134     }
135 
136     @NonNull
137     @Override
getIconFactory()138     public BaseIconFactory getIconFactory() {
139         return LauncherIcons.obtain(mContext);
140     }
141 
142     /**
143      * Updates the entries related to the given package in memory and persistent DB.
144      */
updateIconsForPkg(@onNull final String packageName, @NonNull final UserHandle user)145     public synchronized void updateIconsForPkg(@NonNull final String packageName,
146             @NonNull final UserHandle user) {
147         removeIconsForPkg(packageName, user);
148         try {
149             PackageInfo info = mPackageManager.getPackageInfo(packageName,
150                     PackageManager.GET_UNINSTALLED_PACKAGES);
151             long userSerial = mUserManager.getSerialNumberForUser(user);
152             for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) {
153                 addIconToDBAndMemCache(app, mLauncherActivityInfoCachingLogic, info, userSerial,
154                         false /*replace existing*/);
155             }
156         } catch (NameNotFoundException e) {
157             Log.d(TAG, "Package not found", e);
158         }
159     }
160 
161     /**
162      * Closes the cache DB. This will clear any in-memory cache.
163      */
close()164     public void close() {
165         // This will clear all pending updates
166         getUpdateHandler();
167 
168         mIconDb.close();
169     }
170 
171     /**
172      * Fetches high-res icon for the provided ItemInfo and updates the caller when done.
173      *
174      * @return a request ID that can be used to cancel the request.
175      */
updateIconInBackground(final ItemInfoUpdateReceiver caller, final ItemInfoWithIcon info)176     public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller,
177             final ItemInfoWithIcon info) {
178         Preconditions.assertUIThread();
179         if (mPendingIconRequestCount <= 0) {
180             MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
181         }
182         mPendingIconRequestCount++;
183 
184         HandlerRunnable<ItemInfoWithIcon> request = new HandlerRunnable<>(mWorkerHandler,
185                 () -> {
186                     if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
187                         getTitleAndIcon(info, false);
188                     } else if (info instanceof PackageItemInfo) {
189                         getTitleAndIconForApp((PackageItemInfo) info, false);
190                     }
191                     return info;
192                 },
193                 MAIN_EXECUTOR,
194                 caller::reapplyItemInfo,
195                 this::onIconRequestEnd);
196         Utilities.postAsyncCallback(mWorkerHandler, request);
197         return request;
198     }
199 
onIconRequestEnd()200     private void onIconRequestEnd() {
201         mPendingIconRequestCount--;
202         if (mPendingIconRequestCount <= 0) {
203             MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
204         }
205     }
206 
207     /**
208      * Updates {@param application} only if a valid entry is found.
209      */
updateTitleAndIcon(AppInfo application)210     public synchronized void updateTitleAndIcon(AppInfo application) {
211         CacheEntry entry = cacheLocked(application.componentName,
212                 application.user, () -> null, mLauncherActivityInfoCachingLogic,
213                 false, application.usingLowResIcon());
214         if (entry.bitmap != null && !isDefaultIcon(entry.bitmap, application.user)) {
215             applyCacheEntry(entry, application);
216         }
217     }
218 
219     /**
220      * Fill in {@param info} with the icon and label for {@param activityInfo}
221      */
getTitleAndIcon(ItemInfoWithIcon info, LauncherActivityInfo activityInfo, boolean useLowResIcon)222     public synchronized void getTitleAndIcon(ItemInfoWithIcon info,
223             LauncherActivityInfo activityInfo, boolean useLowResIcon) {
224         // If we already have activity info, no need to use package icon
225         getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon);
226     }
227 
228     /**
229      * Fill in {@param info} with the icon for {@param si}
230      */
getShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si)231     public void getShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
232         getShortcutIcon(info, si, mIsUsingFallbackOrNonDefaultIconCheck);
233     }
234 
235     /**
236      * Fill in {@param info} with the icon and label for {@param si}. If the icon is not
237      * available, and fallback check returns true, it keeps the old icon.
238      */
getShortcutIcon(T info, ShortcutInfo si, @NonNull Predicate<T> fallbackIconCheck)239     public <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
240             @NonNull Predicate<T> fallbackIconCheck) {
241         BitmapInfo bitmapInfo = cacheLocked(ShortcutKey.fromInfo(si).componentName,
242                 si.getUserHandle(), () -> si, mShortcutCachingLogic, false, false).bitmap;
243         if (bitmapInfo.isNullOrLowRes()) {
244             bitmapInfo = getDefaultIcon(si.getUserHandle());
245         }
246 
247         if (isDefaultIcon(bitmapInfo, si.getUserHandle()) && fallbackIconCheck.test(info)) {
248             return;
249         }
250         info.bitmap = bitmapInfo.withBadgeInfo(getShortcutInfoBadge(si));
251     }
252 
253     /**
254      * Returns the badging info for the shortcut
255      */
getShortcutInfoBadge(ShortcutInfo shortcutInfo)256     public BitmapInfo getShortcutInfoBadge(ShortcutInfo shortcutInfo) {
257         ComponentName cn = shortcutInfo.getActivity();
258         if (cn != null) {
259             // Get the app info for the source activity.
260             AppInfo appInfo = new AppInfo();
261             appInfo.user = shortcutInfo.getUserHandle();
262             appInfo.componentName = cn;
263             appInfo.intent = new Intent(Intent.ACTION_MAIN)
264                     .addCategory(Intent.CATEGORY_LAUNCHER)
265                     .setComponent(cn);
266             getTitleAndIcon(appInfo, false);
267             return appInfo.bitmap;
268         } else {
269             String pkg = shortcutInfo.getPackage();
270             String override = shortcutInfo.getExtras() == null ? null
271                     : shortcutInfo.getExtras().getString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE);
272             if (!TextUtils.isEmpty(override)
273                     && InstallSessionHelper.INSTANCE.get(mContext)
274                             .isTrustedPackage(pkg, shortcutInfo.getUserHandle())) {
275                 pkg = override;
276             }
277             PackageItemInfo pkgInfo = new PackageItemInfo(pkg, shortcutInfo.getUserHandle());
278             getTitleAndIconForApp(pkgInfo, false);
279             return pkgInfo.bitmap;
280         }
281     }
282 
283     /**
284      * Fill in {@param info} with the icon and label. If the
285      * corresponding activity is not found, it reverts to the package icon.
286      */
getTitleAndIcon(ItemInfoWithIcon info, boolean useLowResIcon)287     public synchronized void getTitleAndIcon(ItemInfoWithIcon info, boolean useLowResIcon) {
288         // null info means not installed, but if we have a component from the intent then
289         // we should still look in the cache for restored app icons.
290         if (info.getTargetComponent() == null) {
291             info.bitmap = getDefaultIcon(info.user);
292             info.title = "";
293             info.contentDescription = "";
294         } else {
295             Intent intent = info.getIntent();
296             getTitleAndIcon(info, () -> mLauncherApps.resolveActivity(intent, info.user),
297                     true, useLowResIcon);
298         }
299     }
300 
getTitleNoCache(ComponentWithLabel info)301     public synchronized String getTitleNoCache(ComponentWithLabel info) {
302         CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), () -> info,
303                 mComponentWithLabelCachingLogic, false /* usePackageIcon */,
304                 true /* useLowResIcon */);
305         return Utilities.trim(entry.title);
306     }
307 
308     /**
309      * Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info}
310      */
getTitleAndIcon( @onNull ItemInfoWithIcon infoInOut, @NonNull Supplier<LauncherActivityInfo> activityInfoProvider, boolean usePkgIcon, boolean useLowResIcon)311     public synchronized void getTitleAndIcon(
312             @NonNull ItemInfoWithIcon infoInOut,
313             @NonNull Supplier<LauncherActivityInfo> activityInfoProvider,
314             boolean usePkgIcon, boolean useLowResIcon) {
315         CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
316                 activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon,
317                 useLowResIcon);
318         applyCacheEntry(entry, infoInOut);
319     }
320 
321     /**
322      * Creates an sql cursor for a query of a set of ItemInfoWithIcon icons and titles.
323      *
324      * @param iconRequestInfos List of IconRequestInfos representing titles and icons to query.
325      * @param user UserHandle all the given iconRequestInfos share
326      * @param useLowResIcons whether we should exclude the icon column from the sql results.
327      */
createBulkQueryCursor( List<IconRequestInfo<T>> iconRequestInfos, UserHandle user, boolean useLowResIcons)328     private <T extends ItemInfoWithIcon> Cursor createBulkQueryCursor(
329             List<IconRequestInfo<T>> iconRequestInfos, UserHandle user, boolean useLowResIcons)
330             throws SQLiteException {
331         String[] queryParams = Stream.concat(
332                 iconRequestInfos.stream()
333                         .map(r -> r.itemInfo.getTargetComponent())
334                         .filter(Objects::nonNull)
335                         .distinct()
336                         .map(ComponentName::flattenToString),
337                 Stream.of(Long.toString(getSerialNumberForUser(user)))).toArray(String[]::new);
338         String componentNameQuery = TextUtils.join(
339                 ",", Collections.nCopies(queryParams.length - 1, "?"));
340 
341         return mIconDb.query(
342                 useLowResIcons ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES,
343                 IconDB.COLUMN_COMPONENT
344                         + " IN ( " + componentNameQuery + " )"
345                         + " AND " + IconDB.COLUMN_USER + " = ?",
346                 queryParams);
347     }
348 
349     /**
350      * Load and fill icons requested in iconRequestInfos using a single bulk sql query.
351      */
getTitlesAndIconsInBulk( List<IconRequestInfo<T>> iconRequestInfos)352     public synchronized <T extends ItemInfoWithIcon> void getTitlesAndIconsInBulk(
353             List<IconRequestInfo<T>> iconRequestInfos) {
354         Map<Pair<UserHandle, Boolean>, List<IconRequestInfo<T>>> iconLoadSubsectionsMap =
355                 iconRequestInfos.stream()
356                         .filter(iconRequest -> {
357                             if (iconRequest.itemInfo.getTargetComponent() == null) {
358                                 Log.i(TAG,
359                                         "Skipping Item info with null component name: "
360                                                 + iconRequest.itemInfo);
361                                 iconRequest.itemInfo.bitmap = getDefaultIcon(
362                                         iconRequest.itemInfo.user);
363                                 return false;
364                             }
365                             return true;
366                         })
367                         .collect(groupingBy(iconRequest ->
368                                 Pair.create(iconRequest.itemInfo.user, iconRequest.useLowResIcon)));
369 
370         Trace.beginSection("loadIconsInBulk");
371         iconLoadSubsectionsMap.forEach((sectionKey, filteredList) -> {
372             Map<ComponentName, List<IconRequestInfo<T>>> duplicateIconRequestsMap =
373                     filteredList.stream()
374                             .filter(iconRequest -> {
375                                 // Filter out icons that should not share the same bitmap and title
376                                 if (iconRequest.itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
377                                     Log.e(TAG,
378                                             "Skipping Item info for deep shortcut: "
379                                                     + iconRequest.itemInfo,
380                                             new IllegalStateException());
381                                     return false;
382                                 }
383                                 return true;
384                             })
385                             .collect(groupingBy(iconRequest ->
386                                     iconRequest.itemInfo.getTargetComponent()));
387 
388             Trace.beginSection("loadIconSubsectionInBulk");
389             loadIconSubsection(sectionKey, filteredList, duplicateIconRequestsMap);
390             Trace.endSection();
391         });
392         Trace.endSection();
393     }
394 
loadIconSubsection( Pair<UserHandle, Boolean> sectionKey, List<IconRequestInfo<T>> filteredList, Map<ComponentName, List<IconRequestInfo<T>>> duplicateIconRequestsMap)395     private <T extends ItemInfoWithIcon> void loadIconSubsection(
396             Pair<UserHandle, Boolean> sectionKey,
397             List<IconRequestInfo<T>> filteredList,
398             Map<ComponentName, List<IconRequestInfo<T>>> duplicateIconRequestsMap) {
399         Trace.beginSection("loadIconSubsectionWithDatabase");
400         try (Cursor c = createBulkQueryCursor(
401                 filteredList,
402                 /* user = */ sectionKey.first,
403                 /* useLowResIcons = */ sectionKey.second)) {
404             // Database title and icon loading
405             int componentNameColumnIndex = c.getColumnIndexOrThrow(IconDB.COLUMN_COMPONENT);
406             while (c.moveToNext()) {
407                 ComponentName cn = ComponentName.unflattenFromString(
408                         c.getString(componentNameColumnIndex));
409                 List<IconRequestInfo<T>> duplicateIconRequests =
410                         duplicateIconRequestsMap.get(cn);
411 
412                 if (cn != null) {
413                     CacheEntry entry = cacheLocked(
414                             cn,
415                             /* user = */ sectionKey.first,
416                             () -> duplicateIconRequests.get(0).launcherActivityInfo,
417                             mLauncherActivityInfoCachingLogic,
418                             c,
419                             /* usePackageIcon= */ false,
420                             /* useLowResIcons = */ sectionKey.second);
421 
422                     for (IconRequestInfo<T> iconRequest : duplicateIconRequests) {
423                         applyCacheEntry(entry, iconRequest.itemInfo);
424                     }
425                 }
426             }
427         } catch (SQLiteException e) {
428             Log.d(TAG, "Error reading icon cache", e);
429         } finally {
430             Trace.endSection();
431         }
432 
433         Trace.beginSection("loadIconSubsectionWithFallback");
434         // Fallback title and icon loading
435         for (ComponentName cn : duplicateIconRequestsMap.keySet()) {
436             IconRequestInfo<T> iconRequestInfo = duplicateIconRequestsMap.get(cn).get(0);
437             ItemInfoWithIcon itemInfo = iconRequestInfo.itemInfo;
438             BitmapInfo icon = itemInfo.bitmap;
439             boolean loadFallbackTitle = TextUtils.isEmpty(itemInfo.title);
440             boolean loadFallbackIcon = icon == null
441                     || isDefaultIcon(icon, itemInfo.user)
442                     || icon == BitmapInfo.LOW_RES_INFO;
443 
444             if (loadFallbackTitle || loadFallbackIcon) {
445                 Log.i(TAG,
446                         "Database bulk icon loading failed, using fallback bulk icon loading "
447                                 + "for: " + cn);
448                 CacheEntry entry = new CacheEntry();
449                 LauncherActivityInfo lai = iconRequestInfo.launcherActivityInfo;
450 
451                 // Fill fields that are not updated below so they are not subsequently
452                 // deleted.
453                 entry.title = itemInfo.title;
454                 if (icon != null) {
455                     entry.bitmap = icon;
456                 }
457                 entry.contentDescription = itemInfo.contentDescription;
458 
459                 if (loadFallbackIcon) {
460                     loadFallbackIcon(
461                             lai,
462                             entry,
463                             mLauncherActivityInfoCachingLogic,
464                             /* usePackageIcon= */ false,
465                             /* usePackageTitle= */ loadFallbackTitle,
466                             cn,
467                             sectionKey.first);
468                 }
469                 if (loadFallbackTitle && TextUtils.isEmpty(entry.title) && lai != null) {
470                     loadFallbackTitle(
471                             lai,
472                             entry,
473                             mLauncherActivityInfoCachingLogic,
474                             sectionKey.first);
475                 }
476 
477                 for (IconRequestInfo<T> iconRequest : duplicateIconRequestsMap.get(cn)) {
478                     applyCacheEntry(entry, iconRequest.itemInfo);
479                 }
480             }
481         }
482         Trace.endSection();
483     }
484 
485     /**
486      * Fill in {@param infoInOut} with the corresponding icon and label.
487      */
getTitleAndIconForApp( @onNull final PackageItemInfo infoInOut, final boolean useLowResIcon)488     public synchronized void getTitleAndIconForApp(
489             @NonNull final PackageItemInfo infoInOut, final boolean useLowResIcon) {
490         CacheEntry entry = getEntryForPackageLocked(
491                 infoInOut.packageName, infoInOut.user, useLowResIcon);
492         applyCacheEntry(entry, infoInOut);
493         if (infoInOut.widgetCategory == NO_CATEGORY) {
494             return;
495         }
496 
497         WidgetSection widgetSection = WidgetSections.getWidgetSections(mContext)
498                 .get(infoInOut.widgetCategory);
499         infoInOut.title = mContext.getString(widgetSection.mSectionTitle);
500         infoInOut.contentDescription = getUserBadgedLabel(infoInOut.title, infoInOut.user);
501         final BitmapInfo cachedBitmap = mWidgetCategoryBitmapInfos.get(infoInOut.widgetCategory);
502         if (cachedBitmap != null) {
503             infoInOut.bitmap = getBadgedIcon(cachedBitmap, infoInOut.user);
504             return;
505         }
506 
507         try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
508             final BitmapInfo tempBitmap = li.createBadgedIconBitmap(
509                     mContext.getDrawable(widgetSection.mSectionDrawable),
510                     new BaseIconFactory.IconOptions().setShrinkNonAdaptiveIcons(false));
511             mWidgetCategoryBitmapInfos.put(infoInOut.widgetCategory, tempBitmap);
512             infoInOut.bitmap = getBadgedIcon(tempBitmap, infoInOut.user);
513         } catch (Exception e) {
514             Log.e(TAG, "Error initializing bitmap for icons with widget category", e);
515         }
516 
517     }
518 
getBadgedIcon(@ullable final BitmapInfo bitmap, @NonNull final UserHandle user)519     private synchronized BitmapInfo getBadgedIcon(@Nullable final BitmapInfo bitmap,
520             @NonNull final UserHandle user) {
521         if (bitmap == null) {
522             return getDefaultIcon(user);
523         }
524         return bitmap.withFlags(getUserFlagOpLocked(user));
525     }
526 
applyCacheEntry(@onNull final CacheEntry entry, @NonNull final ItemInfoWithIcon info)527     protected void applyCacheEntry(@NonNull final CacheEntry entry,
528             @NonNull final ItemInfoWithIcon info) {
529         info.title = Utilities.trim(entry.title);
530         info.contentDescription = entry.contentDescription;
531         info.bitmap = entry.bitmap;
532         if (entry.bitmap == null) {
533             // TODO: entry.bitmap can never be null, so this should not happen at all.
534             Log.wtf(TAG, "Cannot find bitmap from the cache, default icon was loaded.");
535             info.bitmap = getDefaultIcon(info.user);
536         }
537     }
538 
getFullResIcon(LauncherActivityInfo info)539     public Drawable getFullResIcon(LauncherActivityInfo info) {
540         return mIconProvider.getIcon(info, mIconDpi);
541     }
542 
updateSessionCache(PackageUserKey key, PackageInstaller.SessionInfo info)543     public void updateSessionCache(PackageUserKey key, PackageInstaller.SessionInfo info) {
544         cachePackageInstallInfo(key.mPackageName, key.mUser, info.getAppIcon(),
545                 info.getAppLabel());
546     }
547 
548     @Override
549     @NonNull
getIconSystemState(String packageName)550     protected String getIconSystemState(String packageName) {
551         return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
552     }
553 
554     /**
555      * Interface for receiving itemInfo with high-res icon.
556      */
557     public interface ItemInfoUpdateReceiver {
558 
reapplyItemInfo(ItemInfoWithIcon info)559         void reapplyItemInfo(ItemInfoWithIcon info);
560     }
561 }
562