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