feat:添加浅色、深色切换提醒功能 (#1584)

* feat:添加浅色、深色切换提醒功能

* refactor: 重构SwitchDayNightReminder为通用ActionReminder
- ReadConfigScreen 补上 主题模式切换提醒 开关
This commit is contained in:
fansan
2026-06-30 01:50:52 +08:00
committed by GitHub
parent a767d70d9f
commit af5094801c
17 changed files with 342 additions and 0 deletions
@@ -103,6 +103,7 @@ object PreferKey {
const val adaptSpecialStyle = "adaptSpecialStyle" const val adaptSpecialStyle = "adaptSpecialStyle"
const val autoClearExpired = "autoClearExpired" const val autoClearExpired = "autoClearExpired"
const val autoChangeSource = "autoChangeSource" const val autoChangeSource = "autoChangeSource"
const val autoSuggestDayNight = "autoSuggestDayNight"
const val importKeepName = "importKeepName" const val importKeepName = "importKeepName"
const val importKeepGroup = "importKeepGroup" const val importKeepGroup = "importKeepGroup"
const val screenOrientation = "screenOrientation" const val screenOrientation = "screenOrientation"
@@ -45,6 +45,7 @@ data class ReadPreferences(
val sliderVibrator: Boolean = false, val sliderVibrator: Boolean = false,
val selectVibrator: Boolean = false, val selectVibrator: Boolean = false,
val autoChangeSource: Boolean = true, val autoChangeSource: Boolean = true,
val autoSuggestDayNight: Boolean = false,
val selectText: Boolean = true, val selectText: Boolean = true,
val noAnimScrollPage: Boolean = false, val noAnimScrollPage: Boolean = false,
val clickImgWay: String = "2", val clickImgWay: String = "2",
@@ -214,6 +215,9 @@ class ReadSettingsRepository(
suspend fun setAutoChangeSource(value: Boolean) = suspend fun setAutoChangeSource(value: Boolean) =
settingsRepository.putBoolean(PreferKey.autoChangeSource, value) settingsRepository.putBoolean(PreferKey.autoChangeSource, value)
suspend fun setAutoSuggestDayNight(value: Boolean) =
settingsRepository.putBoolean(PreferKey.autoSuggestDayNight, value)
suspend fun setSelectText(value: Boolean) = suspend fun setSelectText(value: Boolean) =
settingsRepository.putBoolean(PreferKey.selectText, value) settingsRepository.putBoolean(PreferKey.selectText, value)
@@ -422,6 +426,7 @@ class ReadSettingsRepository(
sliderVibrator = this[Keys.SliderVibrator] ?: false, sliderVibrator = this[Keys.SliderVibrator] ?: false,
selectVibrator = this[Keys.SelectVibrator] ?: false, selectVibrator = this[Keys.SelectVibrator] ?: false,
autoChangeSource = this[Keys.AutoChangeSource] ?: true, autoChangeSource = this[Keys.AutoChangeSource] ?: true,
autoSuggestDayNight = this[Keys.AutoSuggestDayNight] ?: false,
selectText = this[Keys.SelectText] ?: true, selectText = this[Keys.SelectText] ?: true,
noAnimScrollPage = this[Keys.NoAnimScrollPage] ?: false, noAnimScrollPage = this[Keys.NoAnimScrollPage] ?: false,
clickImgWay = this[Keys.ClickImgWay] ?: "2", clickImgWay = this[Keys.ClickImgWay] ?: "2",
@@ -522,6 +527,7 @@ class ReadSettingsRepository(
val SliderVibrator = booleanPreferencesKey(PreferKey.sliderVibrator) val SliderVibrator = booleanPreferencesKey(PreferKey.sliderVibrator)
val SelectVibrator = booleanPreferencesKey(PreferKey.selectVibrator) val SelectVibrator = booleanPreferencesKey(PreferKey.selectVibrator)
val AutoChangeSource = booleanPreferencesKey(PreferKey.autoChangeSource) val AutoChangeSource = booleanPreferencesKey(PreferKey.autoChangeSource)
val AutoSuggestDayNight = booleanPreferencesKey(PreferKey.autoSuggestDayNight)
val SelectText = booleanPreferencesKey(PreferKey.selectText) val SelectText = booleanPreferencesKey(PreferKey.selectText)
val NoAnimScrollPage = booleanPreferencesKey(PreferKey.noAnimScrollPage) val NoAnimScrollPage = booleanPreferencesKey(PreferKey.noAnimScrollPage)
val ClickImgWay = stringPreferencesKey(PreferKey.clickImgWay) val ClickImgWay = stringPreferencesKey(PreferKey.clickImgWay)
@@ -0,0 +1,107 @@
package io.legado.app.ui.book.read
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.card.NormalCard
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds
@Composable
fun ActionReminder(
modifier: Modifier = Modifier,
reminder: ReminderUiState?,
onAction: (ReminderUiState) -> Unit,
onDismiss: () -> Unit,
) {
val show = reminder != null
var currentReminder by remember { mutableStateOf(reminder) }
if (reminder != null) {
currentReminder = reminder
}
LaunchedEffect(show, reminder?.id) {
if (show) {
delay(4.seconds)
onDismiss()
}
}
Box(
modifier = modifier
.fillMaxSize()
.navigationBarsPadding(),
contentAlignment = Alignment.BottomCenter,
) {
AnimatedVisibility(
visible = show,
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
) {
currentReminder?.let { target ->
NormalCard(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, top = 8.dp, end = 12.dp, bottom = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = target.message,
fontSize = 13.sp,
style = LegadoTheme.typography.bodyMedium,
color = LegadoTheme.colorScheme.onSurface,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.weight(1f)
.padding(end = 12.dp),
)
if (target.actionText != null) {
Button(
onClick = {
onDismiss()
onAction(target)
},
modifier = Modifier.height(32.dp),
contentPadding = PaddingValues(horizontal = 14.dp, vertical = 0.dp),
) {
Text(target.actionText, fontSize = 12.sp)
}
}
}
}
}
}
}
}
@@ -21,6 +21,15 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentMapOf
import java.util.UUID
@Stable
data class ReminderUiState(
val id: String = UUID.randomUUID().toString(),
val message: String,
val actionText: String? = null,
val actionIntent: ReadBookIntent? = null,
)
@Stable @Stable
data class ReadBookMenuState( data class ReadBookMenuState(
@@ -94,6 +103,7 @@ data class ReadBookUiState(
val isLocalBook: Boolean = true, val isLocalBook: Boolean = true,
val msg: String? = null, val msg: String? = null,
val isInitFinish: Boolean = false, val isInitFinish: Boolean = false,
val activeReminder: ReminderUiState? = null,
// Search // Search
val searchMenuVisible: Boolean = false, val searchMenuVisible: Boolean = false,
val isShowingSearchResult: Boolean = false, val isShowingSearchResult: Boolean = false,
@@ -242,6 +252,8 @@ sealed interface ReadBookIntent {
// Initialization // Initialization
data class InitData(val intent: android.content.Intent) : ReadBookIntent data class InitData(val intent: android.content.Intent) : ReadBookIntent
data class InitReadBookConfig(val intent: android.content.Intent) : ReadBookIntent data class InitReadBookConfig(val intent: android.content.Intent) : ReadBookIntent
data class CheckSwitchDayNight(val lux: Float) : ReadBookIntent
data object DismissReminder : ReadBookIntent
// Navigation // Navigation
data object NextPage : ReadBookIntent data object NextPage : ReadBookIntent
@@ -1175,6 +1187,9 @@ sealed interface ConfigUpdate {
data class AutoChangeSource(val value: Boolean) : ConfigUpdate { data class AutoChangeSource(val value: Boolean) : ConfigUpdate {
override val actions = emptySet<ConfigUpdateAction>() override val actions = emptySet<ConfigUpdateAction>()
} }
data class AutoSuggestDayNight(val value: Boolean) : ConfigUpdate {
override val actions = emptySet<ConfigUpdateAction>()
}
data class SelectText(val value: Boolean) : ConfigUpdate { data class SelectText(val value: Boolean) : ConfigUpdate {
override val actions = emptySet<ConfigUpdateAction>() override val actions = emptySet<ConfigUpdateAction>()
} }
@@ -17,10 +17,18 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.legado.app.ui.config.readConfig.ReadConfig
import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.layerBackdrop
@@ -233,6 +241,44 @@ fun ReadBookRouteScreen(
viewModel.onIntent(ReadBookIntent.BookInfoResult(result.resultCode == android.app.Activity.RESULT_OK)) viewModel.onIntent(ReadBookIntent.BookInfoResult(result.resultCode == android.app.Activity.RESULT_OK))
} }
DisposableEffect(lifecycleOwner) {
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager
val lightSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_LIGHT)
var listener: SensorEventListener? = null
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
if (!ReadConfig.autoSuggestDayNight) return@LifecycleEventObserver
if (lightSensor != null) {
listener = object : SensorEventListener {
override fun onSensorChanged(sensorEvent: SensorEvent?) {
sensorEvent?.values?.firstOrNull()?.let { lux ->
//Log.d("fansangg","lux = $lux")
viewModel.onIntent(ReadBookIntent.CheckSwitchDayNight(lux))
}
listener?.let { sensorManager.unregisterListener(it) }
listener = null
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
sensorManager.registerListener(listener, lightSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
} else if (event == Lifecycle.Event.ON_PAUSE) {
listener?.let {
sensorManager?.unregisterListener(it)
listener = null
}
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
listener?.let { sensorManager?.unregisterListener(it) }
}
}
// ── Effect collection: route handles launcher effects, rest goes to bridge ── // ── Effect collection: route handles launcher effects, rest goes to bridge ──
LaunchedEffect(viewModel) { LaunchedEffect(viewModel) {
@@ -503,4 +503,10 @@ fun ReadBookScreen(
// Sheets using AppModalBottomSheet are composed unconditionally above // Sheets using AppModalBottomSheet are composed unconditionally above
else -> {} else -> {}
} }
ActionReminder(
reminder = state.activeReminder,
onAction = { reminder -> reminder.actionIntent?.let { onIntent(it) } },
onDismiss = { onIntent(ReadBookIntent.DismissReminder) },
)
} }
@@ -8,6 +8,8 @@ import android.provider.OpenableColumns
import android.speech.tts.TextToSpeech import android.speech.tts.TextToSpeech
import android.util.Base64 import android.util.Base64
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.core.graphics.toColorInt
import androidx.core.graphics.ColorUtils as AndroidColorUtils
import io.legado.app.BuildConfig import io.legado.app.BuildConfig
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
@@ -76,6 +78,7 @@ import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
import io.legado.app.ui.widget.components.importComponents.ImportItemWrapper import io.legado.app.ui.widget.components.importComponents.ImportItemWrapper
import io.legado.app.ui.widget.components.importComponents.ImportStatus import io.legado.app.ui.widget.components.importComponents.ImportStatus
import io.legado.app.utils.ColorUtils
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.ImageSaveUtils import io.legado.app.utils.ImageSaveUtils
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
@@ -120,6 +123,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.FileNotFoundException import java.io.FileNotFoundException
import kotlin.coroutines.coroutineContext import kotlin.coroutines.coroutineContext
import kotlin.time.Duration.Companion.milliseconds
/** /**
* 阅读界面 ViewModel — MVI/UDF 架构 * 阅读界面 ViewModel — MVI/UDF 架构
@@ -196,6 +200,8 @@ class ReadBookViewModel(
justInitData = true justInitData = true
} }
is ReadBookIntent.InitReadBookConfig -> initReadBookConfig(intent.intent) is ReadBookIntent.InitReadBookConfig -> initReadBookConfig(intent.intent)
is ReadBookIntent.CheckSwitchDayNight -> checkSwitchDayNight(intent.lux)
is ReadBookIntent.DismissReminder -> dismissReminder()
is ReadBookIntent.NextPage -> ReadBook.moveToNextPage() is ReadBookIntent.NextPage -> ReadBook.moveToNextPage()
is ReadBookIntent.PrevPage -> ReadBook.moveToPrevPage() is ReadBookIntent.PrevPage -> ReadBook.moveToPrevPage()
is ReadBookIntent.NextChapter -> ReadBook.moveToNextChapter(upContent = true) is ReadBookIntent.NextChapter -> ReadBook.moveToNextChapter(upContent = true)
@@ -3430,6 +3436,12 @@ class ReadBookViewModel(
readSettingsRepository.setAutoChangeSource(update.value) readSettingsRepository.setAutoChangeSource(update.value)
} }
} }
is ConfigUpdate.AutoSuggestDayNight -> {
ReadConfig.autoSuggestDayNight = update.value
viewModelScope.launch {
readSettingsRepository.setAutoSuggestDayNight(update.value)
}
}
is ConfigUpdate.SelectText -> { is ConfigUpdate.SelectText -> {
ReadConfig.selectText = update.value ReadConfig.selectText = update.value
viewModelScope.launch { viewModelScope.launch {
@@ -4007,6 +4019,86 @@ class ReadBookViewModel(
context.toastOnUi("添加书籍失败") context.toastOnUi("添加书籍失败")
} }
} }
private var lastSwitchDayNightReminderTime: Long = 0L
private val reminderQueue = ArrayDeque<ReminderUiState>()
private fun showReminder(reminder: ReminderUiState) {
if (_uiState.value.activeReminder == null && reminderQueue.isEmpty()) {
_uiState.update { it.copy(activeReminder = reminder) }
} else {
reminderQueue.addLast(reminder)
}
}
private fun isReadBgLight(colorInt: Int): Boolean {
// io.legado.app.utils.ColorUtils.isColorLight 判断条件是 >= 0.5
// 实际很多肉眼觉得亮的颜色会被判断为false,例如 0xFFC5B098
return AndroidColorUtils.calculateLuminance(colorInt) >= LIGHT_LUMINANCE_THRESHOLD
}
private fun checkSwitchDayNight(lux: Float) {
if (!ReadConfig.autoSuggestDayNight) return
if (System.currentTimeMillis() - lastSwitchDayNightReminderTime < REMINDER_COOLDOWN_MS) return
val isNight = ReadConfig.isNightTheme
val styleConfig = _uiState.value.styleConfig
if (!isNight && lux <= DARK_LUX_THRESHOLD) {
val bgType = styleConfig.bgType
val isLightBg = if (bgType == 0) {
val colorInt = runCatching { styleConfig.bgStr.toColorInt() }.getOrDefault(0xFFEEEEEE.toInt())
isReadBgLight(colorInt)
} else {
val meanColor = ReadBookConfig.bgMeanColor
//Log.d("fansangg","meanColor = ${ColorUtils.intToString(meanColor)},isLight = ${isReadBgLight(meanColor)}")
if (meanColor != 0) isReadBgLight(meanColor) else true
}
if (isLightBg) {
lastSwitchDayNightReminderTime = System.currentTimeMillis()
showReminder(
ReminderUiState(
message = context.getString(R.string.switch_to_dark_mode_tip),
actionText = context.getString(R.string.switch_action),
actionIntent = ReadBookIntent.ToggleDayNight,
)
)
}
} else if (isNight && lux >= BRIGHT_LUX_THRESHOLD) {
val bgTypeNight = styleConfig.bgTypeNight
val isDarkBg = if (bgTypeNight == 0) {
val colorInt = runCatching { styleConfig.bgStrNight.toColorInt() }.getOrDefault(0xFF000000.toInt())
!isReadBgLight(colorInt)
} else {
val meanColor = ReadBookConfig.bgMeanColor
//Log.d("fansangg","meanColor = ${ColorUtils.intToString(meanColor)},isLight = ${isReadBgLight(meanColor)}")
if (meanColor != 0) !isReadBgLight(meanColor) else true
}
if (isDarkBg) {
lastSwitchDayNightReminderTime = System.currentTimeMillis()
showReminder(
ReminderUiState(
message = context.getString(R.string.switch_to_light_mode_tip),
actionText = context.getString(R.string.switch_action),
actionIntent = ReadBookIntent.ToggleDayNight,
)
)
}
}
}
private fun dismissReminder() {
_uiState.update { it.copy(activeReminder = null) }
if (reminderQueue.isNotEmpty()) {
viewModelScope.launch {
//延迟一下,让上一个提醒的动画结束
delay(500.milliseconds)
if (_uiState.value.activeReminder == null && reminderQueue.isNotEmpty()) {
val next = reminderQueue.removeFirst()
_uiState.update { it.copy(activeReminder = next) }
}
}
}
}
} }
private const val TITLE_BAR_ICON_PREFS = "title_bar_icons" private const val TITLE_BAR_ICON_PREFS = "title_bar_icons"
@@ -4015,6 +4107,11 @@ private const val TOOL_BUTTON_PREFS = "tool_button_config"
private const val TOOL_BUTTON_KEY = "tool_buttons" private const val TOOL_BUTTON_KEY = "tool_buttons"
private const val DEFAULT_ENABLED_BUTTON_COUNT = 5 private const val DEFAULT_ENABLED_BUTTON_COUNT = 5
private const val DARK_LUX_THRESHOLD = 10f
private const val BRIGHT_LUX_THRESHOLD = 100f
private const val LIGHT_LUMINANCE_THRESHOLD = 0.35
private const val REMINDER_COOLDOWN_MS = 10 * 60 * 1000L
private data class SearchTextPoint( private data class SearchTextPoint(
val pageIndex: Int, val pageIndex: Int,
val lineIndex: Int, val lineIndex: Int,
@@ -125,6 +125,9 @@ fun MoreConfigSheet(
onAutoChangeSourceChange = { onAutoChangeSourceChange = {
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.AutoChangeSource(it))) onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.AutoChangeSource(it)))
}, },
onAutoSuggestDayNightChange = {
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.AutoSuggestDayNight(it)))
},
onSelectTextChange = { onSelectTextChange = {
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.SelectText(it))) onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.SelectText(it)))
}, },
@@ -298,6 +301,7 @@ private fun OtherSettings(
onSliderVibratorChange: (Boolean) -> Unit, onSliderVibratorChange: (Boolean) -> Unit,
onSelectVibratorChange: (Boolean) -> Unit, onSelectVibratorChange: (Boolean) -> Unit,
onAutoChangeSourceChange: (Boolean) -> Unit, onAutoChangeSourceChange: (Boolean) -> Unit,
onAutoSuggestDayNightChange: (Boolean) -> Unit,
onSelectTextChange: (Boolean) -> Unit, onSelectTextChange: (Boolean) -> Unit,
onNoAnimScrollPageChange: (Boolean) -> Unit, onNoAnimScrollPageChange: (Boolean) -> Unit,
onOptimizeRenderChange: (Boolean) -> Unit, onOptimizeRenderChange: (Boolean) -> Unit,
@@ -329,6 +333,12 @@ private fun OtherSettings(
checked = preferences.autoChangeSource, checked = preferences.autoChangeSource,
onCheckedChange = onAutoChangeSourceChange, onCheckedChange = onAutoChangeSourceChange,
) )
TinySwitchSettingItem(
title = stringResource(R.string.auto_switch_theme_reminder_title),
description = stringResource(R.string.auto_switch_theme_reminder_desc),
checked = preferences.autoSuggestDayNight,
onCheckedChange = onAutoSuggestDayNightChange,
)
TinySwitchSettingItem( TinySwitchSettingItem(
title = stringResource(R.string.selectText), title = stringResource(R.string.selectText),
checked = preferences.selectText, checked = preferences.selectText,
@@ -214,6 +214,9 @@ object ReadConfig {
var autoChangeSource var autoChangeSource
get() = ReadDataConfig.autoChangeSource get() = ReadDataConfig.autoChangeSource
set(value) { ReadDataConfig.autoChangeSource = value } set(value) { ReadDataConfig.autoChangeSource = value }
var autoSuggestDayNight
get() = ReadDataConfig.autoSuggestDayNight
set(value) { ReadDataConfig.autoSuggestDayNight = value }
var defaultSourceChangeAll var defaultSourceChangeAll
get() = ReadDataConfig.defaultSourceChangeAll get() = ReadDataConfig.defaultSourceChangeAll
set(value) { ReadDataConfig.defaultSourceChangeAll = value } set(value) { ReadDataConfig.defaultSourceChangeAll = value }
@@ -299,6 +302,7 @@ object ReadConfig {
keepLight = preferences.keepLight keepLight = preferences.keepLight
screenOrientation = preferences.screenOrientation screenOrientation = preferences.screenOrientation
autoChangeSource = preferences.autoChangeSource autoChangeSource = preferences.autoChangeSource
autoSuggestDayNight = preferences.autoSuggestDayNight
defaultSourceChangeAll = preferences.defaultSourceChangeAll defaultSourceChangeAll = preferences.defaultSourceChangeAll
tocUiUseReplace = preferences.tocUiUseReplace tocUiUseReplace = preferences.tocUiUseReplace
tocCountWords = preferences.tocCountWords tocCountWords = preferences.tocCountWords
@@ -29,6 +29,7 @@ data class ReadConfigUiState(
val sliderVibrator: Boolean = false, val sliderVibrator: Boolean = false,
val selectVibrator: Boolean = false, val selectVibrator: Boolean = false,
val autoChangeSource: Boolean = true, val autoChangeSource: Boolean = true,
val autoSuggestDayNight: Boolean = false,
val selectText: Boolean = true, val selectText: Boolean = true,
val noAnimScrollPage: Boolean = false, val noAnimScrollPage: Boolean = false,
val clickImgWay: String = "2", val clickImgWay: String = "2",
@@ -82,5 +83,6 @@ sealed interface ReadConfigIntent {
data class TextSelectMenuFilterChanged(val value: String) : ReadConfigIntent data class TextSelectMenuFilterChanged(val value: String) : ReadConfigIntent
data class ShowReadTitleAdditionChanged(val value: Boolean) : ReadConfigIntent data class ShowReadTitleAdditionChanged(val value: Boolean) : ReadConfigIntent
data class ShowMenuIconChanged(val value: Boolean) : ReadConfigIntent data class ShowMenuIconChanged(val value: Boolean) : ReadConfigIntent
data class AutoSuggestDayNightChanged(val value: Boolean) : ReadConfigIntent
data class PageKeysChanged(val prevKeys: String, val nextKeys: String) : ReadConfigIntent data class PageKeysChanged(val prevKeys: String, val nextKeys: String) : ReadConfigIntent
} }
@@ -307,6 +307,15 @@ fun ReadConfigScreen(
} }
) )
SwitchSettingItem(
title = stringResource(R.string.auto_switch_theme_reminder_title),
description = stringResource(R.string.auto_switch_theme_reminder_desc),
checked = state.autoSuggestDayNight,
onCheckedChange = {
viewModel.onIntent(ReadConfigIntent.AutoSuggestDayNightChanged(it))
}
)
SwitchSettingItem( SwitchSettingItem(
title = stringResource(R.string.selectText), title = stringResource(R.string.selectText),
checked = state.selectText, checked = state.selectText,
@@ -166,6 +166,11 @@ class ReadConfigViewModel(
readSettingsRepository.setAutoChangeSource(intent.value) readSettingsRepository.setAutoChangeSource(intent.value)
} }
is ReadConfigIntent.AutoSuggestDayNightChanged -> {
ReadConfig.autoSuggestDayNight = intent.value
readSettingsRepository.setAutoSuggestDayNight(intent.value)
}
is ReadConfigIntent.SelectTextChanged -> { is ReadConfigIntent.SelectTextChanged -> {
ReadConfig.selectText = intent.value ReadConfig.selectText = intent.value
readSettingsRepository.setSelectText(intent.value) readSettingsRepository.setSelectText(intent.value)
@@ -267,6 +272,7 @@ class ReadConfigViewModel(
sliderVibrator = sliderVibrator, sliderVibrator = sliderVibrator,
selectVibrator = selectVibrator, selectVibrator = selectVibrator,
autoChangeSource = autoChangeSource, autoChangeSource = autoChangeSource,
autoSuggestDayNight = autoSuggestDayNight,
selectText = selectText, selectText = selectText,
noAnimScrollPage = noAnimScrollPage, noAnimScrollPage = noAnimScrollPage,
clickImgWay = clickImgWay, clickImgWay = clickImgWay,
@@ -25,6 +25,11 @@ object ReadDataConfig {
true true
) )
var autoSuggestDayNight by prefDelegate(
PreferKey.autoSuggestDayNight,
false
)
var defaultSourceChangeAll by prefDelegate( var defaultSourceChangeAll by prefDelegate(
PreferKey.defaultSourceChangeAll, PreferKey.defaultSourceChangeAll,
true true
@@ -2213,4 +2213,11 @@
<string name="is_event_listener">EventListener</string> <string name="is_event_listener">EventListener</string>
<string name="rule_call_back">callBackJs</string> <string name="rule_call_back">callBackJs</string>
<!-- Missing zh-rTW/HK translations (683 keys) --> <!-- Missing zh-rTW/HK translations (683 keys) -->
<!-- 日夜间/深浅色主题切换提醒 -->
<string name="auto_switch_theme_reminder_title">主题模式切换提醒</string>
<string name="auto_switch_theme_reminder_desc">根据环境光提醒切换浅色/深色模式</string>
<string name="switch_to_dark_mode_tip">当前环境光较暗,是否切换至深色模式?</string>
<string name="switch_to_light_mode_tip">当前环境光较亮,是否切换至浅色模式?</string>
<string name="switch_action">切换</string>
</resources> </resources>
@@ -2205,4 +2205,11 @@
<string name="weekday_tue">週二</string> <string name="weekday_tue">週二</string>
<string name="weekday_wed">週三</string> <string name="weekday_wed">週三</string>
<string name="welcome_title">歡迎!</string> <string name="welcome_title">歡迎!</string>
<!-- 日夜間/深淺色主題切換提醒 -->
<string name="auto_switch_theme_reminder_title">主題模式切換提醒</string>
<string name="auto_switch_theme_reminder_desc">根據環境光提醒切換淺色/深色模式</string>
<string name="switch_to_dark_mode_tip">當前環境光較暗,是否切換至深色模式?</string>
<string name="switch_to_light_mode_tip">當前環境光較亮,是否切換至淺色模式?</string>
<string name="switch_action">切換</string>
</resources> </resources>
@@ -2210,4 +2210,11 @@
<string name="export_complete">匯出完成</string> <string name="export_complete">匯出完成</string>
<string name="download_no_storage_permission">下載失敗:無儲存權限</string> <string name="download_no_storage_permission">下載失敗:無儲存權限</string>
<string name="already_in_download_list">已在下載列表中</string> <string name="already_in_download_list">已在下載列表中</string>
<!-- 日夜間/深淺色主題切換提醒 -->
<string name="auto_switch_theme_reminder_title">主題模式切換提醒</string>
<string name="auto_switch_theme_reminder_desc">根據環境光提醒切換淺色/深色模式</string>
<string name="switch_to_dark_mode_tip">目前環境光較暗,是否切換至深色模式?</string>
<string name="switch_to_light_mode_tip">目前環境光較亮,是否切換至淺色模式?</string>
<string name="switch_action">切換</string>
</resources> </resources>
+7
View File
@@ -2218,4 +2218,11 @@
<string name="export_complete">Export complete</string> <string name="export_complete">Export complete</string>
<string name="download_no_storage_permission">Download error: no storage permission</string> <string name="download_no_storage_permission">Download error: no storage permission</string>
<string name="already_in_download_list">Already in download list</string> <string name="already_in_download_list">Already in download list</string>
<!-- Day/Night Theme Switch Reminder -->
<string name="auto_switch_theme_reminder_title">Theme Mode Switch Reminder</string>
<string name="auto_switch_theme_reminder_desc">Remind to switch light/dark mode based on ambient light</string>
<string name="switch_to_dark_mode_tip">Ambient light is low. Switch to dark mode?</string>
<string name="switch_to_light_mode_tip">Ambient light is bright. Switch to light mode?</string>
<string name="switch_action">Switch</string>
</resources> </resources>