Loading iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +13 −1 Original line number Diff line number Diff line Loading @@ -242,7 +242,8 @@ public class BaseIconFactory implements AutoCloseable { if (adaptiveIcon instanceof Extender extender) { info = extender.getExtendedInfo(bitmap, color, this, scale[0]); } else if (IconProvider.ATLEAST_T && mThemeController != null && adaptiveIcon != null) { info.setThemedBitmap(mThemeController.createThemedBitmap(adaptiveIcon, info, this)); info.setThemedBitmap(mThemeController.createThemedBitmap( adaptiveIcon, info, this, options == null ? null : options.mSourceHint)); } info = info.withFlags(getBitmapFlagOp(options)); return info; Loading Loading @@ -526,6 +527,8 @@ public class BaseIconFactory implements AutoCloseable { @Nullable Integer mExtractedColor; @Nullable SourceHint mSourceHint; /** * User for this icon, in case of badging Loading Loading @@ -580,6 +583,15 @@ public class BaseIconFactory implements AutoCloseable { mGenerationMode = generationMode; return this; } /** * User for this icon, in case of badging */ @NonNull public IconOptions setSourceHint(@Nullable SourceHint sourceHint) { mSourceHint = sourceHint; return this; } } /** Loading iconloaderlib/src/com/android/launcher3/icons/ThemedBitmap.kt +15 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.launcher3.icons import android.content.Context import android.graphics.drawable.AdaptiveIconDrawable import com.android.launcher3.icons.cache.CachingLogic import com.android.launcher3.util.ComponentKey /** Represents a themed version of a BitmapInfo */ interface ThemedBitmap { Loading @@ -34,9 +36,15 @@ interface IconThemeController { icon: AdaptiveIconDrawable, info: BitmapInfo, factory: BaseIconFactory, sourceHint: SourceHint? = null, ): ThemedBitmap? fun decode(data: ByteArray, info: BitmapInfo, factory: BaseIconFactory): ThemedBitmap? fun decode( data: ByteArray, info: BitmapInfo, factory: BaseIconFactory, sourceHint: SourceHint, ): ThemedBitmap? fun createThemedAdaptiveIcon( context: Context, Loading @@ -44,3 +52,9 @@ interface IconThemeController { info: BitmapInfo?, ): AdaptiveIconDrawable? } data class SourceHint( val key: ComponentKey, val logic: CachingLogic<*>, val freshnessId: String? = null, ) iconloaderlib/src/com/android/launcher3/icons/cache/AppInfoCachingLogic.kt 0 → 100644 +73 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.icons.cache import android.content.ComponentName import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.os.UserHandle import com.android.launcher3.icons.BaseIconFactory.IconOptions import com.android.launcher3.icons.BitmapInfo import com.android.launcher3.icons.IconProvider import com.android.launcher3.icons.cache.BaseIconCache.Companion.EMPTY_CLASS_NAME /** Caching logic for ApplicationInfo */ class AppInfoCachingLogic( private val pm: PackageManager, private val instantAppResolver: (ApplicationInfo) -> Boolean, private val errorLogger: (String, Exception?) -> Unit = { _, _ -> }, ) : CachingLogic<ApplicationInfo> { override fun getComponent(info: ApplicationInfo) = ComponentName(info.packageName, info.packageName + EMPTY_CLASS_NAME) override fun getUser(info: ApplicationInfo) = UserHandle.getUserHandleForUid(info.uid) override fun getLabel(info: ApplicationInfo) = info.loadLabel(pm) override fun getApplicationInfo(info: ApplicationInfo) = info override fun loadIcon( context: Context, cache: BaseIconCache, info: ApplicationInfo, ): BitmapInfo { // Load the full res icon for the application, but if useLowResIcon is set, then // only keep the low resolution icon instead of the larger full-sized icon val appIcon = cache.iconProvider.getIcon(info) if (context.packageManager.isDefaultApplicationIcon(appIcon)) { errorLogger.invoke( String.format("Default icon returned for %s", info.packageName), null, ) } return cache.iconFactory.use { li -> li.createBadgedIconBitmap( appIcon, IconOptions() .setUser(getUser(info)) .setInstantApp(instantAppResolver.invoke(info)) .setSourceHint(getSourceHint(info, cache)), ) } } override fun getFreshnessIdentifier(item: ApplicationInfo, iconProvider: IconProvider) = iconProvider.getStateForApp(item) } iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.kt +25 −25 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import com.android.launcher3.icons.BaseIconFactory.IconOptions import com.android.launcher3.icons.BitmapInfo import com.android.launcher3.icons.GraphicsUtils import com.android.launcher3.icons.IconProvider import com.android.launcher3.icons.SourceHint import com.android.launcher3.icons.cache.CacheLookupFlag.Companion.DEFAULT_LOOKUP_FLAG import com.android.launcher3.util.ComponentKey import com.android.launcher3.util.FlagOp Loading Loading @@ -95,6 +96,13 @@ constructor( private val userFlagOpMap = SparseArray<FlagOp>() private val userFormatString = SparseArray<String?>() private val appInfoCachingLogic = AppInfoCachingLogic( pm = context.packageManager, instantAppResolver = this::isInstantApp, errorLogger = this::logPersistently, ) init { updateSystemState() } Loading Loading @@ -273,8 +281,8 @@ constructor( if (addToMemCache) cache[cacheKey] = entry // Check the DB first. val cacheEntryUpdated = if (cursor == null) getEntryFromDBLocked(cacheKey, entry, lookupFlags) else updateTitleAndIconLocked(cacheKey, entry, cursor, lookupFlags) if (cursor == null) getEntryFromDBLocked(cacheKey, entry, lookupFlags, cachingLogic) else updateTitleAndIconLocked(cacheKey, entry, cursor, lookupFlags, cachingLogic) val obj: T? by lazy { infoProvider.get() } if (!cacheEntryUpdated) { Loading Loading @@ -409,7 +417,7 @@ constructor( var entryUpdated = true // Check the DB first. if (!getEntryFromDBLocked(cacheKey, entry, lookupFlags)) { if (!getEntryFromDBLocked(cacheKey, entry, lookupFlags, appInfoCachingLogic)) { try { val appInfo = context Loading @@ -430,33 +438,18 @@ constructor( // Load the full res icon for the application, but if useLowResIcon is set, then // only keep the low resolution icon instead of the larger full-sized icon val appIcon = iconProvider.getIcon(appInfo) if (packageManager.isDefaultApplicationIcon(appIcon)) { logPersistently( String.format("Default icon returned for %s", appInfo.packageName), null, ) } val iconInfo = iconFactory.use { li -> li.createBadgedIconBitmap( appIcon, IconOptions().setUser(user).setInstantApp(isInstantApp(appInfo)), ) } entry.title = appInfo.loadLabel(packageManager) entry.contentDescription = getUserBadgedLabel(entry.title, user) val iconInfo = appInfoCachingLogic.loadIcon(context, this, appInfo) entry.bitmap = if (lookupFlags.useLowRes()) BitmapInfo.of(BitmapInfo.LOW_RES_ICON, iconInfo.color) else iconInfo loadFallbackTitle(appInfo, entry, appInfoCachingLogic, user) // Add the icon in the DB here, since these do not get written during // package updates. val freshnessId = iconProvider.getStateForApp(appInfo) if (freshnessId != null) { appInfoCachingLogic.getFreshnessIdentifier(appInfo, iconProvider)?.let { freshnessId -> addOrUpdateCacheDbEntry( iconInfo, entry.title, Loading @@ -483,6 +476,7 @@ constructor( cacheKey: ComponentKey, entry: CacheEntry, lookupFlags: CacheLookupFlag, cachingLogic: CachingLogic<*>, ): Boolean { var c: Cursor? = null Trace.beginSection("loadIconIndividually") Loading @@ -497,7 +491,7 @@ constructor( ), ) if (c.moveToNext()) { return updateTitleAndIconLocked(cacheKey, entry, c, lookupFlags) return updateTitleAndIconLocked(cacheKey, entry, c, lookupFlags, cachingLogic) } } catch (e: SQLiteException) { Log.d(TAG, "Error reading icon cache", e) Loading @@ -513,6 +507,7 @@ constructor( entry: CacheEntry, c: Cursor, lookupFlags: CacheLookupFlag, logic: CachingLogic<*>, ): Boolean { // Set the alpha to be 255, so that we never have a wrong color entry.bitmap = Loading Loading @@ -552,7 +547,12 @@ constructor( val monoIconData = c.getBlob(INDEX_MONO_ICON) if (themeController != null && monoIconData != null) { entry.bitmap.themedBitmap = themeController.decode(monoIconData, entry.bitmap, factory) themeController.decode( data = monoIconData, info = entry.bitmap, factory = factory, sourceHint = SourceHint(cacheKey, logic), ) } } } Loading iconloaderlib/src/com/android/launcher3/icons/cache/CachedObjectCachingLogic.kt +4 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,10 @@ object CachedObjectCachingLogic : CachingLogic<CachedObject> { override fun loadIcon(context: Context, cache: BaseIconCache, info: CachedObject): BitmapInfo { val d = info.getFullResIcon(cache) ?: return BitmapInfo.LOW_RES_INFO cache.iconFactory.use { li -> return li.createBadgedIconBitmap(d, IconOptions().setUser(info.user)) return li.createBadgedIconBitmap( d, IconOptions().setUser(info.user).setSourceHint(getSourceHint(info, cache)), ) } } Loading Loading
iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +13 −1 Original line number Diff line number Diff line Loading @@ -242,7 +242,8 @@ public class BaseIconFactory implements AutoCloseable { if (adaptiveIcon instanceof Extender extender) { info = extender.getExtendedInfo(bitmap, color, this, scale[0]); } else if (IconProvider.ATLEAST_T && mThemeController != null && adaptiveIcon != null) { info.setThemedBitmap(mThemeController.createThemedBitmap(adaptiveIcon, info, this)); info.setThemedBitmap(mThemeController.createThemedBitmap( adaptiveIcon, info, this, options == null ? null : options.mSourceHint)); } info = info.withFlags(getBitmapFlagOp(options)); return info; Loading Loading @@ -526,6 +527,8 @@ public class BaseIconFactory implements AutoCloseable { @Nullable Integer mExtractedColor; @Nullable SourceHint mSourceHint; /** * User for this icon, in case of badging Loading Loading @@ -580,6 +583,15 @@ public class BaseIconFactory implements AutoCloseable { mGenerationMode = generationMode; return this; } /** * User for this icon, in case of badging */ @NonNull public IconOptions setSourceHint(@Nullable SourceHint sourceHint) { mSourceHint = sourceHint; return this; } } /** Loading
iconloaderlib/src/com/android/launcher3/icons/ThemedBitmap.kt +15 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.launcher3.icons import android.content.Context import android.graphics.drawable.AdaptiveIconDrawable import com.android.launcher3.icons.cache.CachingLogic import com.android.launcher3.util.ComponentKey /** Represents a themed version of a BitmapInfo */ interface ThemedBitmap { Loading @@ -34,9 +36,15 @@ interface IconThemeController { icon: AdaptiveIconDrawable, info: BitmapInfo, factory: BaseIconFactory, sourceHint: SourceHint? = null, ): ThemedBitmap? fun decode(data: ByteArray, info: BitmapInfo, factory: BaseIconFactory): ThemedBitmap? fun decode( data: ByteArray, info: BitmapInfo, factory: BaseIconFactory, sourceHint: SourceHint, ): ThemedBitmap? fun createThemedAdaptiveIcon( context: Context, Loading @@ -44,3 +52,9 @@ interface IconThemeController { info: BitmapInfo?, ): AdaptiveIconDrawable? } data class SourceHint( val key: ComponentKey, val logic: CachingLogic<*>, val freshnessId: String? = null, )
iconloaderlib/src/com/android/launcher3/icons/cache/AppInfoCachingLogic.kt 0 → 100644 +73 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.icons.cache import android.content.ComponentName import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.os.UserHandle import com.android.launcher3.icons.BaseIconFactory.IconOptions import com.android.launcher3.icons.BitmapInfo import com.android.launcher3.icons.IconProvider import com.android.launcher3.icons.cache.BaseIconCache.Companion.EMPTY_CLASS_NAME /** Caching logic for ApplicationInfo */ class AppInfoCachingLogic( private val pm: PackageManager, private val instantAppResolver: (ApplicationInfo) -> Boolean, private val errorLogger: (String, Exception?) -> Unit = { _, _ -> }, ) : CachingLogic<ApplicationInfo> { override fun getComponent(info: ApplicationInfo) = ComponentName(info.packageName, info.packageName + EMPTY_CLASS_NAME) override fun getUser(info: ApplicationInfo) = UserHandle.getUserHandleForUid(info.uid) override fun getLabel(info: ApplicationInfo) = info.loadLabel(pm) override fun getApplicationInfo(info: ApplicationInfo) = info override fun loadIcon( context: Context, cache: BaseIconCache, info: ApplicationInfo, ): BitmapInfo { // Load the full res icon for the application, but if useLowResIcon is set, then // only keep the low resolution icon instead of the larger full-sized icon val appIcon = cache.iconProvider.getIcon(info) if (context.packageManager.isDefaultApplicationIcon(appIcon)) { errorLogger.invoke( String.format("Default icon returned for %s", info.packageName), null, ) } return cache.iconFactory.use { li -> li.createBadgedIconBitmap( appIcon, IconOptions() .setUser(getUser(info)) .setInstantApp(instantAppResolver.invoke(info)) .setSourceHint(getSourceHint(info, cache)), ) } } override fun getFreshnessIdentifier(item: ApplicationInfo, iconProvider: IconProvider) = iconProvider.getStateForApp(item) }
iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.kt +25 −25 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import com.android.launcher3.icons.BaseIconFactory.IconOptions import com.android.launcher3.icons.BitmapInfo import com.android.launcher3.icons.GraphicsUtils import com.android.launcher3.icons.IconProvider import com.android.launcher3.icons.SourceHint import com.android.launcher3.icons.cache.CacheLookupFlag.Companion.DEFAULT_LOOKUP_FLAG import com.android.launcher3.util.ComponentKey import com.android.launcher3.util.FlagOp Loading Loading @@ -95,6 +96,13 @@ constructor( private val userFlagOpMap = SparseArray<FlagOp>() private val userFormatString = SparseArray<String?>() private val appInfoCachingLogic = AppInfoCachingLogic( pm = context.packageManager, instantAppResolver = this::isInstantApp, errorLogger = this::logPersistently, ) init { updateSystemState() } Loading Loading @@ -273,8 +281,8 @@ constructor( if (addToMemCache) cache[cacheKey] = entry // Check the DB first. val cacheEntryUpdated = if (cursor == null) getEntryFromDBLocked(cacheKey, entry, lookupFlags) else updateTitleAndIconLocked(cacheKey, entry, cursor, lookupFlags) if (cursor == null) getEntryFromDBLocked(cacheKey, entry, lookupFlags, cachingLogic) else updateTitleAndIconLocked(cacheKey, entry, cursor, lookupFlags, cachingLogic) val obj: T? by lazy { infoProvider.get() } if (!cacheEntryUpdated) { Loading Loading @@ -409,7 +417,7 @@ constructor( var entryUpdated = true // Check the DB first. if (!getEntryFromDBLocked(cacheKey, entry, lookupFlags)) { if (!getEntryFromDBLocked(cacheKey, entry, lookupFlags, appInfoCachingLogic)) { try { val appInfo = context Loading @@ -430,33 +438,18 @@ constructor( // Load the full res icon for the application, but if useLowResIcon is set, then // only keep the low resolution icon instead of the larger full-sized icon val appIcon = iconProvider.getIcon(appInfo) if (packageManager.isDefaultApplicationIcon(appIcon)) { logPersistently( String.format("Default icon returned for %s", appInfo.packageName), null, ) } val iconInfo = iconFactory.use { li -> li.createBadgedIconBitmap( appIcon, IconOptions().setUser(user).setInstantApp(isInstantApp(appInfo)), ) } entry.title = appInfo.loadLabel(packageManager) entry.contentDescription = getUserBadgedLabel(entry.title, user) val iconInfo = appInfoCachingLogic.loadIcon(context, this, appInfo) entry.bitmap = if (lookupFlags.useLowRes()) BitmapInfo.of(BitmapInfo.LOW_RES_ICON, iconInfo.color) else iconInfo loadFallbackTitle(appInfo, entry, appInfoCachingLogic, user) // Add the icon in the DB here, since these do not get written during // package updates. val freshnessId = iconProvider.getStateForApp(appInfo) if (freshnessId != null) { appInfoCachingLogic.getFreshnessIdentifier(appInfo, iconProvider)?.let { freshnessId -> addOrUpdateCacheDbEntry( iconInfo, entry.title, Loading @@ -483,6 +476,7 @@ constructor( cacheKey: ComponentKey, entry: CacheEntry, lookupFlags: CacheLookupFlag, cachingLogic: CachingLogic<*>, ): Boolean { var c: Cursor? = null Trace.beginSection("loadIconIndividually") Loading @@ -497,7 +491,7 @@ constructor( ), ) if (c.moveToNext()) { return updateTitleAndIconLocked(cacheKey, entry, c, lookupFlags) return updateTitleAndIconLocked(cacheKey, entry, c, lookupFlags, cachingLogic) } } catch (e: SQLiteException) { Log.d(TAG, "Error reading icon cache", e) Loading @@ -513,6 +507,7 @@ constructor( entry: CacheEntry, c: Cursor, lookupFlags: CacheLookupFlag, logic: CachingLogic<*>, ): Boolean { // Set the alpha to be 255, so that we never have a wrong color entry.bitmap = Loading Loading @@ -552,7 +547,12 @@ constructor( val monoIconData = c.getBlob(INDEX_MONO_ICON) if (themeController != null && monoIconData != null) { entry.bitmap.themedBitmap = themeController.decode(monoIconData, entry.bitmap, factory) themeController.decode( data = monoIconData, info = entry.bitmap, factory = factory, sourceHint = SourceHint(cacheKey, logic), ) } } } Loading
iconloaderlib/src/com/android/launcher3/icons/cache/CachedObjectCachingLogic.kt +4 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,10 @@ object CachedObjectCachingLogic : CachingLogic<CachedObject> { override fun loadIcon(context: Context, cache: BaseIconCache, info: CachedObject): BitmapInfo { val d = info.getFullResIcon(cache) ?: return BitmapInfo.LOW_RES_INFO cache.iconFactory.use { li -> return li.createBadgedIconBitmap(d, IconOptions().setUser(info.user)) return li.createBadgedIconBitmap( d, IconOptions().setUser(info.user).setSourceHint(getSourceHint(info, cache)), ) } } Loading