fix: 阅读背景问题

This commit is contained in:
HapeLee
2026-06-09 00:16:36 +08:00
parent 43fc1e8614
commit d0be59f607
5 changed files with 148 additions and 49 deletions
@@ -58,4 +58,5 @@ class ReadBookStyleConfigRepository {
}
save()
}
}
@@ -7,6 +7,8 @@ import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import androidx.annotation.Keep
import androidx.core.graphics.toColorInt
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.intPreferencesKey
import io.legado.app.R
import io.legado.app.constant.PageAnim
import io.legado.app.constant.PreferKey
@@ -14,6 +16,7 @@ import io.legado.app.constant.ReadMenuBlurMode
import io.legado.app.constant.ReadMenuBlurStyle
import io.legado.app.data.repository.ReadPreferences
import io.legado.app.data.repository.ReadStyleRepository
import io.legado.app.data.repository.dataStore
import io.legado.app.help.DefaultData
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.utils.GSON
@@ -26,7 +29,13 @@ import io.legado.app.utils.getPrefString
import io.legado.app.utils.hexString
import io.legado.app.utils.putPrefBoolean
import io.legado.app.utils.putPrefInt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import splitties.init.appCtx
import java.io.IOException
import java.io.InputStream
/**
@@ -85,6 +94,23 @@ object ReadBookConfig {
initShareConfig()
}
/**
* 从 DataStore 读取 Int 值,DS 无值时回退到 SharedPreferences。
* 与 PrefDelegate.readFromDs() 逻辑一致。
*/
private fun readIntFromDataStore(key: String, defaultValue: Int): Int {
return runBlocking(Dispatchers.IO) {
try {
appCtx.dataStore.data
.catch { if (it is IOException) emit(emptyPreferences()) else throw it }
.map { it[intPreferencesKey(key)] }
.first() ?: defaultValue
} catch (_: Exception) {
appCtx.getPrefInt(key, defaultValue)
}
}
}
@Synchronized
fun getConfig(index: Int): Config {
if (configList.size < 5) {
@@ -179,7 +205,7 @@ object ReadBookConfig {
}
}
private var readStyleSelectValue = appCtx.getPrefInt(PreferKey.readStyleSelect)
private var readStyleSelectValue = readIntFromDataStore(PreferKey.readStyleSelect, 0)
var readStyleSelect: Int
get() = readStyleSelectValue
set(value) {
@@ -189,7 +215,8 @@ object ReadBookConfig {
}
}
private var comicStyleSelectValue = appCtx.getPrefInt(PreferKey.comicStyleSelect, readStyleSelect)
private var comicStyleSelectValue =
readIntFromDataStore(PreferKey.comicStyleSelect, readStyleSelect)
var comicStyleSelect: Int
get() = comicStyleSelectValue
set(value) {
@@ -363,6 +363,7 @@ sealed interface ReadBookIntent {
data object SaveReadStyleConfig : ReadBookIntent
data object AddReadStyleConfig : ReadBookIntent
data object DeleteCurrentReadStyleConfig : ReadBookIntent
data class ApplyPresetTheme(val presetIndex: Int) : ReadBookIntent
// Bookshelf
data object RemoveFromBookshelf : ReadBookIntent
@@ -900,6 +901,9 @@ sealed interface ConfigUpdate {
data class StatusIconDark(val value: Boolean) : ConfigUpdate {
override val actions = setOf(ConfigUpdateAction.ReloadContent)
}
data class StyleName(val value: String) : ConfigUpdate {
override val actions = emptySet<ConfigUpdateAction>()
}
data class MenuIconShowText(val value: Boolean) : ConfigUpdate {
override val actions = emptySet<ConfigUpdateAction>()
}
@@ -28,6 +28,7 @@ import io.legado.app.domain.model.ReadingProgress
import io.legado.app.domain.usecase.GetReadingProgressUseCase
import io.legado.app.domain.usecase.UploadReadingProgressUseCase
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.DefaultData
import io.legado.app.help.book.BookHelp
import io.legado.app.help.book.ContentProcessor
import io.legado.app.help.book.isEpub
@@ -63,8 +64,8 @@ import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.utils.GSON
import io.legado.app.utils.ImageSaveUtils
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.StringUtils
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.hexString
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.isTrue
@@ -705,6 +706,26 @@ class ReadBookViewModel(
))
}
}
is ReadBookIntent.ApplyPresetTheme -> {
val presets = DefaultData.readConfigs
val preset = presets.getOrNull(intent.presetIndex) ?: return@onIntent
ReadBookConfig.durConfig =
GSON.fromJsonObject<ReadBookConfig.Config>(GSON.toJson(preset)).getOrNull()
?: return@onIntent
readBookStyleConfigRepository.save()
_uiState.update { it.copy(styleConfig = buildStyleConfig()) }
_effects.tryEmit(
ReadBookEffect.UpdateReadViewConfig(
setOf(
ConfigUpdateAction.UpdateBackground,
ConfigUpdateAction.UpdateBackgroundAlpha,
ConfigUpdateAction.UpdateStyle,
ConfigUpdateAction.UpdateSystemUi,
ConfigUpdateAction.ReloadContent
)
)
)
}
is ReadBookIntent.OpenBgTextConfig -> {
ReadBookConfig.styleSelect = intent.index
viewModelScope.launch {
@@ -2246,6 +2267,7 @@ class ReadBookViewModel(
is ConfigUpdate.BgTypeEInk -> ReadBookConfig.durConfig.bgTypeEInk = update.value
is ConfigUpdate.BgAlpha -> ReadBookConfig.bgAlpha = update.value
is ConfigUpdate.StatusIconDark -> ReadBookConfig.durConfig.setCurStatusIconDark(update.value)
is ConfigUpdate.StyleName -> ReadBookConfig.durConfig.name = update.value
is ConfigUpdate.MenuIconShowText -> {
ReadBookConfig.readMenuIconShowText = update.value
viewModelScope.launch {
@@ -1,6 +1,8 @@
package io.legado.app.ui.book.read.sheet
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -8,15 +10,17 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Upload
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -25,26 +29,28 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.core.graphics.toColorInt
import io.legado.app.R
import io.legado.app.help.DefaultData
import io.legado.app.ui.book.read.ConfigUpdate
import io.legado.app.ui.book.read.ReadBookColorPickerIds
import io.legado.app.ui.book.read.ReadBookIntent
import io.legado.app.ui.book.read.ReadBookStyleConfig
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.alert.AppAlertDialog
import io.legado.app.ui.widget.components.card.NormalCard
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
import io.legado.app.ui.widget.components.dialog.TextListInputDialog
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.settingItem.TinyBgImageModeSettingItem
import io.legado.app.ui.widget.components.settingItem.TinyClickableSettingItem
import io.legado.app.ui.widget.components.settingItem.TinyColorModeSettingItem
import io.legado.app.ui.widget.components.settingItem.TinySliderSettingItem
import io.legado.app.ui.widget.components.settingItem.TinySwitchSettingItem
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.hexString
@Composable
@@ -73,6 +79,8 @@ fun BgTextConfigSheet(
var showColorPicker by remember { mutableStateOf(false) }
var colorPickerIsNight by remember { mutableStateOf(false) }
var showEditNameDialog by remember { mutableStateOf(false) }
var showPresetDialog by remember { mutableStateOf(false) }
AppModalBottomSheet(
show = show,
@@ -80,7 +88,7 @@ fun BgTextConfigSheet(
onIntent(ReadBookIntent.SaveReadStyleConfig)
onDismissRequest()
},
title = stringResource(R.string.style_name),
title = styleName,
) {
Column(
modifier = Modifier
@@ -88,54 +96,28 @@ fun BgTextConfigSheet(
.padding(bottom = 16.dp)
.verticalScroll(rememberScrollState()),
) {
// Style name section
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(R.string.style_name),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
)
Text(
text = styleName,
style = MaterialTheme.typography.headlineMedium,
)
}
Row {
IconButton(onClick = {
// TODO: Show edit name dialog
}) {
Icon(
painter = painterResource(R.drawable.ic_edit),
contentDescription = stringResource(R.string.edit),
)
}
IconButton(onClick = {
onIntent(ReadBookIntent.DeleteCurrentReadStyleConfig)
}) {
Icon(
painter = painterResource(R.drawable.ic_clear_all),
contentDescription = stringResource(R.string.delete),
)
}
}
}
Spacer(Modifier.height(8.dp))
// Action buttons row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
ActionCard(
title = stringResource(R.string.edit),
imageVector = Icons.Default.Edit,
modifier = Modifier.weight(1f),
onClick = { showEditNameDialog = true },
)
ActionCard(
title = stringResource(R.string.delete),
imageVector = Icons.Default.Delete,
modifier = Modifier.weight(1f),
onClick = { onIntent(ReadBookIntent.DeleteCurrentReadStyleConfig) },
)
ActionCard(
title = stringResource(R.string.restore),
imageVector = Icons.Default.Refresh,
modifier = Modifier.weight(1f),
onClick = { /* TODO: Show restore presets dialog */ },
onClick = { showPresetDialog = true },
)
ActionCard(
title = stringResource(R.string.import_str),
@@ -251,6 +233,69 @@ fun BgTextConfigSheet(
},
)
}
TextListInputDialog(
show = showEditNameDialog,
title = stringResource(R.string.style_name),
hint = stringResource(R.string.style_name),
initialValue = styleName,
onDismissRequest = { showEditNameDialog = false },
onConfirm = { newName ->
onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.StyleName(newName)))
showEditNameDialog = false
},
)
val presets = DefaultData.readConfigs
AppAlertDialog(
show = showPresetDialog,
onDismissRequest = { showPresetDialog = false },
title = stringResource(R.string.restore),
content = {
presets.forEachIndexed { index, preset ->
val bgColor = runCatching { preset.bgStr.toColorInt() }
.getOrDefault(0xFFEEEEEE.toInt())
val textColor = runCatching { preset.curTextColor() }
.getOrDefault(0xFF000000.toInt())
NormalCard(
onClick = {
onIntent(ReadBookIntent.ApplyPresetTheme(index))
showPresetDialog = false
},
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp),
) {
Box(
modifier = Modifier
.size(24.dp)
.clip(CircleShape)
.background(Color(bgColor))
)
Spacer(Modifier.width(8.dp))
Box(
modifier = Modifier
.size(24.dp)
.clip(CircleShape)
.background(Color(textColor))
)
Spacer(Modifier.width(12.dp))
AppText(
text = preset.name.ifBlank { "预设${index}" },
style = LegadoTheme.typography.labelMediumEmphasized
)
}
}
}
},
)
}
@Composable