Themed icons: correctly apply application theme colors

This commit is contained in:
MM20 2022-03-05 16:49:53 +01:00
parent bfdc2608fc
commit 667ec0f932
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
4 changed files with 60 additions and 94 deletions

View File

@ -4,7 +4,9 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.res.Resources
import android.util.LruCache import android.util.LruCache
import android.util.TypedValue
import de.mm20.launcher2.icons.providers.* import de.mm20.launcher2.icons.providers.*
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.Settings
@ -29,6 +31,8 @@ class IconRepository(
private val cache = LruCache<String, LauncherIcon>(200) private val cache = LruCache<String, LauncherIcon>(200)
private val themeColors = MutableStateFlow(ThemeColors())
private var iconProviders: MutableStateFlow<List<IconProvider>> = MutableStateFlow(listOf()) private var iconProviders: MutableStateFlow<List<IconProvider>> = MutableStateFlow(listOf())
private lateinit var placeholderProvider: IconProvider private lateinit var placeholderProvider: IconProvider
@ -44,8 +48,31 @@ class IconRepository(
}) })
scope.launch { scope.launch {
dataStore.data.map { it.icons }.distinctUntilChanged().collectLatest { dataStore.data.map { it.icons }.distinctUntilChanged().collectLatest { settings ->
recreate(it) themeColors.collectLatest { colors ->
val placeholderProvider = if (settings.themedIcons) {
ThemedPlaceholderIconProvider(context, colors)
} else {
PlaceholderIconProvider(context)
}
val providers = mutableListOf<IconProvider>()
if (settings.themedIcons) {
providers.add(ThemedIconProvider(context, colors))
}
if (settings.iconPack.isNotBlank()) {
providers.add(IconPackIconProvider(context, settings.iconPack, settings.legacyIconBg))
}
providers.add(GoogleClockIconProvider(context))
providers.add(CalendarIconProvider(context))
providers.add(SystemIconProvider(context, settings.legacyIconBg))
providers.add(placeholderProvider)
cache.evictAll()
this@IconRepository.placeholderProvider = placeholderProvider
iconProviders.value = providers
}
} }
} }
} }
@ -82,13 +109,6 @@ class IconRepository(
} }
} }
/**
* Returns the icon for the given Searchable if it was requested earlier and is still in cache.
* Returns `null` otherwise.
*/
fun getIconIfCached(searchable: Searchable): LauncherIcon? {
return cache[searchable.key]
}
fun requestIconPackListUpdate() { fun requestIconPackListUpdate() {
scope.launch { scope.launch {
@ -100,34 +120,19 @@ class IconRepository(
return iconPackManager.getInstalledIconPacks() return iconPackManager.getInstalledIconPacks()
} }
fun recreate() { fun applyTheme(theme: Resources.Theme) {
scope.launch { val typedValue = TypedValue()
recreate(dataStore.data.map { it.icons }.first()) val bgColor = theme.resolveAttribute(R.attr.colorPrimaryContainer, typedValue, true).let {
typedValue.data
} }
val fgColor = theme.resolveAttribute(R.attr.colorOnPrimaryContainer, typedValue, true).let {
typedValue.data
}
themeColors.value = ThemeColors(foreground = fgColor, background = bgColor)
} }
}
private fun recreate(settings: Settings.IconSettings) { internal data class ThemeColors(
val placeholderProvider = if (settings.themedIcons) { val foreground: Int = 0xFFFFFFFF.toInt(),
ThemedPlaceholderIconProvider(context) val background: Int = 0xFF000000.toInt(),
} else { )
PlaceholderIconProvider(context)
}
val providers = mutableListOf<IconProvider>()
if (settings.themedIcons) {
providers.add(ThemedIconProvider(context))
}
if (settings.iconPack.isNotBlank()) {
providers.add(IconPackIconProvider(context, settings.iconPack, settings.legacyIconBg))
}
providers.add(GoogleClockIconProvider(context))
providers.add(CalendarIconProvider(context))
providers.add(SystemIconProvider(context, settings.legacyIconBg))
providers.add(placeholderProvider)
cache.evictAll()
this@IconRepository.placeholderProvider = placeholderProvider
iconProviders.value = providers
}
}

View File

@ -16,28 +16,10 @@ 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
class ThemedIconProvider( internal class ThemedIconProvider(
private val context: Context private val context: Context,
private val colors: ThemeColors,
) : IconProvider { ) : IconProvider {
private val fgColor: Int
private val bgColor: Int
init {
val theme = context.resources.newTheme()
theme.applyStyle(R.style.DefaultColors, true)
val typedValue = TypedValue()
val isDarkMode =
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES != 0
val bgAttr = R.attr.colorPrimaryContainer
val fgAttr = R.attr.colorOnPrimaryContainer
bgColor = theme.resolveAttribute(bgAttr, typedValue, true).let {
typedValue.data
}
fgColor = theme.resolveAttribute(fgAttr, typedValue, true).let {
typedValue.data
}
}
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
@ -67,11 +49,11 @@ class ThemedIconProvider(
private fun getStaticIcon(resources: Resources, resId: Int): LauncherIcon? { private fun getStaticIcon(resources: Resources, resId: Int): LauncherIcon? {
try { try {
val fg = ResourcesCompat.getDrawable(resources, resId, null) ?: return null val fg = ResourcesCompat.getDrawable(resources, resId, null) ?: return null
fg.setTint(fgColor) fg.setTint(colors.foreground)
return LauncherIcon( return LauncherIcon(
foreground = fg, foreground = fg,
foregroundScale = 0.5f, foregroundScale = 0.5f,
background = ColorDrawable(bgColor) background = ColorDrawable(colors.background)
) )
} catch (e: Resources.NotFoundException) { } catch (e: Resources.NotFoundException) {
return null return null
@ -103,10 +85,10 @@ class ThemedIconProvider(
i++ i++
} }
if (drawable != null && minuteIndex != null && hourIndex != null) { if (drawable != null && minuteIndex != null && hourIndex != null) {
drawable.setTint(fgColor) drawable.setTint(colors.foreground)
return ClockDynamicLauncherIcon( return ClockDynamicLauncherIcon(
foreground = drawable, foreground = drawable,
background = ColorDrawable(bgColor), background = ColorDrawable(colors.background),
foregroundScale = 1.5f, foregroundScale = 1.5f,
backgroundScale = 1f, backgroundScale = 1f,
hourLayer = hourIndex, hourLayer = hourIndex,
@ -129,12 +111,12 @@ class ThemedIconProvider(
if (array.length() != 31) return null if (array.length() != 31) return null
return ThemedCalendarDynamicLauncherIcon( return ThemedCalendarDynamicLauncherIcon(
background = ColorDrawable(bgColor), background = ColorDrawable(colors.foreground),
packageName = iconProviderPackage, packageName = iconProviderPackage,
foregroundIds = IntArray(31) { foregroundIds = IntArray(31) {
array.getResourceId(it, 0).takeIf { it != 0 } ?: return null array.getResourceId(it, 0).takeIf { it != 0 } ?: return null
}, },
foregroundTint = fgColor, foregroundTint = colors.background,
foregroundScale = 0.5f, foregroundScale = 0.5f,
) )

View File

@ -5,40 +5,19 @@ import android.content.res.Configuration
import android.util.TypedValue import android.util.TypedValue
import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.icons.R import de.mm20.launcher2.icons.R
import de.mm20.launcher2.icons.ThemeColors
import de.mm20.launcher2.search.data.Searchable import de.mm20.launcher2.search.data.Searchable
class ThemedPlaceholderIconProvider( internal class ThemedPlaceholderIconProvider(
val context: Context private val context: Context,
private val colors: ThemeColors,
) : IconProvider { ) : IconProvider {
private val fgColor: Int
private val bgColor: Int
init {
val theme = context.resources.newTheme()
theme.applyStyle(R.style.DefaultColors, true)
val typedValue = TypedValue()
val isDarkMode =
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES != 0
val bgAttr = R.attr.colorPrimaryContainer
val fgAttr = R.attr.colorOnPrimaryContainer
bgColor = theme.resolveAttribute(bgAttr, typedValue, true).let {
typedValue.data
}
fgColor = theme.resolveAttribute(fgAttr, typedValue, true).let {
typedValue.data
}
}
override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon { override suspend fun getIcon(searchable: Searchable, size: Int): LauncherIcon {
val icon = searchable.getPlaceholderIcon(context) val icon = searchable.getPlaceholderIcon(context)
icon.foreground.setTint(fgColor) icon.foreground.setTint(colors.foreground)
icon.background?.setTint(bgColor) icon.background?.setTint(colors.background)
return icon return icon
} }

View File

@ -32,13 +32,13 @@ class LauncherActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val iconRepository: IconRepository by inject()
iconRepository.recreate()
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
viewModel.setDarkMode(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) viewModel.setDarkMode(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES)
val iconRepository: IconRepository by inject()
iconRepository.applyTheme(theme)
binding = ActivityLauncherBinding.inflate(LayoutInflater.from(this)) binding = ActivityLauncherBinding.inflate(LayoutInflater.from(this))
setContentView(binding.root) setContentView(binding.root)