Add support for themed icon packs

This commit is contained in:
MM20 2023-02-14 22:39:29 +01:00
parent ebba36c384
commit 9e56f960e6
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
6 changed files with 131 additions and 64 deletions

View File

@ -51,6 +51,9 @@ interface IconDao {
@Query("SELECT * FROM IconPack")
suspend fun getInstalledIconPacks(): List<IconPackEntity>
@Query("SELECT * FROM IconPack WHERE packageName = :packageName LIMIT 1")
suspend fun getIconPack(packageName: String): IconPackEntity?
@Query("SELECT * FROM IconPack")
fun getInstalledIconPacksLiveData(): LiveData<List<IconPackEntity>>

View File

@ -4,6 +4,7 @@ import android.content.res.Resources
import android.graphics.drawable.AdaptiveIconDrawable
import androidx.core.content.res.ResourcesCompat
import de.mm20.launcher2.icons.transformations.LauncherIconTransformation
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.time.Instant
@ -33,36 +34,59 @@ internal class DynamicCalendarIcon(
backgroundLayer = ColorLayer()
)
var icon = if (isThemed) {
StaticLauncherIcon(
foregroundLayer = TintedIconLayer(
var icon = when {
isThemed && drawable is AdaptiveIconDrawable -> {
if (isAtLeastApiLevel(33) && drawable.monochrome != null) {
return@withContext StaticLauncherIcon(
foregroundLayer = TintedIconLayer(
icon = drawable.monochrome!!,
scale = 1f,
),
backgroundLayer = ColorLayer()
)
} else {
return@withContext StaticLauncherIcon(
foregroundLayer = TintedIconLayer(
icon = drawable.foreground!!,
scale = 1.5f,
),
backgroundLayer = ColorLayer()
)
}
}
isThemed -> {
StaticLauncherIcon(
foregroundLayer = TintedIconLayer(
icon = drawable,
scale = 0.5f,
),
backgroundLayer = ColorLayer()
)
}
drawable is AdaptiveIconDrawable -> {
return@withContext StaticLauncherIcon(
foregroundLayer = drawable.foreground?.let {
StaticIconLayer(
icon = it,
scale = 1.5f,
)
} ?: TransparentLayer,
backgroundLayer = drawable.background?.let {
StaticIconLayer(
icon = it,
scale = 1.5f,
)
} ?: TransparentLayer,
)
}
else -> StaticLauncherIcon(
foregroundLayer = StaticIconLayer(
icon = drawable,
scale = 0.5f,
scale = 1f,
),
backgroundLayer = ColorLayer()
backgroundLayer = TransparentLayer
)
} else if (drawable is AdaptiveIconDrawable) {
return@withContext StaticLauncherIcon(
foregroundLayer = drawable.foreground?.let {
StaticIconLayer(
icon = it,
scale = 1.5f,
)
} ?: TransparentLayer,
backgroundLayer = drawable.background?.let {
StaticIconLayer(
icon = it,
scale = 1.5f,
)
} ?: TransparentLayer,
)
} else StaticLauncherIcon(
foregroundLayer = StaticIconLayer(
icon = drawable,
scale = 1f,
),
backgroundLayer = TransparentLayer
)
}
for (transformation in transformations) {
icon = transformation.transform(icon)

View File

@ -1,5 +1,7 @@
package de.mm20.launcher2.icons
import android.content.Context
import android.content.pm.ResolveInfo
import de.mm20.launcher2.database.entities.IconPackEntity
data class IconPack(
@ -8,7 +10,7 @@ data class IconPack(
val version: String,
var scale: Float = 1f,
val themed: Boolean = false,
) {
) {
constructor(entity: IconPackEntity) : this(
name = entity.name,
packageName = entity.packageName,
@ -17,6 +19,17 @@ data class IconPack(
themed = entity.themed,
)
internal constructor(
context: Context,
resolveInfo: ResolveInfo,
themed: Boolean = false
): this(
name = resolveInfo.loadLabel(context.packageManager).toString(),
packageName = resolveInfo.activityInfo.packageName,
version = context.packageManager.getPackageInfo(resolveInfo.activityInfo.packageName, 0).versionName,
themed = themed,
)
fun toDatabaseEntity(): IconPackEntity {
return IconPackEntity(
name = name,

View File

@ -14,6 +14,7 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.toBitmap
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.database.AppDatabase
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.ktx.obtainTypedArrayOrNull
import de.mm20.launcher2.ktx.randomElementOrNull
import kotlinx.coroutines.Dispatchers
@ -46,13 +47,21 @@ class IconPackManager(
}
}
suspend fun getIconPack(packageName: String): IconPack? {
return withContext(Dispatchers.IO) {
appDatabase.iconDao().getIconPack(packageName)?.let {
IconPack(it)
}
}
}
suspend fun updateIconPacks() {
withContext(Dispatchers.IO) {
UpdateIconPacksWorker(context).doWork()
}
}
suspend fun getIcon(iconPack: String, componentName: ComponentName): LauncherIcon? {
suspend fun getIcon(iconPack: String, componentName: ComponentName, themed: Boolean = false): LauncherIcon? {
val res = try {
context.packageManager.getResourcesForApplication(iconPack)
} catch (e: PackageManager.NameNotFoundException) {
@ -66,7 +75,7 @@ class IconPackManager(
val drawableName = icon.drawable ?: return null
if (icon.type == "calendar") {
return getIconPackCalendarIcon(context, iconPack, drawableName)
return getIconPackCalendarIcon(context, iconPack, drawableName, themed)
}
val resId = res.getIdentifier(drawableName, "drawable", iconPack).takeIf { it != 0 }
?: return null
@ -75,8 +84,27 @@ class IconPackManager(
} catch (e: Resources.NotFoundException) {
return null
}
return when (drawable) {
is AdaptiveIconDrawable -> {
return when {
themed && drawable is AdaptiveIconDrawable -> {
if (isAtLeastApiLevel(33) && drawable.monochrome != null) {
return StaticLauncherIcon(
foregroundLayer = StaticIconLayer(
icon = drawable.monochrome!!,
scale = 1f,
),
backgroundLayer = ColorLayer(),
)
} else {
return StaticLauncherIcon(
foregroundLayer = TintedIconLayer(
icon = drawable.foreground,
scale = 1.5f,
),
backgroundLayer = ColorLayer(),
)
}
}
drawable is AdaptiveIconDrawable -> {
return StaticLauncherIcon(
foregroundLayer = drawable.foreground?.let {
StaticIconLayer(
@ -237,7 +265,8 @@ class IconPackManager(
private fun getIconPackCalendarIcon(
context: Context,
iconPack: String,
baseIconName: String
baseIconName: String,
themed: Boolean,
): DynamicCalendarIcon? {
val resources = try {
context.packageManager.getResourcesForApplication(iconPack)
@ -252,7 +281,8 @@ class IconPackManager(
}.toIntArray()
return DynamicCalendarIcon(
resources = resources,
resourceIds = drawableIds
resourceIds = drawableIds,
isThemed = themed,
)
}
@ -417,23 +447,16 @@ class IconPackManager(
class UpdateIconPacksWorker(val context: Context) {
fun doWork() {
val packs = loadInstalledPacks(context).map { it.activityInfo.packageName }
val packs = loadInstalledPacks(context)
val grayscaleProviders = loadInstalledGreyscaleProviders(context)
val iconDao = AppDatabase.getInstance(context).iconDao()
iconDao.uninstallIconPacksExcept(
packs.union(grayscaleProviders).toList()
packs.map { it.packageName }.union(grayscaleProviders).toList()
)
for (pack in packs) {
try {
val packInfo = context.packageManager.getPackageInfo(pack, 0)
val iconPack = IconPack(
name = packInfo.applicationInfo.loadLabel(context.packageManager).toString(),
packageName = pack,
version = packInfo.versionName
)
//if (iconDao.isInstalled(iconPack)) continue
installIconPack(iconPack)
installIconPack(pack)
} catch (e: PackageManager.NameNotFoundException) {
continue
}
@ -455,21 +478,19 @@ class UpdateIconPacksWorker(val context: Context) {
}
}
private fun loadInstalledPacks(context: Context): List<ResolveInfo> {
val packs = mutableListOf<ResolveInfo>()
private fun loadInstalledPacks(context: Context): List<IconPack> {
val packs = mutableListOf<IconPack>()
val pm = context.packageManager
var intent = Intent("org.adw.ActivityStarter.THEMES")
var intent = Intent("app.lawnchair.icons.THEMED_ICON")
val themedPacks = pm.queryIntentActivities(intent, 0)
packs.addAll(themedPacks.map { IconPack(context, it, true) })
intent = Intent("org.adw.ActivityStarter.THEMES")
val adwPacks = pm.queryIntentActivities(intent, 0)
packs.addAll(adwPacks)
packs.addAll(adwPacks.map { IconPack(context, it, false) })
intent = Intent("com.novalauncher.THEME")
val novaPacks = pm.queryIntentActivities(intent, 0)
novaPacks.forEach {
if (packs.none { p -> p.activityInfo.packageName == it.activityInfo.packageName }) packs.add(
it
)
}
packs.sortWith(ResolveInfo.DisplayNameComparator(pm))
return packs
packs.addAll(novaPacks.map { IconPack(context, it, false) })
return packs.distinctBy { it.packageName }
}
private fun installIconPack(iconPack: IconPack) {

View File

@ -5,6 +5,7 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.util.Log
import android.util.LruCache
import de.mm20.launcher2.data.customattrs.AdaptifiedLegacyIcon
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
@ -94,13 +95,18 @@ class IconRepository(
}
if (settings.iconPack.isNotBlank()) {
providers.add(
IconPackIconProvider(
context,
settings.iconPack,
iconPackManager
val pack = iconPackManager.getIconPack(settings.iconPack)
if (pack != null) {
providers.add(
IconPackIconProvider(
context,
pack,
iconPackManager
)
)
)
} else {
Log.w("MM20", "Icon pack ${settings.iconPack} not found")
}
}
providers.add(GoogleClockIconProvider(context))
providers.add(CalendarIconProvider(context))

View File

@ -10,17 +10,17 @@ import kotlinx.coroutines.withContext
class IconPackIconProvider(
private val context: Context,
private val iconPack: String,
private val iconPack: IconPack,
private val iconPackManager: IconPackManager,
): IconProvider {
override suspend fun getIcon(searchable: SavableSearchable, size: Int): LauncherIcon? {
if (searchable !is LauncherApp) return null
val component = ComponentName(searchable.`package`, searchable.activity)
return iconPackManager.getIcon(iconPack, component)
return iconPackManager.getIcon(iconPack.packageName, component, iconPack.themed)
?: iconPackManager.generateIcon(
context,
iconPack,
iconPack.packageName,
baseIcon = withContext(Dispatchers.IO) {
searchable.launcherActivityInfo.getIcon(context.resources.displayMetrics.densityDpi)
},