Optimize icon loading

This commit is contained in:
MM20 2023-07-20 19:40:00 +02:00
parent 6ea9751535
commit 1241067357
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
5 changed files with 64 additions and 64 deletions

View File

@ -90,26 +90,26 @@ class IconPackManager(
packageName: String, packageName: String,
activityName: String?, activityName: String?,
allowThemed: Boolean = true allowThemed: Boolean = true
): LauncherIcon? { ): LauncherIcon? = withContext(Dispatchers.IO) {
val res = try { val res = try {
context.packageManager.getResourcesForApplication(iconPack) context.packageManager.getResourcesForApplication(iconPack)
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
Log.e("MM20", "Icon pack package $iconPack not found!") Log.e("MM20", "Icon pack package $iconPack not found!")
return null return@withContext null
} }
val activity = activityName?.let { ComponentName(packageName, it) }?.shortClassName val activity = activityName?.let { ComponentName(packageName, it) }?.shortClassName
val iconDao = appDatabase.iconDao() val iconDao = appDatabase.iconDao()
val icon = iconDao.getIcon(packageName, activity, iconPack)?.let { IconPackAppIcon(it) } val icon = iconDao.getIcon(packageName, activity, iconPack)?.let { IconPackAppIcon(it) }
?: return null ?: return@withContext null
if (icon is CalendarIcon) { if (icon is CalendarIcon) {
return getIconPackCalendarIcon(icon, res, allowThemed) return@withContext getIconPackCalendarIcon(icon, res, allowThemed)
} else if (icon is AppIcon) { } else if (icon is AppIcon) {
return getIconPackStaticIcon(icon, res, allowThemed) return@withContext getIconPackStaticIcon(icon, res, allowThemed)
} else if (icon is ClockIcon) { } else if (icon is ClockIcon) {
return getIconPackClockIcon(icon, res, allowThemed) return@withContext getIconPackClockIcon(icon, res, allowThemed)
} }
return null return@withContext null
} }
suspend fun generateIcon( suspend fun generateIcon(
@ -117,14 +117,14 @@ class IconPackManager(
iconPack: String, iconPack: String,
baseIcon: Drawable, baseIcon: Drawable,
size: Int size: Int
): LauncherIcon? { ): LauncherIcon? = withContext(Dispatchers.IO) {
val back = getIconBack(iconPack) val back = getIconBack(iconPack)
val upon = getIconUpon(iconPack) val upon = getIconUpon(iconPack)
val mask = getIconMask(iconPack) val mask = getIconMask(iconPack)
val scale = getPackScale(iconPack) val scale = getPackScale(iconPack)
if (back == null && upon == null && mask == null) { if (back == null && upon == null && mask == null) {
return null return@withContext null
} }
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
@ -155,18 +155,18 @@ class IconPackManager(
val res = try { val res = try {
pm.getResourcesForApplication(pack) pm.getResourcesForApplication(pack)
} catch (e: Resources.NotFoundException) { } catch (e: Resources.NotFoundException) {
return null return@withContext null
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
return null return@withContext null
} }
if (mask != null) { if (mask != null) {
res.getIdentifier(mask, "drawable", pack).takeIf { it != 0 }?.let { res.getIdentifier(mask, "drawable", pack).takeIf { it != 0 }?.let {
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT) paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
val maskDrawable = try { val maskDrawable = try {
ResourcesCompat.getDrawable(res, it, null) ?: return null ResourcesCompat.getDrawable(res, it, null) ?: return@withContext null
} catch (e: Resources.NotFoundException) { } catch (e: Resources.NotFoundException) {
return null return@withContext null
} }
val maskBmp = maskDrawable.toBitmap(size, size) val maskBmp = maskDrawable.toBitmap(size, size)
inBounds = Rect(0, 0, maskBmp.width, maskBmp.height) inBounds = Rect(0, 0, maskBmp.width, maskBmp.height)
@ -178,9 +178,9 @@ class IconPackManager(
res.getIdentifier(upon, "drawable", pack).takeIf { it != 0 }?.let { res.getIdentifier(upon, "drawable", pack).takeIf { it != 0 }?.let {
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER) paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
val maskDrawable = try { val maskDrawable = try {
ResourcesCompat.getDrawable(res, it, null) ?: return null ResourcesCompat.getDrawable(res, it, null) ?: return@withContext null
} catch (e: Resources.NotFoundException) { } catch (e: Resources.NotFoundException) {
return null return@withContext null
} }
val maskBmp = maskDrawable.toBitmap(size, size) val maskBmp = maskDrawable.toBitmap(size, size)
inBounds = Rect(0, 0, maskBmp.width, maskBmp.height) inBounds = Rect(0, 0, maskBmp.width, maskBmp.height)
@ -192,9 +192,9 @@ class IconPackManager(
res.getIdentifier(back, "drawable", pack).takeIf { it != 0 }?.let { res.getIdentifier(back, "drawable", pack).takeIf { it != 0 }?.let {
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER) paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER)
val maskDrawable = try { val maskDrawable = try {
ResourcesCompat.getDrawable(res, it, null) ?: return null ResourcesCompat.getDrawable(res, it, null) ?: return@withContext null
} catch (e: Resources.NotFoundException) { } catch (e: Resources.NotFoundException) {
return null return@withContext null
} }
val maskBmp = maskDrawable.toBitmap(size, size) val maskBmp = maskDrawable.toBitmap(size, size)
inBounds = Rect(0, 0, maskBmp.width, maskBmp.height) inBounds = Rect(0, 0, maskBmp.width, maskBmp.height)
@ -203,7 +203,7 @@ class IconPackManager(
} }
} }
return StaticLauncherIcon( return@withContext StaticLauncherIcon(
foregroundLayer = StaticIconLayer( foregroundLayer = StaticIconLayer(
icon = BitmapDrawable(context.resources, bitmap), icon = BitmapDrawable(context.resources, bitmap),
scale = 1f, scale = 1f,

View File

@ -42,6 +42,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@ -138,30 +139,25 @@ class IconService(
} }
fun getIcon(searchable: SavableSearchable, size: Int): Flow<LauncherIcon> = channelFlow { fun getIcon(searchable: SavableSearchable, size: Int): Flow<LauncherIcon> {
iconProviders.collectLatest { providers -> val customIcon = customAttributesRepository.getCustomIcon(searchable)
transformations.collectLatest { transformations -> return combine(iconProviders, transformations, customIcon) { providers, transformations, ci ->
customAttributesRepository.getCustomIcon(searchable).collectLatest { customIcon -> var icon = cache.get(searchable.key + customIcon.hashCode())
if (icon != null) {
val provs = getProviders(customIcon) + providers return@combine icon
val transforms = getTransformations(customIcon) ?: transformations
var icon = cache.get(searchable.key + customIcon.hashCode())
if (icon != null) {
send(icon)
return@collectLatest
}
icon = provs.getFirstIcon(searchable, size)
if (icon != null) {
icon = icon.transform(transforms)
cache.put(searchable.key + customIcon.hashCode(), icon)
send(icon)
}
}
} }
val provs = if (ci != null) getProviders(ci) + providers else providers
val transforms = getTransformations(ci) ?: transformations
icon = provs.getFirstIcon(searchable, size)
if (icon != null) {
icon = icon.transform(transforms)
cache.put(searchable.key + customIcon.hashCode(), icon)
}
return@combine icon
} }
} }

View File

@ -8,39 +8,41 @@ import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.ktx.obtainTypedArrayOrNull import de.mm20.launcher2.ktx.obtainTypedArrayOrNull
import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.data.LauncherApp import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class CalendarIconProvider(val context: Context, val themed: Boolean): IconProvider { class CalendarIconProvider(val context: Context, val themed: Boolean): IconProvider {
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? { override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? = withContext(Dispatchers.IO) {
if(searchable !is LauncherApp) return null if(searchable !is LauncherApp) return@withContext null
val component = ComponentName(searchable.`package`, searchable.activity) val component = ComponentName(searchable.`package`, searchable.activity)
val pm = context.packageManager val pm = context.packageManager
val ai = try { val ai = try {
pm.getActivityInfo(component, PackageManager.GET_META_DATA) pm.getActivityInfo(component, PackageManager.GET_META_DATA)
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
return null return@withContext null
} }
var arrayId = ai.metaData?.getInt("com.teslacoilsw.launcher.calendarIconArray") ?: 0 var arrayId = ai.metaData?.getInt("com.teslacoilsw.launcher.calendarIconArray") ?: 0
if (arrayId == 0) arrayId = ai.metaData?.getInt("com.google.android.calendar.dynamic_icons") if (arrayId == 0) arrayId = ai.metaData?.getInt("com.google.android.calendar.dynamic_icons")
?: return null ?: return@withContext null
if (arrayId == 0) arrayId = ai.metaData?.getInt("org.lineageos.etar.dynamic_icons") if (arrayId == 0) arrayId = ai.metaData?.getInt("org.lineageos.etar.dynamic_icons")
?: return null ?: return@withContext null
if (arrayId == 0) return null if (arrayId == 0) return@withContext null
val resources = try { val resources = try {
pm.getResourcesForActivity(component) pm.getResourcesForActivity(component)
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
return null return@withContext null
} }
val typedArray = resources.obtainTypedArrayOrNull(arrayId) ?: return null val typedArray = resources.obtainTypedArrayOrNull(arrayId) ?: return@withContext null
if (typedArray.length() != 31) { if (typedArray.length() != 31) {
typedArray.recycle() typedArray.recycle()
return null return@withContext null
} }
val drawableIds = IntArray(31) val drawableIds = IntArray(31)
for (i in 0 until 31) { for (i in 0 until 31) {
drawableIds[i] = typedArray.getResourceId(i, 0) drawableIds[i] = typedArray.getResourceId(i, 0)
} }
typedArray.recycle() typedArray.recycle()
return DynamicCalendarIcon( return@withContext DynamicCalendarIcon(
resources = resources, resources = resources,
resourceIds = drawableIds, resourceIds = drawableIds,
isThemed = themed isThemed = themed

View File

@ -8,10 +8,12 @@ import de.mm20.launcher2.icons.compat.ClockIconConfig
import de.mm20.launcher2.icons.compat.toLauncherIcon import de.mm20.launcher2.icons.compat.toLauncherIcon
import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.data.LauncherApp import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class DynamicClockIconProvider(val context: Context, private val themed: Boolean) : IconProvider { class DynamicClockIconProvider(val context: Context, private val themed: Boolean) : IconProvider {
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? { override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? = withContext(Dispatchers.IO) {
if (searchable !is LauncherApp) return null if (searchable !is LauncherApp) return@withContext null
val pm = context.packageManager val pm = context.packageManager
val appInfo = try { val appInfo = try {
pm.getApplicationInfo( pm.getApplicationInfo(
@ -19,22 +21,22 @@ class DynamicClockIconProvider(val context: Context, private val themed: Boolean
PackageManager.GET_META_DATA PackageManager.GET_META_DATA
) )
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
return null return@withContext null
} }
if (appInfo.metaData == null) return null if (appInfo.metaData == null) return@withContext null
val drawableId = val drawableId =
appInfo.metaData.getInt("com.android.launcher3.LEVEL_PER_TICK_ICON_ROUND") appInfo.metaData.getInt("com.android.launcher3.LEVEL_PER_TICK_ICON_ROUND")
if (drawableId == 0) return null if (drawableId == 0) return@withContext null
val resources = try { val resources = try {
pm.getResourcesForApplication(appInfo) pm.getResourcesForApplication(appInfo)
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
return null return@withContext null
} }
val icon = AdaptiveIconDrawableCompat.from(resources, drawableId) ?: return null val icon = AdaptiveIconDrawableCompat.from(resources, drawableId) ?: return@withContext null
val defaultHour = val defaultHour =
appInfo.metaData.getInt("com.android.launcher3.DEFAULT_HOUR") appInfo.metaData.getInt("com.android.launcher3.DEFAULT_HOUR")
@ -71,7 +73,7 @@ class DynamicClockIconProvider(val context: Context, private val themed: Boolean
) )
} }
return icon.toLauncherIcon( return@withContext icon.toLauncherIcon(
themed = themed, themed = themed,
clock = clockConfig clock = clockConfig
) )

View File

@ -35,13 +35,13 @@ internal class LegacyToAdaptiveTransformation(
if (layer is StaticIconLayer) { if (layer is StaticIconLayer) {
val drawable = layer.icon val drawable = layer.icon
val bitmap = if (drawable is BitmapDrawable) {
drawable.bitmap
} else {
drawable.toBitmap(48, 48)
}
val palette = withContext(Dispatchers.Default) { val palette = withContext(Dispatchers.Default) {
val bitmap = if (drawable is BitmapDrawable) {
drawable.bitmap
} else {
drawable.toBitmap(48, 48)
}
Palette.from(bitmap).generate() Palette.from(bitmap).generate()
} }
return palette.getDominantColor(0) return palette.getDominantColor(0)