Themed icons: correctly apply application theme colors
This commit is contained in:
parent
bfdc2608fc
commit
667ec0f932
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user