Merge pull request #47 from Ashinch/feature/dark-theme
Add dark theme settings
This commit is contained in:
commit
4d2d857676
|
@ -0,0 +1,41 @@
|
||||||
|
package me.ash.reader.data.preference
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import me.ash.reader.ui.ext.DataStoreKeys
|
||||||
|
import me.ash.reader.ui.ext.dataStore
|
||||||
|
import me.ash.reader.ui.ext.put
|
||||||
|
|
||||||
|
sealed class AmoledDarkThemePreference(val value: Boolean) : Preference() {
|
||||||
|
object ON : AmoledDarkThemePreference(true)
|
||||||
|
object OFF : AmoledDarkThemePreference(false)
|
||||||
|
|
||||||
|
override fun put(context: Context, scope: CoroutineScope) {
|
||||||
|
scope.launch {
|
||||||
|
context.dataStore.put(
|
||||||
|
DataStoreKeys.AmoledDarkTheme,
|
||||||
|
value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val default = OFF
|
||||||
|
val values = listOf(ON, OFF)
|
||||||
|
|
||||||
|
fun fromPreferences(preferences: Preferences) =
|
||||||
|
when (preferences[DataStoreKeys.AmoledDarkTheme.key]) {
|
||||||
|
true -> ON
|
||||||
|
false -> OFF
|
||||||
|
else -> default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun AmoledDarkThemePreference.not(): AmoledDarkThemePreference =
|
||||||
|
when (value) {
|
||||||
|
true -> AmoledDarkThemePreference.OFF
|
||||||
|
false -> AmoledDarkThemePreference.ON
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package me.ash.reader.data.preference
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import me.ash.reader.R
|
||||||
|
import me.ash.reader.ui.ext.DataStoreKeys
|
||||||
|
import me.ash.reader.ui.ext.dataStore
|
||||||
|
import me.ash.reader.ui.ext.put
|
||||||
|
|
||||||
|
sealed class DarkThemePreference(val value: Int) : Preference() {
|
||||||
|
object UseDeviceTheme : DarkThemePreference(0)
|
||||||
|
object ON : DarkThemePreference(1)
|
||||||
|
object OFF : DarkThemePreference(2)
|
||||||
|
|
||||||
|
override fun put(context: Context, scope: CoroutineScope) {
|
||||||
|
scope.launch {
|
||||||
|
context.dataStore.put(
|
||||||
|
DataStoreKeys.DarkTheme,
|
||||||
|
value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDesc(context: Context): String =
|
||||||
|
when (this) {
|
||||||
|
UseDeviceTheme -> context.getString(R.string.use_device_theme)
|
||||||
|
ON -> context.getString(R.string.on)
|
||||||
|
OFF -> context.getString(R.string.off)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun isDarkTheme(): Boolean = when (this) {
|
||||||
|
UseDeviceTheme -> isSystemInDarkTheme()
|
||||||
|
ON -> true
|
||||||
|
OFF -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val default = UseDeviceTheme
|
||||||
|
val values = listOf(UseDeviceTheme, ON, OFF)
|
||||||
|
|
||||||
|
fun fromPreferences(preferences: Preferences) =
|
||||||
|
when (preferences[DataStoreKeys.DarkTheme.key]) {
|
||||||
|
0 -> UseDeviceTheme
|
||||||
|
1 -> ON
|
||||||
|
2 -> OFF
|
||||||
|
else -> default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
operator fun DarkThemePreference.not(): DarkThemePreference =
|
||||||
|
when (this) {
|
||||||
|
DarkThemePreference.UseDeviceTheme -> if (isSystemInDarkTheme()) {
|
||||||
|
DarkThemePreference.OFF
|
||||||
|
} else {
|
||||||
|
DarkThemePreference.ON
|
||||||
|
}
|
||||||
|
DarkThemePreference.ON -> DarkThemePreference.OFF
|
||||||
|
DarkThemePreference.OFF -> DarkThemePreference.ON
|
||||||
|
}
|
|
@ -14,6 +14,8 @@ import me.ash.reader.ui.ext.dataStore
|
||||||
data class Settings(
|
data class Settings(
|
||||||
val themeIndex: Int = ThemeIndexPreference.default,
|
val themeIndex: Int = ThemeIndexPreference.default,
|
||||||
val customPrimaryColor: String = CustomPrimaryColorPreference.default,
|
val customPrimaryColor: String = CustomPrimaryColorPreference.default,
|
||||||
|
val darkTheme: DarkThemePreference = DarkThemePreference.default,
|
||||||
|
val amoledDarkTheme: AmoledDarkThemePreference = AmoledDarkThemePreference.default,
|
||||||
|
|
||||||
val feedsFilterBarStyle: FeedsFilterBarStylePreference = FeedsFilterBarStylePreference.default,
|
val feedsFilterBarStyle: FeedsFilterBarStylePreference = FeedsFilterBarStylePreference.default,
|
||||||
val feedsFilterBarFilled: FeedsFilterBarFilledPreference = FeedsFilterBarFilledPreference.default,
|
val feedsFilterBarFilled: FeedsFilterBarFilledPreference = FeedsFilterBarFilledPreference.default,
|
||||||
|
@ -41,6 +43,8 @@ fun Preferences.toSettings(): Settings {
|
||||||
return Settings(
|
return Settings(
|
||||||
themeIndex = ThemeIndexPreference.fromPreferences(this),
|
themeIndex = ThemeIndexPreference.fromPreferences(this),
|
||||||
customPrimaryColor = CustomPrimaryColorPreference.fromPreferences(this),
|
customPrimaryColor = CustomPrimaryColorPreference.fromPreferences(this),
|
||||||
|
darkTheme = DarkThemePreference.fromPreferences(this),
|
||||||
|
amoledDarkTheme = AmoledDarkThemePreference.fromPreferences(this),
|
||||||
|
|
||||||
feedsFilterBarStyle = FeedsFilterBarStylePreference.fromPreferences(this),
|
feedsFilterBarStyle = FeedsFilterBarStylePreference.fromPreferences(this),
|
||||||
feedsFilterBarFilled = FeedsFilterBarFilledPreference.fromPreferences(this),
|
feedsFilterBarFilled = FeedsFilterBarFilledPreference.fromPreferences(this),
|
||||||
|
@ -60,7 +64,9 @@ fun Preferences.toSettings(): Settings {
|
||||||
flowArticleListImage = FlowArticleListImagePreference.fromPreferences(this),
|
flowArticleListImage = FlowArticleListImagePreference.fromPreferences(this),
|
||||||
flowArticleListDesc = FlowArticleListDescPreference.fromPreferences(this),
|
flowArticleListDesc = FlowArticleListDescPreference.fromPreferences(this),
|
||||||
flowArticleListTime = FlowArticleListTimePreference.fromPreferences(this),
|
flowArticleListTime = FlowArticleListTimePreference.fromPreferences(this),
|
||||||
flowArticleListDateStickyHeader = FlowArticleListDateStickyHeaderPreference.fromPreferences(this),
|
flowArticleListDateStickyHeader = FlowArticleListDateStickyHeaderPreference.fromPreferences(
|
||||||
|
this
|
||||||
|
),
|
||||||
flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this),
|
flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -80,6 +86,8 @@ fun SettingsProvider(
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalThemeIndex provides settings.themeIndex,
|
LocalThemeIndex provides settings.themeIndex,
|
||||||
LocalCustomPrimaryColor provides settings.customPrimaryColor,
|
LocalCustomPrimaryColor provides settings.customPrimaryColor,
|
||||||
|
LocalDarkTheme provides settings.darkTheme,
|
||||||
|
LocalAmoledDarkTheme provides settings.amoledDarkTheme,
|
||||||
|
|
||||||
LocalFeedsTopBarTonalElevation provides settings.feedsTopBarTonalElevation,
|
LocalFeedsTopBarTonalElevation provides settings.feedsTopBarTonalElevation,
|
||||||
LocalFeedsGroupListExpand provides settings.feedsGroupListExpand,
|
LocalFeedsGroupListExpand provides settings.feedsGroupListExpand,
|
||||||
|
@ -110,6 +118,10 @@ val LocalThemeIndex =
|
||||||
compositionLocalOf { ThemeIndexPreference.default }
|
compositionLocalOf { ThemeIndexPreference.default }
|
||||||
val LocalCustomPrimaryColor =
|
val LocalCustomPrimaryColor =
|
||||||
compositionLocalOf { CustomPrimaryColorPreference.default }
|
compositionLocalOf { CustomPrimaryColorPreference.default }
|
||||||
|
val LocalDarkTheme =
|
||||||
|
compositionLocalOf<DarkThemePreference> { DarkThemePreference.default }
|
||||||
|
val LocalAmoledDarkTheme =
|
||||||
|
compositionLocalOf<AmoledDarkThemePreference> { AmoledDarkThemePreference.default }
|
||||||
|
|
||||||
val LocalFeedsFilterBarStyle =
|
val LocalFeedsFilterBarStyle =
|
||||||
compositionLocalOf<FeedsFilterBarStylePreference> { FeedsFilterBarStylePreference.default }
|
compositionLocalOf<FeedsFilterBarStylePreference> { FeedsFilterBarStylePreference.default }
|
||||||
|
|
|
@ -13,8 +13,8 @@ import coil.compose.AsyncImage
|
||||||
import coil.imageLoader
|
import coil.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.caverock.androidsvg.SVG
|
import com.caverock.androidsvg.SVG
|
||||||
|
import me.ash.reader.data.preference.LocalDarkTheme
|
||||||
import me.ash.reader.ui.svg.parseDynamicColor
|
import me.ash.reader.ui.svg.parseDynamicColor
|
||||||
import me.ash.reader.ui.theme.LocalUseDarkTheme
|
|
||||||
import me.ash.reader.ui.theme.palette.LocalTonalPalettes
|
import me.ash.reader.ui.theme.palette.LocalTonalPalettes
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -24,10 +24,10 @@ fun DynamicSVGImage(
|
||||||
contentDescription: String,
|
contentDescription: String,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val useDarkTheme = LocalUseDarkTheme.current
|
val useDarkTheme = LocalDarkTheme.current.isDarkTheme()
|
||||||
val tonalPalettes = LocalTonalPalettes.current
|
val tonalPalettes = LocalTonalPalettes.current
|
||||||
var size by remember { mutableStateOf(IntSize.Zero) }
|
var size by remember { mutableStateOf(IntSize.Zero) }
|
||||||
val pic by remember(tonalPalettes, size) {
|
val pic by remember(useDarkTheme, tonalPalettes, size) {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
PictureDrawable(
|
PictureDrawable(
|
||||||
SVG.getFromString(svgImageString.parseDynamicColor(tonalPalettes, useDarkTheme))
|
SVG.getFromString(svgImageString.parseDynamicColor(tonalPalettes, useDarkTheme))
|
||||||
|
|
|
@ -130,6 +130,16 @@ sealed class DataStoreKeys<T> {
|
||||||
get() = stringPreferencesKey("customPrimaryColor")
|
get() = stringPreferencesKey("customPrimaryColor")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object DarkTheme : DataStoreKeys<Int>() {
|
||||||
|
override val key: Preferences.Key<Int>
|
||||||
|
get() = intPreferencesKey("darkTheme")
|
||||||
|
}
|
||||||
|
|
||||||
|
object AmoledDarkTheme : DataStoreKeys<Boolean>() {
|
||||||
|
override val key: Preferences.Key<Boolean>
|
||||||
|
get() = booleanPreferencesKey("amoledDarkTheme")
|
||||||
|
}
|
||||||
|
|
||||||
object FeedsFilterBarStyle : DataStoreKeys<Int>() {
|
object FeedsFilterBarStyle : DataStoreKeys<Int>() {
|
||||||
override val key: Preferences.Key<Int>
|
override val key: Preferences.Key<Int>
|
||||||
get() = intPreferencesKey("feedsFilterBarStyle")
|
get() = intPreferencesKey("feedsFilterBarStyle")
|
||||||
|
|
|
@ -14,6 +14,7 @@ import com.google.accompanist.navigation.animation.AnimatedNavHost
|
||||||
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import me.ash.reader.data.entity.Filter
|
import me.ash.reader.data.entity.Filter
|
||||||
|
import me.ash.reader.data.preference.LocalDarkTheme
|
||||||
import me.ash.reader.ui.ext.*
|
import me.ash.reader.ui.ext.*
|
||||||
import me.ash.reader.ui.page.home.HomeViewAction
|
import me.ash.reader.ui.page.home.HomeViewAction
|
||||||
import me.ash.reader.ui.page.home.HomeViewModel
|
import me.ash.reader.ui.page.home.HomeViewModel
|
||||||
|
@ -22,13 +23,13 @@ import me.ash.reader.ui.page.home.flow.FlowPage
|
||||||
import me.ash.reader.ui.page.home.read.ReadPage
|
import me.ash.reader.ui.page.home.read.ReadPage
|
||||||
import me.ash.reader.ui.page.settings.SettingsPage
|
import me.ash.reader.ui.page.settings.SettingsPage
|
||||||
import me.ash.reader.ui.page.settings.color.ColorAndStyle
|
import me.ash.reader.ui.page.settings.color.ColorAndStyle
|
||||||
|
import me.ash.reader.ui.page.settings.color.DarkTheme
|
||||||
import me.ash.reader.ui.page.settings.color.feeds.FeedsPageStyle
|
import me.ash.reader.ui.page.settings.color.feeds.FeedsPageStyle
|
||||||
import me.ash.reader.ui.page.settings.color.flow.FlowPageStyle
|
import me.ash.reader.ui.page.settings.color.flow.FlowPageStyle
|
||||||
import me.ash.reader.ui.page.settings.interaction.Interaction
|
import me.ash.reader.ui.page.settings.interaction.Interaction
|
||||||
import me.ash.reader.ui.page.settings.tips.TipsAndSupport
|
import me.ash.reader.ui.page.settings.tips.TipsAndSupport
|
||||||
import me.ash.reader.ui.page.startup.StartupPage
|
import me.ash.reader.ui.page.startup.StartupPage
|
||||||
import me.ash.reader.ui.theme.AppTheme
|
import me.ash.reader.ui.theme.AppTheme
|
||||||
import me.ash.reader.ui.theme.LocalUseDarkTheme
|
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class, androidx.compose.material.ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalAnimationApi::class, androidx.compose.material.ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -86,8 +87,9 @@ fun HomeEntry(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AppTheme {
|
val useDarkTheme = LocalDarkTheme.current.isDarkTheme()
|
||||||
val useDarkTheme = LocalUseDarkTheme.current
|
|
||||||
|
AppTheme(useDarkTheme = useDarkTheme) {
|
||||||
|
|
||||||
rememberSystemUiController().run {
|
rememberSystemUiController().run {
|
||||||
setStatusBarColor(Color.Transparent, !useDarkTheme)
|
setStatusBarColor(Color.Transparent, !useDarkTheme)
|
||||||
|
@ -129,6 +131,9 @@ fun HomeEntry(
|
||||||
animatedComposable(route = RouteName.COLOR_AND_STYLE) {
|
animatedComposable(route = RouteName.COLOR_AND_STYLE) {
|
||||||
ColorAndStyle(navController)
|
ColorAndStyle(navController)
|
||||||
}
|
}
|
||||||
|
animatedComposable(route = RouteName.DARK_THEME) {
|
||||||
|
DarkTheme(navController)
|
||||||
|
}
|
||||||
animatedComposable(route = RouteName.FEEDS_PAGE_STYLE) {
|
animatedComposable(route = RouteName.FEEDS_PAGE_STYLE) {
|
||||||
FeedsPageStyle(navController)
|
FeedsPageStyle(navController)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ object RouteName {
|
||||||
|
|
||||||
// Color & Style
|
// Color & Style
|
||||||
const val COLOR_AND_STYLE = "color_and_style"
|
const val COLOR_AND_STYLE = "color_and_style"
|
||||||
|
const val DARK_THEME = "dark_theme"
|
||||||
const val FEEDS_PAGE_STYLE = "feeds_page_style"
|
const val FEEDS_PAGE_STYLE = "feeds_page_style"
|
||||||
const val FLOW_PAGE_STYLE = "flow_page_style"
|
const val FLOW_PAGE_STYLE = "flow_page_style"
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package me.ash.reader.ui.page.home.read
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
@ -77,7 +76,7 @@ fun ReadPage(
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.background(MaterialTheme.colorScheme.surface),
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
topBar = {},
|
topBar = {},
|
||||||
content = {
|
content = {
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
|
|
|
@ -19,6 +19,8 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import me.ash.reader.ui.theme.palette.LocalTonalPalettes
|
||||||
|
import me.ash.reader.ui.theme.palette.onDark
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingItem(
|
fun SettingItem(
|
||||||
|
@ -31,6 +33,8 @@ fun SettingItem(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
action: (@Composable () -> Unit)? = null
|
action: (@Composable () -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
|
val tonalPalettes = LocalTonalPalettes.current
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clickable { onClick() }
|
.clickable { onClick() }
|
||||||
|
@ -71,7 +75,8 @@ fun SettingItem(
|
||||||
Divider(
|
Divider(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 16.dp)
|
.padding(start = 16.dp)
|
||||||
.size(1.dp, 32.dp)
|
.size(1.dp, 32.dp),
|
||||||
|
color = tonalPalettes neutralVariant 80 onDark (tonalPalettes neutralVariant 30),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Box(Modifier.padding(start = 16.dp)) {
|
Box(Modifier.padding(start = 16.dp)) {
|
||||||
|
|
|
@ -26,16 +26,12 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.preference.CustomPrimaryColorPreference
|
import me.ash.reader.data.preference.*
|
||||||
import me.ash.reader.data.preference.LocalCustomPrimaryColor
|
|
||||||
import me.ash.reader.data.preference.LocalThemeIndex
|
|
||||||
import me.ash.reader.data.preference.ThemeIndexPreference
|
|
||||||
import me.ash.reader.ui.component.*
|
import me.ash.reader.ui.component.*
|
||||||
import me.ash.reader.ui.page.common.RouteName
|
import me.ash.reader.ui.page.common.RouteName
|
||||||
import me.ash.reader.ui.page.settings.SettingItem
|
import me.ash.reader.ui.page.settings.SettingItem
|
||||||
import me.ash.reader.ui.svg.PALETTE
|
import me.ash.reader.ui.svg.PALETTE
|
||||||
import me.ash.reader.ui.svg.SVGString
|
import me.ash.reader.ui.svg.SVGString
|
||||||
import me.ash.reader.ui.theme.LocalUseDarkTheme
|
|
||||||
import me.ash.reader.ui.theme.palette.*
|
import me.ash.reader.ui.theme.palette.*
|
||||||
import me.ash.reader.ui.theme.palette.TonalPalettes.Companion.toTonalPalettes
|
import me.ash.reader.ui.theme.palette.TonalPalettes.Companion.toTonalPalettes
|
||||||
import me.ash.reader.ui.theme.palette.dynamic.extractTonalPalettesFromUserWallpaper
|
import me.ash.reader.ui.theme.palette.dynamic.extractTonalPalettesFromUserWallpaper
|
||||||
|
@ -47,9 +43,11 @@ fun ColorAndStyle(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val useDarkTheme = LocalUseDarkTheme.current
|
val darkTheme = LocalDarkTheme.current
|
||||||
|
val darkThemeNot = !darkTheme
|
||||||
val themeIndex = LocalThemeIndex.current
|
val themeIndex = LocalThemeIndex.current
|
||||||
val customPrimaryColor = LocalCustomPrimaryColor.current
|
val customPrimaryColor = LocalCustomPrimaryColor.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val wallpaperTonalPalettes = extractTonalPalettesFromUserWallpaper()
|
val wallpaperTonalPalettes = extractTonalPalettesFromUserWallpaper()
|
||||||
var radioButtonSelected by remember { mutableStateOf(if (themeIndex > 4) 0 else 1) }
|
var radioButtonSelected by remember { mutableStateOf(if (themeIndex > 4) 0 else 1) }
|
||||||
|
@ -151,12 +149,19 @@ fun ColorAndStyle(
|
||||||
)
|
)
|
||||||
SettingItem(
|
SettingItem(
|
||||||
title = stringResource(R.string.dark_theme),
|
title = stringResource(R.string.dark_theme),
|
||||||
desc = stringResource(R.string.use_device_theme),
|
desc = darkTheme.getDesc(context),
|
||||||
enable = false,
|
|
||||||
separatedActions = true,
|
separatedActions = true,
|
||||||
onClick = {},
|
onClick = {
|
||||||
|
navController.navigate(RouteName.DARK_THEME) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
Switch(activated = useDarkTheme, enable = false)
|
Switch(
|
||||||
|
activated = darkTheme.isDarkTheme()
|
||||||
|
) {
|
||||||
|
darkThemeNot.put(context, scope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SettingItem(
|
SettingItem(
|
||||||
title = stringResource(R.string.basic_fonts),
|
title = stringResource(R.string.basic_fonts),
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
package me.ash.reader.ui.page.settings.color
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.ArrowBack
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import me.ash.reader.R
|
||||||
|
import me.ash.reader.data.preference.DarkThemePreference
|
||||||
|
import me.ash.reader.data.preference.LocalAmoledDarkTheme
|
||||||
|
import me.ash.reader.data.preference.LocalDarkTheme
|
||||||
|
import me.ash.reader.data.preference.not
|
||||||
|
import me.ash.reader.ui.component.DisplayText
|
||||||
|
import me.ash.reader.ui.component.FeedbackIconButton
|
||||||
|
import me.ash.reader.ui.component.Subtitle
|
||||||
|
import me.ash.reader.ui.component.Switch
|
||||||
|
import me.ash.reader.ui.page.settings.SettingItem
|
||||||
|
import me.ash.reader.ui.theme.palette.onLight
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun DarkTheme(
|
||||||
|
navController: NavHostController,
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val darkTheme = LocalDarkTheme.current
|
||||||
|
val amoledDarkTheme = LocalAmoledDarkTheme.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface)
|
||||||
|
.statusBarsPadding()
|
||||||
|
.navigationBarsPadding(),
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
|
||||||
|
topBar = {
|
||||||
|
SmallTopAppBar(
|
||||||
|
colors = TopAppBarDefaults.smallTopAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface
|
||||||
|
),
|
||||||
|
title = {},
|
||||||
|
navigationIcon = {
|
||||||
|
FeedbackIconButton(
|
||||||
|
imageVector = Icons.Rounded.ArrowBack,
|
||||||
|
contentDescription = stringResource(R.string.back),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface
|
||||||
|
) {
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
LazyColumn {
|
||||||
|
item {
|
||||||
|
DisplayText(text = stringResource(R.string.dark_theme), desc = "")
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
DarkThemePreference.values.map {
|
||||||
|
SettingItem(
|
||||||
|
title = it.getDesc(context),
|
||||||
|
onClick = {
|
||||||
|
it.put(context, scope)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
RadioButton(selected = it == darkTheme, onClick = {
|
||||||
|
it.put(context, scope)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Subtitle(
|
||||||
|
modifier = Modifier.padding(horizontal = 24.dp),
|
||||||
|
text = stringResource(R.string.other),
|
||||||
|
)
|
||||||
|
SettingItem(
|
||||||
|
title = stringResource(R.string.amoled_dark_theme),
|
||||||
|
onClick = {
|
||||||
|
(!amoledDarkTheme).put(context, scope)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Switch(activated = amoledDarkTheme.value) {
|
||||||
|
(!amoledDarkTheme).put(context, scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
|
||||||
import me.ash.reader.data.preference.LocalThemeIndex
|
import me.ash.reader.data.preference.LocalThemeIndex
|
||||||
import me.ash.reader.ui.theme.palette.LocalTonalPalettes
|
import me.ash.reader.ui.theme.palette.LocalTonalPalettes
|
||||||
import me.ash.reader.ui.theme.palette.TonalPalettes
|
import me.ash.reader.ui.theme.palette.TonalPalettes
|
||||||
|
@ -13,8 +12,6 @@ import me.ash.reader.ui.theme.palette.dynamic.extractTonalPalettesFromUserWallpa
|
||||||
import me.ash.reader.ui.theme.palette.dynamicDarkColorScheme
|
import me.ash.reader.ui.theme.palette.dynamicDarkColorScheme
|
||||||
import me.ash.reader.ui.theme.palette.dynamicLightColorScheme
|
import me.ash.reader.ui.theme.palette.dynamicLightColorScheme
|
||||||
|
|
||||||
val LocalUseDarkTheme = compositionLocalOf { false }
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppTheme(
|
fun AppTheme(
|
||||||
useDarkTheme: Boolean = isSystemInDarkTheme(),
|
useDarkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
|
@ -38,7 +35,6 @@ fun AppTheme(
|
||||||
ProvideZcamViewingConditions {
|
ProvideZcamViewingConditions {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalTonalPalettes provides tonalPalettes.also { it.Preheating() },
|
LocalTonalPalettes provides tonalPalettes.also { it.Preheating() },
|
||||||
LocalUseDarkTheme provides useDarkTheme,
|
|
||||||
) {
|
) {
|
||||||
MaterialTheme(
|
MaterialTheme(
|
||||||
colorScheme =
|
colorScheme =
|
||||||
|
|
|
@ -6,7 +6,8 @@ import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import me.ash.reader.ui.theme.LocalUseDarkTheme
|
import me.ash.reader.data.preference.LocalAmoledDarkTheme
|
||||||
|
import me.ash.reader.data.preference.LocalDarkTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun dynamicLightColorScheme(): ColorScheme {
|
fun dynamicLightColorScheme(): ColorScheme {
|
||||||
|
@ -41,6 +42,8 @@ fun dynamicLightColorScheme(): ColorScheme {
|
||||||
@Composable
|
@Composable
|
||||||
fun dynamicDarkColorScheme(): ColorScheme {
|
fun dynamicDarkColorScheme(): ColorScheme {
|
||||||
val palettes = LocalTonalPalettes.current
|
val palettes = LocalTonalPalettes.current
|
||||||
|
val amoledDarkTheme = LocalAmoledDarkTheme.current
|
||||||
|
|
||||||
return darkColorScheme(
|
return darkColorScheme(
|
||||||
primary = palettes primary 80,
|
primary = palettes primary 80,
|
||||||
onPrimary = palettes primary 20,
|
onPrimary = palettes primary 20,
|
||||||
|
@ -57,7 +60,7 @@ fun dynamicDarkColorScheme(): ColorScheme {
|
||||||
onTertiaryContainer = palettes tertiary 90,
|
onTertiaryContainer = palettes tertiary 90,
|
||||||
background = palettes neutral 10,
|
background = palettes neutral 10,
|
||||||
onBackground = palettes neutral 90,
|
onBackground = palettes neutral 90,
|
||||||
surface = palettes neutral 10,
|
surface = palettes neutral if (amoledDarkTheme.value) 0 else 10,
|
||||||
onSurface = palettes neutral 90,
|
onSurface = palettes neutral 90,
|
||||||
surfaceVariant = palettes neutralVariant 30,
|
surfaceVariant = palettes neutralVariant 30,
|
||||||
onSurfaceVariant = palettes neutralVariant 80,
|
onSurfaceVariant = palettes neutralVariant 80,
|
||||||
|
@ -68,20 +71,18 @@ fun dynamicDarkColorScheme(): ColorScheme {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
@Composable
|
@Composable
|
||||||
inline infix fun Color.onLight(lightColor: Color): Color =
|
infix fun Color.onLight(lightColor: Color): Color =
|
||||||
if (!LocalUseDarkTheme.current) lightColor else this
|
if (!LocalDarkTheme.current.isDarkTheme()) lightColor else this
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
@Composable
|
@Composable
|
||||||
inline infix fun Color.onDark(darkColor: Color): Color =
|
infix fun Color.onDark(darkColor: Color): Color =
|
||||||
if (LocalUseDarkTheme.current) darkColor else this
|
if (LocalDarkTheme.current.isDarkTheme()) darkColor else this
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
infix fun Color.alwaysLight(isAlways: Boolean): Color {
|
infix fun Color.alwaysLight(isAlways: Boolean): Color {
|
||||||
val colorScheme = MaterialTheme.colorScheme
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
return if (isAlways && LocalUseDarkTheme.current) {
|
return if (isAlways && LocalDarkTheme.current.isDarkTheme()) {
|
||||||
when (this) {
|
when (this) {
|
||||||
colorScheme.primary -> colorScheme.onPrimary
|
colorScheme.primary -> colorScheme.onPrimary
|
||||||
colorScheme.secondary -> colorScheme.onSecondary
|
colorScheme.secondary -> colorScheme.onSecondary
|
||||||
|
|
|
@ -102,6 +102,10 @@
|
||||||
<string name="style">样式</string>
|
<string name="style">样式</string>
|
||||||
<string name="dark_theme">深色主题</string>
|
<string name="dark_theme">深色主题</string>
|
||||||
<string name="use_device_theme">跟随系统设置</string>
|
<string name="use_device_theme">跟随系统设置</string>
|
||||||
|
<string name="on">开启</string>
|
||||||
|
<string name="off">关闭</string>
|
||||||
|
<string name="amoled_dark_theme">Amoled 深色主题</string>
|
||||||
|
<string name="other">其他</string>
|
||||||
<string name="tonal_elevation">色调海拔</string>
|
<string name="tonal_elevation">色调海拔</string>
|
||||||
<string name="fonts">字体</string>
|
<string name="fonts">字体</string>
|
||||||
<string name="basic_fonts">基本字体</string>
|
<string name="basic_fonts">基本字体</string>
|
||||||
|
|
|
@ -103,6 +103,10 @@
|
||||||
<string name="style">Style</string>
|
<string name="style">Style</string>
|
||||||
<string name="dark_theme">Dark Theme</string>
|
<string name="dark_theme">Dark Theme</string>
|
||||||
<string name="use_device_theme">Use Device Theme</string>
|
<string name="use_device_theme">Use Device Theme</string>
|
||||||
|
<string name="on">On</string>
|
||||||
|
<string name="off">Off</string>
|
||||||
|
<string name="other">Other</string>
|
||||||
|
<string name="amoled_dark_theme">Amoled Dark Theme</string>
|
||||||
<string name="tonal_elevation">Tonal Elevation</string>
|
<string name="tonal_elevation">Tonal Elevation</string>
|
||||||
<string name="fonts">Fonts</string>
|
<string name="fonts">Fonts</string>
|
||||||
<string name="basic_fonts">Basic Fonts</string>
|
<string name="basic_fonts">Basic Fonts</string>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user