fix: 阅读背景问题

This commit is contained in:
HapeLee committed 2026-06-09 00:16:36 +08:00
1 parent 43fc1e8614
commit d0be59f607
5 files changed
+148 -49

No files matched your search

@@ -58,4 +58,5 @@ class ReadBookStyleConfigRepository {
} }
save() save()
} }
} }
@@ -7,6 +7,8 @@ import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.annotation.Keep import androidx.annotation.Keep
import androidx.core.graphics.toColorInt 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.R
import io.legado.app.constant.PageAnim import io.legado.app.constant.PageAnim
import io.legado.app.constant.PreferKey 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.constant.ReadMenuBlurStyle
import io.legado.app.data.repository.ReadPreferences import io.legado.app.data.repository.ReadPreferences
import io.legado.app.data.repository.ReadStyleRepository 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.DefaultData
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.utils.GSON 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.hexString
import io.legado.app.utils.putPrefBoolean import io.legado.app.utils.putPrefBoolean
import io.legado.app.utils.putPrefInt 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 splitties.init.appCtx
import java.io.IOException
import java.io.InputStream import java.io.InputStream
/** /**
@@ -85,6 +94,23 @@ object ReadBookConfig {
initShareConfig() 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 @Synchronized
fun getConfig(index: Int): Config { fun getConfig(index: Int): Config {
if (configList.size < 5) { 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 var readStyleSelect: Int
get() = readStyleSelectValue get() = readStyleSelectValue
set(value) { 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 var comicStyleSelect: Int
get() = comicStyleSelectValue get() = comicStyleSelectValue
set(value) { set(value) {
@@ -363,6 +363,7 @@ sealed interface ReadBookIntent {
data object SaveReadStyleConfig : ReadBookIntent data object SaveReadStyleConfig : ReadBookIntent
data object AddReadStyleConfig : ReadBookIntent data object AddReadStyleConfig : ReadBookIntent
data object DeleteCurrentReadStyleConfig : ReadBookIntent data object DeleteCurrentReadStyleConfig : ReadBookIntent
data class ApplyPresetTheme(val presetIndex: Int) : ReadBookIntent
// Bookshelf // Bookshelf
data object RemoveFromBookshelf : ReadBookIntent data object RemoveFromBookshelf : ReadBookIntent
@@ -900,6 +901,9 @@ sealed interface ConfigUpdate {
data class StatusIconDark(val value: Boolean) : ConfigUpdate { data class StatusIconDark(val value: Boolean) : ConfigUpdate {
override val actions = setOf(ConfigUpdateAction.ReloadContent) 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 { data class MenuIconShowText(val value: Boolean) : ConfigUpdate {
override val actions = emptySet<ConfigUpdateAction>() 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.GetReadingProgressUseCase
import io.legado.app.domain.usecase.UploadReadingProgressUseCase import io.legado.app.domain.usecase.UploadReadingProgressUseCase
import io.legado.app.exception.NoStackTraceException 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.BookHelp
import io.legado.app.help.book.ContentProcessor import io.legado.app.help.book.ContentProcessor
import io.legado.app.help.book.isEpub 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.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
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.StringUtils import io.legado.app.utils.StringUtils
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.hexString import io.legado.app.utils.hexString
import io.legado.app.utils.isAbsUrl import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.isTrue 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 -> { is ReadBookIntent.OpenBgTextConfig -> {
ReadBookConfig.styleSelect = intent.index ReadBookConfig.styleSelect = intent.index
viewModelScope.launch { viewModelScope.launch {
@@ -2246,6 +2267,7 @@ class ReadBookViewModel(
is ConfigUpdate.BgTypeEInk -> ReadBookConfig.durConfig.bgTypeEInk = update.value is ConfigUpdate.BgTypeEInk -> ReadBookConfig.durConfig.bgTypeEInk = update.value
is ConfigUpdate.BgAlpha -> ReadBookConfig.bgAlpha = update.value is ConfigUpdate.BgAlpha -> ReadBookConfig.bgAlpha = update.value
is ConfigUpdate.StatusIconDark -> ReadBookConfig.durConfig.setCurStatusIconDark(update.value) is ConfigUpdate.StatusIconDark -> ReadBookConfig.durConfig.setCurStatusIconDark(update.value)
is ConfigUpdate.StyleName -> ReadBookConfig.durConfig.name = update.value
is ConfigUpdate.MenuIconShowText -> { is ConfigUpdate.MenuIconShowText -> {
ReadBookConfig.readMenuIconShowText = update.value ReadBookConfig.readMenuIconShowText = update.value
viewModelScope.launch { viewModelScope.launch {
@@ -1,6 +1,8 @@
package io.legado.app.ui.book.read.sheet package io.legado.app.ui.book.read.sheet
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer 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.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons 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.Download
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Upload import androidx.compose.material.icons.filled.Upload
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -25,26 +29,28 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.graphics.toColorInt import androidx.core.graphics.toColorInt
import io.legado.app.R 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.ConfigUpdate
import io.legado.app.ui.book.read.ReadBookColorPickerIds
import io.legado.app.ui.book.read.ReadBookIntent import io.legado.app.ui.book.read.ReadBookIntent
import io.legado.app.ui.book.read.ReadBookStyleConfig import io.legado.app.ui.book.read.ReadBookStyleConfig
import io.legado.app.ui.theme.LegadoTheme 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.card.NormalCard
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet 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.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.settingItem.TinyBgImageModeSettingItem 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.TinyColorModeSettingItem
import io.legado.app.ui.widget.components.settingItem.TinySliderSettingItem 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.settingItem.TinySwitchSettingItem
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.hexString import io.legado.app.utils.hexString
@Composable @Composable
@@ -73,6 +79,8 @@ fun BgTextConfigSheet(
var showColorPicker by remember { mutableStateOf(false) } var showColorPicker by remember { mutableStateOf(false) }
var colorPickerIsNight by remember { mutableStateOf(false) } var colorPickerIsNight by remember { mutableStateOf(false) }
var showEditNameDialog by remember { mutableStateOf(false) }
var showPresetDialog by remember { mutableStateOf(false) }
AppModalBottomSheet( AppModalBottomSheet(
show = show, show = show,
@@ -80,7 +88,7 @@ fun BgTextConfigSheet(
onIntent(ReadBookIntent.SaveReadStyleConfig) onIntent(ReadBookIntent.SaveReadStyleConfig)
onDismissRequest() onDismissRequest()
}, },
title = stringResource(R.string.style_name), title = styleName,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -88,54 +96,28 @@ fun BgTextConfigSheet(
.padding(bottom = 16.dp) .padding(bottom = 16.dp)
.verticalScroll(rememberScrollState()), .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 // Action buttons row
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(4.dp), 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( ActionCard(
title = stringResource(R.string.restore), title = stringResource(R.string.restore),
imageVector = Icons.Default.Refresh, imageVector = Icons.Default.Refresh,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
onClick = { /* TODO: Show restore presets dialog */ }, onClick = { showPresetDialog = true },
) )
ActionCard( ActionCard(
title = stringResource(R.string.import_str), 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 @Composable