From 121334498cb323cd9eaa85134854c7ebd6ad560e Mon Sep 17 00:00:00 2001 From: Super12138 <70494801+Super12138@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:26:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(settings):=20=E6=B7=BB=E5=8A=A0=E9=A2=9C?= =?UTF-8?q?=E8=89=B2=E5=AF=B9=E6=AF=94=E5=BA=A6=E8=AE=BE=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/super12138/todo/constants/Constants.kt | 3 + .../super12138/todo/constants/GlobalValues.kt | 4 + .../todo/logic/model/ContrastLevel.kt | 29 +++++++ .../todo/ui/activities/MainActivity.kt | 8 +- .../ui/pages/settings/SettingsAppearance.kt | 8 +- .../settings/components/RowSettingsItem.kt | 48 +++++++++--- .../components/contrast/ContrastPicker.kt | 75 +++++++++++++++++++ .../components/palette/PaletteItem.kt | 3 + .../components/palette/PalettePicker.kt | 3 + .../pages/settings/state/PrefMutableState.kt | 31 ++++++++ .../todo/ui/viewmodels/MainViewModel.kt | 7 ++ app/src/main/res/values-zh-rCN/strings.xml | 7 ++ app/src/main/res/values/strings.xml | 8 ++ 13 files changed, 221 insertions(+), 13 deletions(-) create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/model/ContrastLevel.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/contrast/ContrastPicker.kt diff --git a/app/src/main/kotlin/cn/super12138/todo/constants/Constants.kt b/app/src/main/kotlin/cn/super12138/todo/constants/Constants.kt index 835589c..f2392ef 100644 --- a/app/src/main/kotlin/cn/super12138/todo/constants/Constants.kt +++ b/app/src/main/kotlin/cn/super12138/todo/constants/Constants.kt @@ -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 } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/constants/GlobalValues.kt b/app/src/main/kotlin/cn/super12138/todo/constants/GlobalValues.kt index 69524c5..da70b05 100644 --- a/app/src/main/kotlin/cn/super12138/todo/constants/GlobalValues.kt +++ b/app/src/main/kotlin/cn/super12138/todo/constants/GlobalValues.kt @@ -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 + ) } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/model/ContrastLevel.kt b/app/src/main/kotlin/cn/super12138/todo/logic/model/ContrastLevel.kt new file mode 100644 index 0000000..0ca7b3b --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/model/ContrastLevel.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt b/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt index aab611d..e787030 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/activities/MainActivity.kt @@ -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, diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt index 13670eb..dadf0e8 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAppearance.kt @@ -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, diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/RowSettingsItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/RowSettingsItem.kt index a954618..fef93cf 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/RowSettingsItem.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/RowSettingsItem.kt @@ -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() } } \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/contrast/ContrastPicker.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/contrast/ContrastPicker.kt new file mode 100644 index 0000000..e95d428 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/contrast/ContrastPicker.kt @@ -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) + } + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PaletteItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PaletteItem.kt index ff7266d..23219f0 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PaletteItem.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PaletteItem.kt @@ -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 ) ) { diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PalettePicker.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PalettePicker.kt index ef63cf2..73bfb09 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PalettePicker.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/palette/PalettePicker.kt @@ -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) diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/state/PrefMutableState.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/state/PrefMutableState.kt index 0949a59..d5604b0 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/state/PrefMutableState.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/state/PrefMutableState.kt @@ -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 diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/viewmodels/MainViewModel.kt b/app/src/main/kotlin/cn/super12138/todo/ui/viewmodels/MainViewModel.kt index 05abed6..45a6f39 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/viewmodels/MainViewModel.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/viewmodels/MainViewModel.kt @@ -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()) @@ -118,4 +121,8 @@ class MainViewModel : ViewModel() { fun setDarkMode(darkMode: DarkMode) { appDarkMode = darkMode } + + fun setContrastLevel(contrastLevel: ContrastLevel) { + appContrastLevel = contrastLevel + } } \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4856cb5..efa8c2b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -66,4 +66,11 @@ 跟随系统 浅色模式 深色模式 + 对比度 + 调整应用的颜色对比度 + 非常低 + + 默认 + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dfc3d93..69dbfd2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -67,4 +67,12 @@ Follow System Light Dark + Contrast Level + Change the app\'s color contrast + Very Low + Low + Default + Medium + High + Change the contrast level \ No newline at end of file