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.Intent
import android.content.IntentFilter
import android.content.res.Resources
import android.util.LruCache
import android.util.TypedValue
import de.mm20.launcher2.icons.providers.*
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
@ -29,6 +31,8 @@ class IconRepository(
private val cache = LruCache<String, LauncherIcon>(200)
private val themeColors = MutableStateFlow(ThemeColors())
private var iconProviders: MutableStateFlow<List<IconProvider>> = MutableStateFlow(listOf())
private lateinit var placeholderProvider: IconProvider
@ -44,8 +48,31 @@ class IconRepository(
})
scope.launch {
dataStore.data.map { it.icons }.distinctUntilChanged().collectLatest {
recreate(it)
dataStore.data.map { it.icons }.distinctUntilChanged().collectLatest { settings ->
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() {
scope.launch {
@ -100,34 +120,19 @@ class IconRepository(
return iconPackManager.getInstalledIconPacks()
}
fun recreate() {
scope.launch {
recreate(dataStore.data.map { it.icons }.first())
fun applyTheme(theme: Resources.Theme) {
val typedValue = TypedValue()
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) {
val placeholderProvider = if (settings.themedIcons) {
ThemedPlaceholderIconProvider(context)
} 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
}
}
internal data class ThemeColors(
val foreground: Int = 0xFFFFFFFF.toInt(),
val background: Int = 0xFF000000.toInt(),
)

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.Searchable
class ThemedIconProvider(
private val context: Context
internal class ThemedIconProvider(
private val context: Context,
private val colors: ThemeColors,
) : 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? {
if (searchable !is Application) return null
@ -67,11 +49,11 @@ class ThemedIconProvider(
private fun getStaticIcon(resources: Resources, resId: Int): LauncherIcon? {
try {
val fg = ResourcesCompat.getDrawable(resources, resId, null) ?: return null
fg.setTint(fgColor)
fg.setTint(colors.foreground)
return LauncherIcon(
foreground = fg,
foregroundScale = 0.5f,
background = ColorDrawable(bgColor)
background = ColorDrawable(colors.background)
)
} catch (e: Resources.NotFoundException) {
return null
@ -103,10 +85,10 @@ class ThemedIconProvider(
i++
}
if (drawable != null && minuteIndex != null && hourIndex != null) {
drawable.setTint(fgColor)
drawable.setTint(colors.foreground)
return ClockDynamicLauncherIcon(
foreground = drawable,
background = ColorDrawable(bgColor),
background = ColorDrawable(colors.background),
foregroundScale = 1.5f,
backgroundScale = 1f,
hourLayer = hourIndex,
@ -129,12 +111,12 @@ class ThemedIconProvider(
if (array.length() != 31) return null
return ThemedCalendarDynamicLauncherIcon(
background = ColorDrawable(bgColor),
background = ColorDrawable(colors.foreground),
packageName = iconProviderPackage,
foregroundIds = IntArray(31) {
array.getResourceId(it, 0).takeIf { it != 0 } ?: return null
},
foregroundTint = fgColor,
foregroundTint = colors.background,
foregroundScale = 0.5f,
)

View File

@ -5,40 +5,19 @@ import android.content.res.Configuration
import android.util.TypedValue
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.icons.R
import de.mm20.launcher2.icons.ThemeColors
import de.mm20.launcher2.search.data.Searchable
class ThemedPlaceholderIconProvider(
val context: Context
internal class ThemedPlaceholderIconProvider(
private val context: Context,
private val colors: ThemeColors,
) : 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 {
val icon = searchable.getPlaceholderIcon(context)
icon.foreground.setTint(fgColor)
icon.background?.setTint(bgColor)
icon.foreground.setTint(colors.foreground)
icon.background?.setTint(colors.background)
return icon
}

View File

@ -32,13 +32,13 @@ class LauncherActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val iconRepository: IconRepository by inject()
iconRepository.recreate()
WindowCompat.setDecorFitsSystemWindows(window, false)
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))
setContentView(binding.root)