Add themed icons to icon picker, add option to force themed icons
This commit is contained in:
parent
b979c81501
commit
389b33aebe
@ -81,8 +81,7 @@ sealed class CustomIcon : CustomAttribute {
|
|||||||
}
|
}
|
||||||
"custom_themed_icon" -> {
|
"custom_themed_icon" -> {
|
||||||
CustomThemedIcon(
|
CustomThemedIcon(
|
||||||
iconName = payload.getString("icon"),
|
iconPackageName = payload.getString("icon"),
|
||||||
iconPackPackage = payload.getString("icon_pack")
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
"default_icon" -> {
|
"default_icon" -> {
|
||||||
@ -143,14 +142,12 @@ data class AdaptifiedLegacyIcon(
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class CustomThemedIcon(
|
data class CustomThemedIcon(
|
||||||
val iconPackPackage: String,
|
val iconPackageName: String,
|
||||||
val iconName: String,
|
|
||||||
) : CustomIcon() {
|
) : CustomIcon() {
|
||||||
override fun toDatabaseValue(): String {
|
override fun toDatabaseValue(): String {
|
||||||
return jsonObjectOf(
|
return jsonObjectOf(
|
||||||
"type" to "custom_themed_icon",
|
"type" to "custom_themed_icon",
|
||||||
"icon" to iconName,
|
"icon" to iconPackageName,
|
||||||
"icon_pack" to iconPackPackage,
|
|
||||||
).toString()
|
).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,9 +19,12 @@ interface IconDao {
|
|||||||
@Query("SELECT * FROM Icons WHERE componentName = :componentName AND (type = 'app' OR type = 'calendar')")
|
@Query("SELECT * FROM Icons WHERE componentName = :componentName AND (type = 'app' OR type = 'calendar')")
|
||||||
suspend fun getIconsFromAllPacks(componentName: String): List<IconEntity>
|
suspend fun getIconsFromAllPacks(componentName: String): List<IconEntity>
|
||||||
|
|
||||||
@Query("SELECT * FROM Icons WHERE (type = 'app' OR type = 'calendar') AND drawable LIKE :query ORDER BY iconPack, drawable LIMIT :limit")
|
@Query("SELECT * FROM Icons WHERE (type = 'app' OR type = 'calendar') AND (drawable LIKE :query OR componentName LIKE :query) ORDER BY iconPack, drawable LIMIT :limit")
|
||||||
suspend fun searchIconPackIcons(query: String, limit: Int = 100): List<IconEntity>
|
suspend fun searchIconPackIcons(query: String, limit: Int = 100): List<IconEntity>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Icons WHERE (type = 'greyscale_icon') AND componentName LIKE :query GROUP BY componentName ORDER BY drawable LIMIT :limit")
|
||||||
|
suspend fun searchGreyscaleIcons(query: String, limit: Int = 100): List<IconEntity>
|
||||||
|
|
||||||
@Query("DELETE FROM Icons WHERE iconPack = :iconPack")
|
@Query("DELETE FROM Icons WHERE iconPack = :iconPack")
|
||||||
fun deleteIcons(iconPack: String)
|
fun deleteIcons(iconPack: String)
|
||||||
|
|
||||||
|
|||||||
@ -454,6 +454,8 @@
|
|||||||
<string name="preference_theme_system">Follow system</string>
|
<string name="preference_theme_system">Follow system</string>
|
||||||
<string name="preference_themed_icons">Themed icons</string>
|
<string name="preference_themed_icons">Themed icons</string>
|
||||||
<string name="preference_themed_icons_summary">Color icons with the application\'s color scheme</string>
|
<string name="preference_themed_icons_summary">Color icons with the application\'s color scheme</string>
|
||||||
|
<string name="preference_force_themed_icons">Force themed icons</string>
|
||||||
|
<string name="preference_force_themed_icons_summary">Apply the application\'s color scheme to all icons, including unsupported ones (not recommended)</string>
|
||||||
<string name="preference_icon_pack">Icon pack</string>
|
<string name="preference_icon_pack">Icon pack</string>
|
||||||
<string name="preference_icon_pack_summary_empty">No icon packs installed</string>
|
<string name="preference_icon_pack_summary_empty">No icon packs installed</string>
|
||||||
<string name="preference_category_system_bars">System bars</string>
|
<string name="preference_category_system_bars">System bars</string>
|
||||||
|
|||||||
@ -8,15 +8,14 @@ import android.content.pm.ResolveInfo
|
|||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.content.res.XmlResourceParser
|
import android.content.res.XmlResourceParser
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
import android.graphics.drawable.AdaptiveIconDrawable
|
import android.graphics.drawable.*
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.res.ResourcesCompat
|
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.customattrs.CustomIconPackIcon
|
import de.mm20.launcher2.customattrs.CustomIconPackIcon
|
||||||
import de.mm20.launcher2.database.AppDatabase
|
import de.mm20.launcher2.database.AppDatabase
|
||||||
|
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
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -237,6 +236,138 @@ class IconPackManager(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getThemedIcon(packageName: String): LauncherIcon? {
|
||||||
|
val icon = getGreyscaleIcon(packageName) ?: return null
|
||||||
|
val resId = icon.drawable?.toIntOrNull() ?: return null
|
||||||
|
try {
|
||||||
|
val resources = context.packageManager.getResourcesForApplication(icon.iconPack)
|
||||||
|
return getThemedClockIcon(resources, resId) ?: getThemedCalendarIcon(
|
||||||
|
resources,
|
||||||
|
resId,
|
||||||
|
iconProviderPackage = icon.iconPack
|
||||||
|
) ?: getThemedStaticIcon(resources, resId)
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun getGreyscaleIcon(packageName: String): IconPackIcon? {
|
||||||
|
val iconDao = AppDatabase.getInstance(context).iconDao()
|
||||||
|
return iconDao.getGreyscaleIcon(ComponentName(packageName, packageName).flattenToString())
|
||||||
|
?.let { IconPackIcon(it) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getThemedStaticIcon(resources: Resources, resId: Int): LauncherIcon? {
|
||||||
|
try {
|
||||||
|
val fg = ResourcesCompat.getDrawable(resources, resId, null) ?: return null
|
||||||
|
return StaticLauncherIcon(
|
||||||
|
foregroundLayer = TintedIconLayer(
|
||||||
|
icon = fg,
|
||||||
|
scale = 0.5f,
|
||||||
|
),
|
||||||
|
backgroundLayer = ColorLayer()
|
||||||
|
)
|
||||||
|
} catch (e: Resources.NotFoundException) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getThemedClockIcon(resources: Resources, resId: Int): LauncherIcon? {
|
||||||
|
try {
|
||||||
|
val array = resources.obtainTypedArrayOrNull(resId) ?: return null
|
||||||
|
var i = 0
|
||||||
|
var drawable: LayerDrawable? = null
|
||||||
|
var minuteIndex: Int? = null
|
||||||
|
var defaultMinute = 0
|
||||||
|
var hourIndex: Int? = null
|
||||||
|
var defaultHour = 0
|
||||||
|
while (i < array.length()) {
|
||||||
|
when (array.getString(i)) {
|
||||||
|
"com.android.launcher3.LEVEL_PER_TICK_ICON_ROUND" -> {
|
||||||
|
i++
|
||||||
|
drawable = array.getDrawable(i) as? LayerDrawable
|
||||||
|
}
|
||||||
|
"com.android.launcher3.HOUR_LAYER_INDEX" -> {
|
||||||
|
i++
|
||||||
|
hourIndex = array.getInt(i, -1).takeIf { it != -1 }
|
||||||
|
}
|
||||||
|
"com.android.launcher3.MINUTE_LAYER_INDEX" -> {
|
||||||
|
i++
|
||||||
|
minuteIndex = array.getInt(i, -1).takeIf { it != -1 }
|
||||||
|
}
|
||||||
|
"com.android.launcher3.DEFAULT_HOUR" -> {
|
||||||
|
i++
|
||||||
|
defaultHour = array.getInt(i, 0)
|
||||||
|
}
|
||||||
|
"com.android.launcher3.DEFAULT_MINUTE" -> {
|
||||||
|
i++
|
||||||
|
defaultMinute = array.getInt(i, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if (drawable != null && minuteIndex != null && hourIndex != null) {
|
||||||
|
|
||||||
|
return StaticLauncherIcon(
|
||||||
|
foregroundLayer = TintedClockLayer(
|
||||||
|
sublayers = (0 until drawable.numberOfLayers).map {
|
||||||
|
val drw = drawable.getDrawable(it)
|
||||||
|
if (drw is RotateDrawable) {
|
||||||
|
drw.level = when (it) {
|
||||||
|
hourIndex -> {
|
||||||
|
(12 - defaultHour) * 60
|
||||||
|
}
|
||||||
|
minuteIndex -> {
|
||||||
|
(60 - defaultMinute)
|
||||||
|
}
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClockSublayer(
|
||||||
|
drawable = drw,
|
||||||
|
role = when (it) {
|
||||||
|
hourIndex -> ClockSublayerRole.Hour
|
||||||
|
minuteIndex -> ClockSublayerRole.Minute
|
||||||
|
else -> ClockSublayerRole.Static
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
scale = 1.5f,
|
||||||
|
),
|
||||||
|
backgroundLayer = ColorLayer()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Resources.NotFoundException) {
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getThemedCalendarIcon(
|
||||||
|
resources: Resources,
|
||||||
|
resId: Int,
|
||||||
|
iconProviderPackage: String
|
||||||
|
): LauncherIcon? {
|
||||||
|
try {
|
||||||
|
val array = resources.obtainTypedArrayOrNull(resId) ?: return null
|
||||||
|
if (array.length() != 31) return null
|
||||||
|
|
||||||
|
return DynamicCalendarIcon(
|
||||||
|
resources = resources,
|
||||||
|
resourceIds = IntArray(31) {
|
||||||
|
array.getResourceId(it, 0).takeIf { it != 0 } ?: return null
|
||||||
|
},
|
||||||
|
isThemed = true
|
||||||
|
)
|
||||||
|
} catch (e: Resources.NotFoundException) {
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun searchIconPackIcon(query: String): List<IconPackIcon> {
|
suspend fun searchIconPackIcon(query: String): List<IconPackIcon> {
|
||||||
val iconDao = appDatabase.iconDao()
|
val iconDao = appDatabase.iconDao()
|
||||||
return iconDao.searchIconPackIcons("%$query%").map {
|
return iconDao.searchIconPackIcons("%$query%").map {
|
||||||
@ -244,6 +375,12 @@ class IconPackManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun searchThemedIcons(query: String): List<IconPackIcon> {
|
||||||
|
val iconDao = appDatabase.iconDao()
|
||||||
|
return iconDao.searchGreyscaleIcons("%$query%").map {
|
||||||
|
IconPackIcon(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import android.graphics.Color
|
|||||||
import android.util.LruCache
|
import android.util.LruCache
|
||||||
import de.mm20.launcher2.customattrs.*
|
import de.mm20.launcher2.customattrs.*
|
||||||
import de.mm20.launcher2.icons.providers.*
|
import de.mm20.launcher2.icons.providers.*
|
||||||
|
import de.mm20.launcher2.icons.transformations.ForceThemedIconTransformation
|
||||||
import de.mm20.launcher2.icons.transformations.LauncherIconTransformation
|
import de.mm20.launcher2.icons.transformations.LauncherIconTransformation
|
||||||
import de.mm20.launcher2.icons.transformations.LegacyToAdaptiveTransformation
|
import de.mm20.launcher2.icons.transformations.LegacyToAdaptiveTransformation
|
||||||
import de.mm20.launcher2.icons.transformations.transform
|
import de.mm20.launcher2.icons.transformations.transform
|
||||||
@ -66,7 +67,7 @@ class IconRepository(
|
|||||||
val providers = mutableListOf<IconProvider>()
|
val providers = mutableListOf<IconProvider>()
|
||||||
|
|
||||||
if (settings.themedIcons) {
|
if (settings.themedIcons) {
|
||||||
providers.add(ThemedIconProvider(context))
|
providers.add(ThemedIconProvider(iconPackManager))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.iconPack.isNotBlank()) {
|
if (settings.iconPack.isNotBlank()) {
|
||||||
@ -87,6 +88,7 @@ class IconRepository(
|
|||||||
val transformations = mutableListOf<LauncherIconTransformation>()
|
val transformations = mutableListOf<LauncherIconTransformation>()
|
||||||
|
|
||||||
if (settings.adaptify) transformations.add(LegacyToAdaptiveTransformation())
|
if (settings.adaptify) transformations.add(LegacyToAdaptiveTransformation())
|
||||||
|
if (settings.themedIcons && settings.forceThemed) transformations.add(ForceThemedIconTransformation())
|
||||||
|
|
||||||
this@IconRepository.placeholderProvider = placeholderProvider
|
this@IconRepository.placeholderProvider = placeholderProvider
|
||||||
iconProviders.value = providers
|
iconProviders.value = providers
|
||||||
@ -140,6 +142,14 @@ class IconRepository(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (customIcon is CustomThemedIcon) {
|
||||||
|
return listOf(
|
||||||
|
CustomThemedIconProvider(
|
||||||
|
customIcon,
|
||||||
|
iconPackManager
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,6 +253,15 @@ class IconRepository(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val themedIcon = iconPackManager.getGreyscaleIcon(searchable.`package`)
|
||||||
|
if (themedIcon != null && themedIcon.componentName?.packageName != null) {
|
||||||
|
providerOptions.add(
|
||||||
|
CustomThemedIcon(
|
||||||
|
iconPackageName = themedIcon.componentName.packageName,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestions.addAll(
|
suggestions.addAll(
|
||||||
@ -272,9 +291,9 @@ class IconRepository(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun searchIconPackIcon(query: String): List<CustomIconWithPreview> {
|
suspend fun searchCustomIcons(query: String): List<CustomIconWithPreview> {
|
||||||
val transformations = this.transformations.first()
|
val transformations = this.transformations.first()
|
||||||
return iconPackManager.searchIconPackIcon(query).mapNotNull {
|
val iconPackIcons = iconPackManager.searchIconPackIcon(query).mapNotNull {
|
||||||
val componentName = it.componentName ?: return@mapNotNull null
|
val componentName = it.componentName ?: return@mapNotNull null
|
||||||
|
|
||||||
CustomIconWithPreview(
|
CustomIconWithPreview(
|
||||||
@ -285,6 +304,19 @@ class IconRepository(
|
|||||||
preview = iconPackManager.getIcon(it.iconPack, componentName)?.transform(transformations) ?: return@mapNotNull null
|
preview = iconPackManager.getIcon(it.iconPack, componentName)?.transform(transformations) ?: return@mapNotNull null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val themedIcons = iconPackManager.searchThemedIcons(query).mapNotNull {
|
||||||
|
val componentName = it.componentName ?: return@mapNotNull null
|
||||||
|
|
||||||
|
CustomIconWithPreview(
|
||||||
|
customIcon = CustomThemedIcon(
|
||||||
|
iconPackageName = componentName.packageName,
|
||||||
|
),
|
||||||
|
preview = iconPackManager.getThemedIcon(componentName.packageName)?.transform(transformations) ?: return@mapNotNull null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return iconPackIcons + themedIcons
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCustomIcon(searchable: Searchable, icon: CustomIcon?) {
|
fun setCustomIcon(searchable: Searchable, icon: CustomIcon?) {
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
package de.mm20.launcher2.icons.providers
|
||||||
|
|
||||||
|
import de.mm20.launcher2.customattrs.CustomThemedIcon
|
||||||
|
import de.mm20.launcher2.icons.IconPackManager
|
||||||
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
|
import de.mm20.launcher2.search.data.Searchable
|
||||||
|
|
||||||
|
class CustomThemedIconProvider(
|
||||||
|
private val customIcon: CustomThemedIcon,
|
||||||
|
private val iconPackManager: IconPackManager,
|
||||||
|
): IconProvider {
|
||||||
|
override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon? {
|
||||||
|
return iconPackManager.getThemedIcon(customIcon.iconPackageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,143 +10,16 @@ import androidx.core.content.res.ResourcesCompat
|
|||||||
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.icons.*
|
import de.mm20.launcher2.icons.*
|
||||||
import de.mm20.launcher2.ktx.getDrawableOrNull
|
|
||||||
import de.mm20.launcher2.ktx.obtainTypedArrayOrNull
|
import de.mm20.launcher2.ktx.obtainTypedArrayOrNull
|
||||||
import de.mm20.launcher2.search.data.Application
|
import de.mm20.launcher2.search.data.Application
|
||||||
import de.mm20.launcher2.search.data.Searchable
|
import de.mm20.launcher2.search.data.Searchable
|
||||||
|
|
||||||
internal class ThemedIconProvider(
|
internal class ThemedIconProvider(
|
||||||
private val context: Context,
|
private val iconPackManager: IconPackManager,
|
||||||
) : IconProvider {
|
) : IconProvider {
|
||||||
|
|
||||||
override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon? {
|
override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon? {
|
||||||
if (searchable !is Application) return null
|
if (searchable !is Application) return null
|
||||||
val icon = getGreyscaleIcon(searchable.`package`) ?: return null
|
return iconPackManager.getThemedIcon(searchable.`package`)
|
||||||
val resId = icon.drawable?.toIntOrNull() ?: return null
|
|
||||||
try {
|
|
||||||
val resources = context.packageManager.getResourcesForApplication(icon.iconPack)
|
|
||||||
return getClockIcon(resources, resId) ?: getCalendarIcon(
|
|
||||||
resources,
|
|
||||||
resId,
|
|
||||||
iconProviderPackage = icon.iconPack
|
|
||||||
) ?: getStaticIcon(resources, resId)
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private suspend fun getGreyscaleIcon(packageName: String): IconPackIcon? {
|
|
||||||
val iconDao = AppDatabase.getInstance(context).iconDao()
|
|
||||||
return iconDao.getGreyscaleIcon(ComponentName(packageName, packageName).flattenToString())
|
|
||||||
?.let { IconPackIcon(it) }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getStaticIcon(resources: Resources, resId: Int): LauncherIcon? {
|
|
||||||
try {
|
|
||||||
val fg = ResourcesCompat.getDrawable(resources, resId, null) ?: return null
|
|
||||||
return StaticLauncherIcon(
|
|
||||||
foregroundLayer = TintedIconLayer(
|
|
||||||
icon = fg,
|
|
||||||
scale = 0.5f,
|
|
||||||
),
|
|
||||||
backgroundLayer = ColorLayer()
|
|
||||||
)
|
|
||||||
} catch (e: Resources.NotFoundException) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getClockIcon(resources: Resources, resId: Int): LauncherIcon? {
|
|
||||||
try {
|
|
||||||
val array = resources.obtainTypedArrayOrNull(resId) ?: return null
|
|
||||||
var i = 0
|
|
||||||
var drawable: LayerDrawable? = null
|
|
||||||
var minuteIndex: Int? = null
|
|
||||||
var defaultMinute = 0
|
|
||||||
var hourIndex: Int? = null
|
|
||||||
var defaultHour = 0
|
|
||||||
while (i < array.length()) {
|
|
||||||
when (array.getString(i)) {
|
|
||||||
"com.android.launcher3.LEVEL_PER_TICK_ICON_ROUND" -> {
|
|
||||||
i++
|
|
||||||
drawable = array.getDrawable(i) as? LayerDrawable
|
|
||||||
}
|
|
||||||
"com.android.launcher3.HOUR_LAYER_INDEX" -> {
|
|
||||||
i++
|
|
||||||
hourIndex = array.getInt(i, -1).takeIf { it != -1 }
|
|
||||||
}
|
|
||||||
"com.android.launcher3.MINUTE_LAYER_INDEX" -> {
|
|
||||||
i++
|
|
||||||
minuteIndex = array.getInt(i, -1).takeIf { it != -1 }
|
|
||||||
}
|
|
||||||
"com.android.launcher3.DEFAULT_HOUR" -> {
|
|
||||||
i++
|
|
||||||
defaultHour = array.getInt(i, 0)
|
|
||||||
}
|
|
||||||
"com.android.launcher3.DEFAULT_MINUTE" -> {
|
|
||||||
i++
|
|
||||||
defaultMinute = array.getInt(i, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if (drawable != null && minuteIndex != null && hourIndex != null) {
|
|
||||||
|
|
||||||
return StaticLauncherIcon(
|
|
||||||
foregroundLayer = TintedClockLayer(
|
|
||||||
sublayers = (0 until drawable.numberOfLayers).map {
|
|
||||||
val drw = drawable.getDrawable(it)
|
|
||||||
if (drw is RotateDrawable) {
|
|
||||||
drw.level = when (it) {
|
|
||||||
hourIndex -> {
|
|
||||||
(12 - defaultHour) * 60
|
|
||||||
}
|
|
||||||
minuteIndex -> {
|
|
||||||
(60 - defaultMinute)
|
|
||||||
}
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClockSublayer(
|
|
||||||
drawable = drw,
|
|
||||||
role = when (it) {
|
|
||||||
hourIndex -> ClockSublayerRole.Hour
|
|
||||||
minuteIndex -> ClockSublayerRole.Minute
|
|
||||||
else -> ClockSublayerRole.Static
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
scale = 1.5f,
|
|
||||||
),
|
|
||||||
backgroundLayer = ColorLayer()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (e: Resources.NotFoundException) {
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getCalendarIcon(
|
|
||||||
resources: Resources,
|
|
||||||
resId: Int,
|
|
||||||
iconProviderPackage: String
|
|
||||||
): LauncherIcon? {
|
|
||||||
try {
|
|
||||||
val array = resources.obtainTypedArrayOrNull(resId) ?: return null
|
|
||||||
if (array.length() != 31) return null
|
|
||||||
|
|
||||||
return DynamicCalendarIcon(
|
|
||||||
resources = resources,
|
|
||||||
resourceIds = IntArray(31) {
|
|
||||||
array.getResourceId(it, 0).takeIf { it != 0 } ?: return null
|
|
||||||
},
|
|
||||||
isThemed = true
|
|
||||||
)
|
|
||||||
} catch (e: Resources.NotFoundException) {
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package de.mm20.launcher2.icons.transformations
|
||||||
|
|
||||||
|
import de.mm20.launcher2.icons.*
|
||||||
|
|
||||||
|
internal class ForceThemedIconTransformation : LauncherIconTransformation {
|
||||||
|
override suspend fun transform(icon: StaticLauncherIcon): StaticLauncherIcon {
|
||||||
|
return StaticLauncherIcon(
|
||||||
|
foregroundLayer = asThemed(icon.foregroundLayer),
|
||||||
|
backgroundLayer = ColorLayer(0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun asThemed(layer: LauncherIconLayer): LauncherIconLayer {
|
||||||
|
return when(layer) {
|
||||||
|
is ClockLayer -> TintedClockLayer(
|
||||||
|
scale = layer.scale,
|
||||||
|
sublayers = layer.sublayers,
|
||||||
|
)
|
||||||
|
is ColorLayer -> layer.copy(color = 0)
|
||||||
|
is StaticIconLayer -> TintedIconLayer(
|
||||||
|
color = 0,
|
||||||
|
icon = layer.icon,
|
||||||
|
scale = layer.scale / 1.5f,
|
||||||
|
)
|
||||||
|
is TextLayer -> layer.copy(
|
||||||
|
color = 0
|
||||||
|
)
|
||||||
|
else -> layer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -219,7 +219,9 @@ message Settings {
|
|||||||
IconShape shape = 1;
|
IconShape shape = 1;
|
||||||
bool themed_icons = 2;
|
bool themed_icons = 2;
|
||||||
string icon_pack = 3;
|
string icon_pack = 3;
|
||||||
|
reserved 4;
|
||||||
bool adaptify = 5;
|
bool adaptify = 5;
|
||||||
|
bool force_themed = 6;
|
||||||
}
|
}
|
||||||
IconSettings icons = 21;
|
IconSettings icons = 21;
|
||||||
|
|
||||||
|
|||||||
@ -60,7 +60,7 @@ class CustomizeSearchableSheetVM(
|
|||||||
debounceSearchJob = launch {
|
debounceSearchJob = launch {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
isSearchingIcons.value = true
|
isSearchingIcons.value = true
|
||||||
iconSearchResults.value = iconRepository.searchIconPackIcon(query)
|
iconSearchResults.value = iconRepository.searchCustomIcons(query)
|
||||||
isSearchingIcons.value = false
|
isSearchingIcons.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -199,6 +199,16 @@ fun AppearanceSettingsScreen() {
|
|||||||
viewModel.setThemedIcons(it)
|
viewModel.setThemedIcons(it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
val forceThemedIcons by viewModel.forceThemedIcons.observeAsState()
|
||||||
|
SwitchPreference(
|
||||||
|
title = stringResource(R.string.preference_force_themed_icons),
|
||||||
|
summary = stringResource(R.string.preference_force_themed_icons_summary),
|
||||||
|
value = forceThemedIcons == true,
|
||||||
|
enabled = themedIcons == true,
|
||||||
|
onValueChanged = {
|
||||||
|
viewModel.setForceThemedIcons(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
val iconPack by viewModel.iconPack.observeAsState()
|
val iconPack by viewModel.iconPack.observeAsState()
|
||||||
val installedIconPacks by viewModel.installedIconPacks.observeAsState(emptyList())
|
val installedIconPacks by viewModel.installedIconPacks.observeAsState(emptyList())
|
||||||
|
|||||||
@ -165,6 +165,20 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val forceThemedIcons = dataStore.data.map { it.icons.forceThemed }.asLiveData()
|
||||||
|
fun setForceThemedIcons(forceThemedIcons: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
dataStore.updateData {
|
||||||
|
it.toBuilder()
|
||||||
|
.setIcons(
|
||||||
|
it.icons.toBuilder()
|
||||||
|
.setForceThemed(forceThemedIcons)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val installedIconPacks: LiveData<List<IconPack>> = liveData {
|
val installedIconPacks: LiveData<List<IconPack>> = liveData {
|
||||||
emit(
|
emit(
|
||||||
listOf(
|
listOf(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user