Add support for themed icon packs
This commit is contained in:
parent
ebba36c384
commit
9e56f960e6
@ -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>>
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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))
|
||||||
|
|||||||
@ -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)
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user