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") @Query("SELECT * FROM IconPack")
suspend fun getInstalledIconPacks(): List<IconPackEntity> suspend fun getInstalledIconPacks(): List<IconPackEntity>
@Query("SELECT * FROM IconPack WHERE packageName = :packageName LIMIT 1")
suspend fun getIconPack(packageName: String): IconPackEntity?
@Query("SELECT * FROM IconPack") @Query("SELECT * FROM IconPack")
fun getInstalledIconPacksLiveData(): LiveData<List<IconPackEntity>> fun getInstalledIconPacksLiveData(): LiveData<List<IconPackEntity>>

View File

@ -4,6 +4,7 @@ import android.content.res.Resources
import android.graphics.drawable.AdaptiveIconDrawable import android.graphics.drawable.AdaptiveIconDrawable
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import de.mm20.launcher2.icons.transformations.LauncherIconTransformation import de.mm20.launcher2.icons.transformations.LauncherIconTransformation
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.time.Instant import java.time.Instant
@ -33,36 +34,59 @@ internal class DynamicCalendarIcon(
backgroundLayer = ColorLayer() backgroundLayer = ColorLayer()
) )
var icon = if (isThemed) { var icon = when {
StaticLauncherIcon( isThemed && drawable is AdaptiveIconDrawable -> {
foregroundLayer = TintedIconLayer( 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, 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) { for (transformation in transformations) {
icon = transformation.transform(icon) icon = transformation.transform(icon)

View File

@ -1,5 +1,7 @@
package de.mm20.launcher2.icons package de.mm20.launcher2.icons
import android.content.Context
import android.content.pm.ResolveInfo
import de.mm20.launcher2.database.entities.IconPackEntity import de.mm20.launcher2.database.entities.IconPackEntity
data class IconPack( data class IconPack(
@ -8,7 +10,7 @@ data class IconPack(
val version: String, val version: String,
var scale: Float = 1f, var scale: Float = 1f,
val themed: Boolean = false, val themed: Boolean = false,
) { ) {
constructor(entity: IconPackEntity) : this( constructor(entity: IconPackEntity) : this(
name = entity.name, name = entity.name,
packageName = entity.packageName, packageName = entity.packageName,
@ -17,6 +19,17 @@ data class IconPack(
themed = entity.themed, 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 { fun toDatabaseEntity(): IconPackEntity {
return IconPackEntity( return IconPackEntity(
name = name, name = name,

View File

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

View File

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

View File

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