Bläddra i källkod

fix: 阅读背景问题

HapeLee 5 månader sedan
förälder
incheckning
d0be59f607

+ 1 - 0
app/src/main/java/io/legado/app/data/repository/ReadBookStyleConfigRepository.kt

@@ -58,4 +58,5 @@ class ReadBookStyleConfigRepository {
         }
         }
         save()
         save()
     }
     }
+
 }
 }

+ 29 - 2
app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt

@@ -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) {

+ 4 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt

@@ -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>()
     }
     }

+ 23 - 1
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -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 {

+ 91 - 46
app/src/main/java/io/legado/app/ui/book/read/sheet/BgTextConfigSheet.kt

@@ -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