From b1f6b60e723993150c74e92415454ebce5a7d4d3 Mon Sep 17 00:00:00 2001 From: Ashinch Date: Sat, 7 May 2022 00:39:38 +0800 Subject: [PATCH] Replace the first palette with the color scheme provided by the system (#48) * Replace the first palette with the color scheme provided by the system * Clean up the code --- .../me/ash/reader/ui/page/home/FilterBar.kt | 15 +- .../ui/page/settings/color/ColorAndStyle.kt | 1 - .../main/java/me/ash/reader/ui/theme/Theme.kt | 5 +- .../ui/theme/palette/DynamicTonalPalette.kt | 2 + .../reader/ui/theme/palette/TonalPalettes.kt | 147 +++++++++++++++++- .../theme/palette/dynamic/WallpaperColors.kt | 38 +++-- 6 files changed, 177 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt index b4ed1ea..06f47d3 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt @@ -1,5 +1,6 @@ package me.ash.reader.ui.page.home +import android.os.Build import android.view.SoundEffectConstants import androidx.compose.foundation.background import androidx.compose.foundation.layout.Spacer @@ -13,6 +14,7 @@ import androidx.compose.ui.unit.Dp import com.google.accompanist.pager.ExperimentalPagerApi import me.ash.reader.data.entity.Filter import me.ash.reader.data.preference.FlowFilterBarStylePreference +import me.ash.reader.data.preference.LocalThemeIndex import me.ash.reader.ui.ext.getName import me.ash.reader.ui.ext.surfaceColorAtElevation import me.ash.reader.ui.theme.palette.onDark @@ -29,6 +31,7 @@ fun FilterBar( filterOnClick: (Filter) -> Unit = {}, ) { val view = LocalView.current + val themeIndex = LocalThemeIndex.current NavigationBar( modifier = Modifier @@ -77,12 +80,12 @@ fun FilterBar( filterOnClick(item) }, colors = NavigationBarItemDefaults.colors( -// selectedIconColor = MaterialTheme.colorScheme.onSecondaryContainer alwaysLight true, -// unselectedIconColor = MaterialTheme.colorScheme.outline, -// selectedTextColor = MaterialTheme.colorScheme.onSurface alwaysLight true, -// unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant, - indicatorColor = MaterialTheme.colorScheme.primaryContainer onDark MaterialTheme.colorScheme.secondaryContainer, - ) + indicatorColor = if (themeIndex == 5 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + MaterialTheme.colorScheme.secondaryContainer + } else { + MaterialTheme.colorScheme.primaryContainer + } onDark MaterialTheme.colorScheme.secondaryContainer, + ), ) } Spacer(modifier = Modifier.width(filterBarPadding)) diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt index e958d23..012a448 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt @@ -204,7 +204,6 @@ fun ColorAndStyle( ) } -@SuppressLint("FlowOperatorInvokedInComposition") @Composable fun Palettes( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/theme/Theme.kt b/app/src/main/java/me/ash/reader/ui/theme/Theme.kt index cdb48dd..f09a07d 100644 --- a/app/src/main/java/me/ash/reader/ui/theme/Theme.kt +++ b/app/src/main/java/me/ash/reader/ui/theme/Theme.kt @@ -1,6 +1,5 @@ package me.ash.reader.ui.theme -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -14,7 +13,7 @@ import me.ash.reader.ui.theme.palette.dynamicLightColorScheme @Composable fun AppTheme( - useDarkTheme: Boolean = isSystemInDarkTheme(), + useDarkTheme: Boolean, wallpaperPalettes: List = extractTonalPalettesFromUserWallpaper(), content: @Composable () -> Unit ) { @@ -34,7 +33,7 @@ fun AppTheme( ProvideZcamViewingConditions { CompositionLocalProvider( - LocalTonalPalettes provides tonalPalettes.also { it.Preheating() }, + LocalTonalPalettes provides tonalPalettes.apply { Preheating() }, ) { MaterialTheme( colorScheme = diff --git a/app/src/main/java/me/ash/reader/ui/theme/palette/DynamicTonalPalette.kt b/app/src/main/java/me/ash/reader/ui/theme/palette/DynamicTonalPalette.kt index 4ea8945..a266317 100644 --- a/app/src/main/java/me/ash/reader/ui/theme/palette/DynamicTonalPalette.kt +++ b/app/src/main/java/me/ash/reader/ui/theme/palette/DynamicTonalPalette.kt @@ -5,6 +5,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import androidx.compose.ui.graphics.Color import me.ash.reader.data.preference.LocalAmoledDarkTheme import me.ash.reader.data.preference.LocalDarkTheme @@ -127,6 +128,7 @@ fun String.checkColorHex(): String? { return "[0-9a-fA-F]{6}".toRegex().find(s)?.value } +@Stable fun String.safeHexToColor(): Color = try { Color(java.lang.Long.parseLong(this, 16)) diff --git a/app/src/main/java/me/ash/reader/ui/theme/palette/TonalPalettes.kt b/app/src/main/java/me/ash/reader/ui/theme/palette/TonalPalettes.kt index f7cefb8..7b44a8a 100644 --- a/app/src/main/java/me/ash/reader/ui/theme/palette/TonalPalettes.kt +++ b/app/src/main/java/me/ash/reader/ui/theme/palette/TonalPalettes.kt @@ -3,11 +3,18 @@ * * @link https://github.com/Kyant0/MusicYou * @author Kyant0 + * @modifier Ashinch */ package me.ash.reader.ui.theme.palette +import android.content.Context +import android.os.Build +import androidx.annotation.ColorRes +import androidx.annotation.DoNotInline +import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.graphics.Color import me.ash.reader.ui.theme.palette.colorspace.cielab.CieLab @@ -21,6 +28,8 @@ typealias TonalValue = Int typealias TonalPalette = MutableMap +val tonalTokens = listOf(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100) + val LocalTonalPalettes = compositionLocalOf { TonalPalettes(238.36, 15.0) } @@ -97,20 +106,144 @@ data class TonalPalettes( @Composable fun Preheating() { - val tonalValues = listOf(0, 10, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 95, 98, 99, 100) - tonalValues.forEach { primary(it) } - tonalValues.forEach { secondary(it) } - tonalValues.forEach { tertiary(it) } - tonalValues.forEach { neutral(it) } - tonalValues.forEach { neutralVariant(it) } - tonalValues.forEach { error(it) } + tonalTokens.forEach { primary(it) } + tonalTokens.forEach { secondary(it) } + tonalTokens.forEach { tertiary(it) } + tonalTokens.forEach { neutral(it) } + tonalTokens.forEach { neutralVariant(it) } + tonalTokens.forEach { error(it) } + } + + @RequiresApi(Build.VERSION_CODES.S) + private fun primarySystem(context: Context, tone: TonalValue): Color = when (tone) { + 0 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_1000) + 10 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_900) + 20 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_800) + 30 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_700) + 40 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_600) + 50 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_500) + 60 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_400) + 70 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_300) + 80 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_200) + 90 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_100) + 95 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_50) + 99 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_10) + 100 -> ColorResourceHelper.getColor(context, android.R.color.system_accent1_0) + else -> throw IllegalArgumentException("Unknown primary tone: $tone") + } + + @RequiresApi(Build.VERSION_CODES.S) + private fun secondarySystem(context: Context, tone: TonalValue): Color = when (tone) { + 0 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_1000) + 10 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_900) + 20 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_800) + 30 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_700) + 40 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_600) + 50 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_500) + 60 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_400) + 70 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_300) + 80 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_200) + 90 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_100) + 95 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_50) + 99 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_10) + 100 -> ColorResourceHelper.getColor(context, android.R.color.system_accent2_0) + else -> throw IllegalArgumentException("Unknown secondary tone: $tone") + } + + @RequiresApi(Build.VERSION_CODES.S) + private fun tertiarySystem(context: Context, tone: TonalValue): Color = when (tone) { + 0 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_1000) + 10 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_900) + 20 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_800) + 30 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_700) + 40 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_600) + 50 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_500) + 60 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_400) + 70 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_300) + 80 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_200) + 90 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_100) + 95 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_50) + 99 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_10) + 100 -> ColorResourceHelper.getColor(context, android.R.color.system_accent3_0) + else -> throw IllegalArgumentException("Unknown tertiary tone: $tone") + } + + @RequiresApi(Build.VERSION_CODES.S) + private fun neutralSystem(context: Context, tone: TonalValue): Color = when (tone) { + 0 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_1000) + 10 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_900) + 20 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_800) + 30 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_700) + 40 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_600) + 50 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_500) + 60 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_400) + 70 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_300) + 80 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_200) + 90 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_100) + 95 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_50) + 99 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_10) + 100 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral1_0) + else -> throw IllegalArgumentException("Unknown neutral tone: $tone") + } + + @RequiresApi(Build.VERSION_CODES.S) + private fun neutralVariantSystem(context: Context, tone: TonalValue): Color = when (tone) { + 0 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_1000) + 10 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_900) + 20 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_800) + 30 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_700) + 40 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_600) + 50 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_500) + 60 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_400) + 70 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_300) + 80 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_200) + 90 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_100) + 95 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_50) + 99 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_10) + 100 -> ColorResourceHelper.getColor(context, android.R.color.system_neutral2_0) + else -> throw IllegalArgumentException("Unknown neutral variant tone: $tone") } companion object { @Composable + @Stable fun Color.toTonalPalettes(): TonalPalettes { val zcam = toRgb().toZcam() return TonalPalettes(hue = zcam.hz, primaryChroma = zcam.Cz) } + + @RequiresApi(Build.VERSION_CODES.S) + @Composable + @Stable + fun Context.getSystemTonalPalettes(): TonalPalettes { + val tonalPalettes = LocalTonalPalettes.current + tonalTokens.forEach { + tonalPalettes.primary[it] = tonalPalettes.primarySystem(this, it) + } + tonalTokens.forEach { + tonalPalettes.secondary[it] = tonalPalettes.secondarySystem(this, it) + } + tonalTokens.forEach { + tonalPalettes.tertiary[it] = tonalPalettes.tertiarySystem(this, it) + } + tonalTokens.forEach { + tonalPalettes.neutral[it] = tonalPalettes.neutralSystem(this, it) + } + tonalTokens.forEach { + tonalPalettes.neutralVariant[it] = tonalPalettes.neutralVariantSystem(this, it) + } + tonalTokens.forEach { + tonalPalettes error it + } + return tonalPalettes + } } } + +@RequiresApi(23) +object ColorResourceHelper { + @DoNotInline + fun getColor(context: Context, @ColorRes id: Int): Color { + return Color(context.resources.getColor(id, context.theme)) + } +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/theme/palette/dynamic/WallpaperColors.kt b/app/src/main/java/me/ash/reader/ui/theme/palette/dynamic/WallpaperColors.kt index e771f04..f7cbf9b 100644 --- a/app/src/main/java/me/ash/reader/ui/theme/palette/dynamic/WallpaperColors.kt +++ b/app/src/main/java/me/ash/reader/ui/theme/palette/dynamic/WallpaperColors.kt @@ -8,43 +8,53 @@ package me.ash.reader.ui.theme.palette.dynamic -import android.annotation.SuppressLint import android.app.WallpaperManager import android.os.Build import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.Stable import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView -import kotlinx.coroutines.flow.map -import me.ash.reader.ui.ext.DataStoreKeys -import me.ash.reader.ui.ext.dataStore +import me.ash.reader.data.preference.LocalCustomPrimaryColor import me.ash.reader.ui.theme.palette.TonalPalettes +import me.ash.reader.ui.theme.palette.TonalPalettes.Companion.getSystemTonalPalettes import me.ash.reader.ui.theme.palette.TonalPalettes.Companion.toTonalPalettes import me.ash.reader.ui.theme.palette.safeHexToColor -@SuppressLint("FlowOperatorInvokedInComposition") +object PresetColor { + val blue = Color(0xFF80BBFF) + val pink = Color(0xFFFFD8E4) + val purple = Color(0xFF62539f) + val yellow = Color(0xFFE9B666) +} + @Composable +@Stable fun extractTonalPalettesFromUserWallpaper(): List { val context = LocalContext.current - val customPrimaryColor = - context.dataStore.data.map { it[DataStoreKeys.CustomPrimaryColor.key] ?: "" } - .collectAsState(initial = "").value + val customPrimaryColor = LocalCustomPrimaryColor.current val preset = mutableListOf( - Color(0xFF80BBFF).toTonalPalettes(), - Color(0xFFFFD8E4).toTonalPalettes(), - Color(0xFF62539f).toTonalPalettes(), - Color(0xFFE9B666).toTonalPalettes(), + PresetColor.blue.toTonalPalettes(), + PresetColor.pink.toTonalPalettes(), + PresetColor.purple.toTonalPalettes(), + PresetColor.yellow.toTonalPalettes(), customPrimaryColor.safeHexToColor().toTonalPalettes() ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && !LocalView.current.isInEditMode) { val colors = WallpaperManager.getInstance(LocalContext.current) .getWallpaperColors(WallpaperManager.FLAG_SYSTEM) val primary = colors?.primaryColor?.toArgb() val secondary = colors?.secondaryColor?.toArgb() val tertiary = colors?.tertiaryColor?.toArgb() - if (primary != null) preset.add(Color(primary).toTonalPalettes()) + if (primary != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + preset.add(context.getSystemTonalPalettes()) + } else { + preset.add(Color(primary).toTonalPalettes()) + } + } if (secondary != null) preset.add(Color(secondary).toTonalPalettes()) if (tertiary != null) preset.add(Color(tertiary).toTonalPalettes()) }