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