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