feat(settings): 添加颜色对比度设置项
This commit is contained in:
parent
7ab13cd6f9
commit
121334498c
13 changed files with 221 additions and 13 deletions
|
|
@ -11,4 +11,7 @@ object Constants {
|
|||
|
||||
const val PREF_DARK_MODE = "dark_mode"
|
||||
const val PREF_DARK_MODE_DEFAULT = 1 // Follow System
|
||||
|
||||
const val PREF_CONTRAST_LEVEL = "contrast_level"
|
||||
const val PREF_CONTRAST_LEVEL_DEFAULT = 0f // Normal
|
||||
}
|
||||
|
|
@ -15,4 +15,8 @@ object GlobalValues {
|
|||
Constants.PREF_DARK_MODE,
|
||||
Constants.PREF_DARK_MODE_DEFAULT
|
||||
)
|
||||
var contrastLevel: Float by SPDelegates(
|
||||
Constants.PREF_CONTRAST_LEVEL,
|
||||
Constants.PREF_CONTRAST_LEVEL_DEFAULT
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package cn.super12138.todo.logic.model
|
||||
|
||||
import android.content.Context
|
||||
import cn.super12138.todo.R
|
||||
|
||||
enum class ContrastLevel(val value: Float) {
|
||||
VeryLow(-1f),
|
||||
Low(-0.5f),
|
||||
Default(0f),
|
||||
Medium(0.5f),
|
||||
High(1f);
|
||||
|
||||
fun getDisplayName(context: Context): String {
|
||||
val resId = when (this) {
|
||||
VeryLow -> R.string.contrast_very_low
|
||||
Low -> R.string.contrast_low
|
||||
Default -> R.string.contrast_default
|
||||
Medium -> R.string.contrast_medium
|
||||
High -> R.string.contrast_high
|
||||
}
|
||||
return context.getString(resId)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromFloat(float: Float): ContrastLevel {
|
||||
return ContrastLevel.entries.find { it.value == float } ?: Default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,11 +27,13 @@ class MainActivity : ComponentActivity() {
|
|||
setContent {
|
||||
val mainViewModel: MainViewModel = viewModel()
|
||||
val showConfetti = mainViewModel.showConfetti
|
||||
// 深色模式
|
||||
val darkTheme = when (mainViewModel.appDarkMode) {
|
||||
FollowSystem -> isSystemInDarkTheme()
|
||||
Light -> false
|
||||
Dark -> true
|
||||
}
|
||||
// 配置状态栏和底部导航栏的颜色(在用户切换深色模式时)
|
||||
// https://github.com/dn0ne/lotus/blob/master/app/src/main/java/com/dn0ne/player/MainActivity.kt#L266
|
||||
LaunchedEffect(mainViewModel.appDarkMode) {
|
||||
WindowCompat.getInsetsController(window, window.decorView).apply {
|
||||
|
|
@ -39,7 +41,11 @@ class MainActivity : ComponentActivity() {
|
|||
isAppearanceLightNavigationBars = !darkTheme
|
||||
}
|
||||
}
|
||||
ToDoTheme(darkTheme = darkTheme) {
|
||||
|
||||
ToDoTheme(
|
||||
darkTheme = darkTheme,
|
||||
contrastLevel = mainViewModel.appContrastLevel.value.toDouble()
|
||||
) {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
TodoNavigation(
|
||||
viewModel = mainViewModel,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import cn.super12138.todo.R
|
|||
import cn.super12138.todo.constants.Constants
|
||||
import cn.super12138.todo.ui.components.LargeTopAppBarScaffold
|
||||
import cn.super12138.todo.ui.pages.settings.components.SwitchSettingsItem
|
||||
import cn.super12138.todo.ui.pages.settings.components.contrast.ContrastPicker
|
||||
import cn.super12138.todo.ui.pages.settings.components.darkmode.DarkModePicker
|
||||
import cn.super12138.todo.ui.pages.settings.components.palette.PalettePicker
|
||||
import cn.super12138.todo.ui.theme.appPaletteStyle
|
||||
|
|
@ -45,7 +46,12 @@ fun SettingsAppearance(
|
|||
) {
|
||||
DarkModePicker(onDarkModeChange = { viewModel.setDarkMode(it) })
|
||||
|
||||
PalettePicker(onPaletteChange = { appPaletteStyle = it })
|
||||
PalettePicker(
|
||||
contrastLevel = viewModel.appContrastLevel,
|
||||
onPaletteChange = { appPaletteStyle = it }
|
||||
)
|
||||
|
||||
ContrastPicker(onContrastChange = { viewModel.setContrastLevel(it) })
|
||||
|
||||
SwitchSettingsItem(
|
||||
key = Constants.PREF_DYNAMIC_COLOR,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package cn.super12138.todo.ui.pages.settings.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.FlingBehavior
|
||||
import androidx.compose.foundation.gestures.ScrollableDefaults
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
|
@ -40,6 +42,34 @@ fun RowSettingsItem(
|
|||
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
|
||||
userScrollEnabled: Boolean = true,
|
||||
content: LazyListScope.() -> Unit
|
||||
) {
|
||||
MoreContentSettingsItem(
|
||||
title = title,
|
||||
description = description,
|
||||
shape = shape,
|
||||
modifier = modifier
|
||||
) {
|
||||
LazyRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = state,
|
||||
contentPadding = contentPadding,
|
||||
reverseLayout = reverseLayout,
|
||||
horizontalArrangement = horizontalArrangement,
|
||||
verticalAlignment = verticalAlignment,
|
||||
flingBehavior = flingBehavior,
|
||||
userScrollEnabled = userScrollEnabled,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MoreContentSettingsItem(
|
||||
title: String,
|
||||
description: String? = null,
|
||||
shape: Shape = MaterialTheme.shapes.large,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
|
|
@ -70,16 +100,12 @@ fun RowSettingsItem(
|
|||
|
||||
Spacer(Modifier.size(8.dp))
|
||||
|
||||
LazyRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = state,
|
||||
contentPadding = contentPadding,
|
||||
reverseLayout = reverseLayout,
|
||||
horizontalArrangement = horizontalArrangement,
|
||||
verticalAlignment = verticalAlignment,
|
||||
flingBehavior = flingBehavior,
|
||||
userScrollEnabled = userScrollEnabled,
|
||||
content = content
|
||||
)
|
||||
/*Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.large)
|
||||
.background(MaterialTheme.colorScheme.surfaceContainer)
|
||||
) {*/
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package cn.super12138.todo.ui.pages.settings.components.contrast
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Label
|
||||
import androidx.compose.material3.PlainTooltip
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.SliderDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cn.super12138.todo.R
|
||||
import cn.super12138.todo.constants.Constants
|
||||
import cn.super12138.todo.logic.model.ContrastLevel
|
||||
import cn.super12138.todo.ui.pages.settings.components.MoreContentSettingsItem
|
||||
import cn.super12138.todo.ui.pages.settings.state.rememberPrefFloatState
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ContrastPicker(
|
||||
onContrastChange: (ContrastLevel) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
MoreContentSettingsItem(
|
||||
title = stringResource(R.string.pref_contrast_level),
|
||||
description = stringResource(R.string.pref_contrast_level_desc)
|
||||
) {
|
||||
var contrastState by rememberPrefFloatState(
|
||||
Constants.PREF_CONTRAST_LEVEL,
|
||||
Constants.PREF_CONTRAST_LEVEL_DEFAULT
|
||||
)
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
Slider(
|
||||
modifier = Modifier.semantics {
|
||||
contentDescription = context.getString(R.string.tip_change_contrast_level)
|
||||
},
|
||||
value = contrastState,
|
||||
onValueChange = {
|
||||
contrastState = it
|
||||
onContrastChange(ContrastLevel.fromFloat(it))
|
||||
},
|
||||
valueRange = -1f..1f,
|
||||
steps = 3,
|
||||
interactionSource = interactionSource,
|
||||
thumb = {
|
||||
Label(
|
||||
label = {
|
||||
PlainTooltip(
|
||||
modifier = Modifier
|
||||
.sizeIn(45.dp, 25.dp)
|
||||
.wrapContentWidth()
|
||||
) {
|
||||
Text(ContrastLevel.fromFloat(contrastState).getDisplayName(context))
|
||||
}
|
||||
},
|
||||
interactionSource = interactionSource
|
||||
) {
|
||||
SliderDefaults.Thumb(interactionSource)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -28,12 +28,14 @@ import androidx.compose.ui.res.colorResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import cn.super12138.todo.constants.GlobalValues
|
||||
import cn.super12138.todo.logic.model.ContrastLevel
|
||||
import cn.super12138.todo.ui.theme.PaletteStyle
|
||||
import cn.super12138.todo.ui.theme.dynamicColorScheme
|
||||
|
||||
@Composable
|
||||
fun PaletteItem(
|
||||
paletteStyle: PaletteStyle,
|
||||
contrastLevel: ContrastLevel,
|
||||
selected: Boolean,
|
||||
onSelect: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
|
|
@ -58,6 +60,7 @@ fun PaletteItem(
|
|||
Color(0xFF0061A4)
|
||||
},
|
||||
isDark = isSystemInDarkTheme(),
|
||||
contrastLevel = contrastLevel.value.toDouble(),
|
||||
style = paletteStyle
|
||||
)
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import cn.super12138.todo.R
|
||||
import cn.super12138.todo.constants.Constants
|
||||
import cn.super12138.todo.logic.model.ContrastLevel
|
||||
import cn.super12138.todo.ui.pages.settings.components.RowSettingsItem
|
||||
import cn.super12138.todo.ui.pages.settings.state.rememberPrefIntState
|
||||
import cn.super12138.todo.ui.theme.PaletteStyle
|
||||
|
||||
@Composable
|
||||
fun PalettePicker(
|
||||
contrastLevel: ContrastLevel,
|
||||
onPaletteChange: (paletteStyle: PaletteStyle) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
|
|
@ -38,6 +40,7 @@ fun PalettePicker(
|
|||
PaletteItem(
|
||||
paletteStyle = paletteStyle,
|
||||
selected = PaletteStyle.fromId(paletteState) == paletteStyle,
|
||||
contrastLevel = contrastLevel,
|
||||
onSelect = {
|
||||
paletteState = paletteStyle.id
|
||||
onPaletteChange(paletteStyle)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ package cn.super12138.todo.ui.pages.settings.state
|
|||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableFloatState
|
||||
import androidx.compose.runtime.MutableIntState
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -28,6 +30,12 @@ fun rememberPrefIntState(key: String, default: Int): MutableIntState {
|
|||
return remember { PrefMutableIntState(context, key, default) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberPrefFloatState(key: String, default: Float): MutableFloatState {
|
||||
val context = LocalContext.current
|
||||
return remember { PrefMutableFloatState(context, key, default) }
|
||||
}
|
||||
|
||||
private class PrefMutableBooleanState(
|
||||
val context: Context,
|
||||
val key: String,
|
||||
|
|
@ -74,6 +82,29 @@ private class PrefMutableIntState(
|
|||
}
|
||||
}
|
||||
|
||||
private class PrefMutableFloatState(
|
||||
val context: Context,
|
||||
val key: String,
|
||||
default: Float,
|
||||
) : MutableFloatState {
|
||||
private val delegate = mutableFloatStateOf(context.pref.getFloat(key, default))
|
||||
|
||||
override var floatValue: Float
|
||||
get() = delegate.floatValue
|
||||
set(value) {
|
||||
delegate.floatValue = value
|
||||
context.pref.edit().putFloat(key, value).apply()
|
||||
}
|
||||
|
||||
override fun component1(): Float {
|
||||
return delegate.component1()
|
||||
}
|
||||
|
||||
override fun component2(): (Float) -> Unit {
|
||||
return delegate.component2()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 来自:https://github.com/hushenghao/AndroidEasterEggs/blob/main/basic/src/main/java/com/dede/android_eggs/util/Pref.kt
|
||||
* @author hushenghao
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
|
|||
import cn.super12138.todo.constants.GlobalValues
|
||||
import cn.super12138.todo.logic.Repository
|
||||
import cn.super12138.todo.logic.database.TodoEntity
|
||||
import cn.super12138.todo.logic.model.ContrastLevel
|
||||
import cn.super12138.todo.logic.model.DarkMode
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -26,6 +27,8 @@ class MainViewModel : ViewModel() {
|
|||
// 深色模式
|
||||
var appDarkMode by mutableStateOf(DarkMode.fromId(GlobalValues.darkMode))
|
||||
private set
|
||||
var appContrastLevel by mutableStateOf(ContrastLevel.fromFloat(GlobalValues.contrastLevel))
|
||||
private set
|
||||
|
||||
// 多选逻辑参考:https://github.com/X1nto/Mauth
|
||||
private val _selectedTodoIds = MutableStateFlow(listOf<Int>())
|
||||
|
|
@ -118,4 +121,8 @@ class MainViewModel : ViewModel() {
|
|||
fun setDarkMode(darkMode: DarkMode) {
|
||||
appDarkMode = darkMode
|
||||
}
|
||||
|
||||
fun setContrastLevel(contrastLevel: ContrastLevel) {
|
||||
appContrastLevel = contrastLevel
|
||||
}
|
||||
}
|
||||
|
|
@ -66,4 +66,11 @@
|
|||
<string name="dark_mode_system">跟随系统</string>
|
||||
<string name="dark_mode_light">浅色模式</string>
|
||||
<string name="dark_mode_dark">深色模式</string>
|
||||
<string name="pref_contrast_level">对比度</string>
|
||||
<string name="pref_contrast_level_desc">调整应用的颜色对比度</string>
|
||||
<string name="contrast_very_low">非常低</string>
|
||||
<string name="contrast_low">低</string>
|
||||
<string name="contrast_default">默认</string>
|
||||
<string name="contrast_medium">中</string>
|
||||
<string name="contrast_high">高</string>
|
||||
</resources>
|
||||
|
|
@ -67,4 +67,12 @@
|
|||
<string name="dark_mode_system">Follow System</string>
|
||||
<string name="dark_mode_light">Light</string>
|
||||
<string name="dark_mode_dark">Dark</string>
|
||||
<string name="pref_contrast_level">Contrast Level</string>
|
||||
<string name="pref_contrast_level_desc">Change the app\'s color contrast</string>
|
||||
<string name="contrast_very_low">Very Low</string>
|
||||
<string name="contrast_low">Low</string>
|
||||
<string name="contrast_default">Default</string>
|
||||
<string name="contrast_medium">Medium</string>
|
||||
<string name="contrast_high">High</string>
|
||||
<string name="tip_change_contrast_level">Change the contrast level</string>
|
||||
</resources>
|
||||
Loading…
Reference in a new issue