Browse Source

​fix: 优化书籍详情页封面背景设置与取色联动逻辑Fixes #1668

a328929 4 months ago
parent
commit
19942236de

+ 2 - 0
app/src/main/java/io/legado/app/constant/PreferKey.kt

@@ -206,6 +206,8 @@ object PreferKey {
     const val useFlexibleTopAppBar = "useFlexibleTopAppBar"
     const val bookInfoFollowCoverColor = "bookInfoFollowCoverColor"
     const val bookInfoBackgroundBlur = "bookInfoBackgroundBlur"
+    const val bookInfoNetworkCoverBackground = "bookInfoNetworkCoverBackground"
+    const val bookInfoDefaultCoverBackground = "bookInfoDefaultCoverBackground"
     const val cBackground = "colorBackground"
     const val cBBackground = "colorBottomBackground"
     const val enableDeepPersonalization = "enableDeepPersonalization"

+ 8 - 0
app/src/main/java/io/legado/app/help/config/ThemeImportExport.kt

@@ -153,6 +153,8 @@ object ThemeImportExport {
             bookInfoInputColor = ThemeConfig.bookInfoInputColor,
             bookInfoFollowCoverColor = ThemeConfig.bookInfoFollowCoverColor,
             bookInfoBackgroundBlur = ThemeConfig.bookInfoBackgroundBlur,
+            bookInfoNetworkCoverBackground = ThemeConfig.bookInfoNetworkCoverBackground,
+            bookInfoDefaultCoverBackground = ThemeConfig.bookInfoDefaultCoverBackground,
 
             // 容器设置
             containerOpacity = ThemeConfig.containerOpacity,
@@ -335,6 +337,10 @@ object ThemeImportExport {
         ThemeConfig.bookInfoInputColor = data.bookInfoInputColor
         ThemeConfig.bookInfoFollowCoverColor = data.bookInfoFollowCoverColor
         ThemeConfig.bookInfoBackgroundBlur = data.bookInfoBackgroundBlur
+        ThemeConfig.bookInfoNetworkCoverBackground =
+            data.bookInfoNetworkCoverBackground ?: data.bookInfoBackgroundBlur
+        ThemeConfig.bookInfoDefaultCoverBackground =
+            data.bookInfoDefaultCoverBackground ?: data.bookInfoBackgroundBlur
 
         // 容器设置
         ThemeConfig.containerOpacity = data.containerOpacity
@@ -725,6 +731,8 @@ data class ThemeExportData(
     val bookInfoInputColor: Int = 0,
     val bookInfoFollowCoverColor: Boolean = true,
     val bookInfoBackgroundBlur: String = ThemeConfig.BOOK_INFO_BACKGROUND_BLUR_ON,
+    val bookInfoNetworkCoverBackground: String? = null,
+    val bookInfoDefaultCoverBackground: String? = null,
 
     // 容器设置
     val containerOpacity: Int = 100,

+ 2 - 0
app/src/main/java/io/legado/app/help/storage/BackupConfig.kt

@@ -112,6 +112,8 @@ object BackupConfig {
         PreferKey.useFlexibleTopAppBar,
         PreferKey.bookInfoFollowCoverColor,
         PreferKey.bookInfoBackgroundBlur,
+        PreferKey.bookInfoNetworkCoverBackground,
+        PreferKey.bookInfoDefaultCoverBackground,
         PreferKey.cBackground,
         PreferKey.cBBackground,
         PreferKey.cNBackground,

+ 19 - 0
app/src/main/java/io/legado/app/help/storage/Restore.kt

@@ -396,6 +396,15 @@ object Restore : KoinComponent {
 
     private suspend fun applyConfigMap(map: Map<String, Any?>, aes: BackupAES) {
         val finalMap = mutableMapOf<String, Any>()
+        val legacyBookInfoBackground = map[PreferKey.bookInfoBackgroundBlur] as? String
+        val restoreNetworkCoverBackground =
+            legacyBookInfoBackground != null &&
+                    PreferKey.bookInfoNetworkCoverBackground !in map &&
+                    BackupConfig.keyIsNotIgnore(PreferKey.bookInfoBackgroundBlur)
+        val restoreDefaultCoverBackground =
+            legacyBookInfoBackground != null &&
+                    PreferKey.bookInfoDefaultCoverBackground !in map &&
+                    BackupConfig.keyIsNotIgnore(PreferKey.bookInfoBackgroundBlur)
         appCtx.defaultSharedPreferences.edit {
             map.forEach { (key, value) ->
                 if (BackupConfig.keyIsNotIgnore(key)) {
@@ -433,6 +442,16 @@ object Restore : KoinComponent {
                     }
                 }
             }
+            legacyBookInfoBackground?.let { backgroundMode ->
+                if (restoreNetworkCoverBackground) {
+                    putString(PreferKey.bookInfoNetworkCoverBackground, backgroundMode)
+                    finalMap[PreferKey.bookInfoNetworkCoverBackground] = backgroundMode
+                }
+                if (restoreDefaultCoverBackground) {
+                    putString(PreferKey.bookInfoDefaultCoverBackground, backgroundMode)
+                    finalMap[PreferKey.bookInfoDefaultCoverBackground] = backgroundMode
+                }
+            }
         }
         // 同步恢复到 DataStore
         settingsRepository.batchPutFromMap(finalMap)

+ 27 - 0
app/src/main/java/io/legado/app/ui/book/info/BookInfoBackdropStyle.kt

@@ -0,0 +1,27 @@
+package io.legado.app.ui.book.info
+
+import io.legado.app.ui.config.themeConfig.ThemeConfig
+
+internal data class BookInfoBackdropStyle(
+    val showCover: Boolean,
+    val blurCover: Boolean,
+)
+
+internal fun resolveBookInfoBackdropStyle(backgroundMode: String): BookInfoBackdropStyle {
+    return when (backgroundMode) {
+        ThemeConfig.BOOK_INFO_BACKGROUND_BLUR_OFF -> BookInfoBackdropStyle(
+            showCover = true,
+            blurCover = false,
+        )
+
+        ThemeConfig.BOOK_INFO_BACKGROUND_COVER_HIDDEN -> BookInfoBackdropStyle(
+            showCover = false,
+            blurCover = false,
+        )
+
+        else -> BookInfoBackdropStyle(
+            showCover = true,
+            blurCover = true,
+        )
+    }
+}

+ 93 - 27
app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt

@@ -153,9 +153,24 @@ fun BookInfoScreen(
             canApplyCoverTheme = true
         }
     }
-    val bookColorTheme = rememberBookInfoColorTheme(state.book)
+    val initiallyUsesDefaultCover = state.book?.let { usesDefaultBookCover(it.coverPath) } ?: true
+    var usesDefaultCover by remember(
+        state.book?.bookUrl,
+        state.book?.coverPath,
+        initiallyUsesDefaultCover,
+    ) {
+        mutableStateOf(initiallyUsesDefaultCover)
+    }
+    val backdropStyle = state.book?.let { resolveBookInfoBackdropStyle(it, usesDefaultCover) }
+    val bookColorTheme = rememberBookInfoColorTheme(
+        book = state.book,
+        enabled = backdropStyle?.showCover == true,
+        usesDefaultCover = usesDefaultCover,
+    )
 
-    BookInfoColorTheme(theme = bookColorTheme.takeIf { canApplyCoverTheme }) {
+    BookInfoColorTheme(theme = bookColorTheme.takeIf {
+        canApplyCoverTheme && backdropStyle?.showCover == true
+    }) {
         BookInfoScreenContent(
             state = state,
             onIntent = onIntent,
@@ -163,6 +178,13 @@ fun BookInfoScreen(
             sharedTransitionScope = sharedTransitionScope,
             animatedVisibilityScope = animatedVisibilityScope,
             sharedCoverKey = sharedCoverKey,
+            backdropStyle = backdropStyle,
+            usesDefaultCover = usesDefaultCover,
+            onNetworkCoverLoadError = { failedCoverPath ->
+                if (failedCoverPath == state.book?.coverPath) {
+                    usesDefaultCover = true
+                }
+            },
         )
     }
 }
@@ -178,6 +200,9 @@ private fun BookInfoScreenContent(
     sharedTransitionScope: SharedTransitionScope?,
     animatedVisibilityScope: AnimatedVisibilityScope?,
     sharedCoverKey: String?,
+    backdropStyle: BookInfoBackdropStyle?,
+    usesDefaultCover: Boolean,
+    onNetworkCoverLoadError: (String?) -> Unit,
 ) {
     val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
     val scrollBehavior = if (isMiuix) {
@@ -217,9 +242,13 @@ private fun BookInfoScreenContent(
         if (book == null) {
             Box(modifier = Modifier.fillMaxSize())
         } else {
+            val resolvedBackdropStyle = requireNotNull(backdropStyle)
             Box(modifier = Modifier.fillMaxSize()) {
                 BookInfoBackdrop(
                     book = book,
+                    style = resolvedBackdropStyle,
+                    usesDefaultCover = usesDefaultCover,
+                    onNetworkCoverLoadError = onNetworkCoverLoadError,
                 )
                 AppPullToRefresh(
                     modifier = Modifier.fillMaxSize(),
@@ -247,6 +276,11 @@ private fun BookInfoScreenContent(
                                 onAuthorClick = { onIntent(BookInfoIntent.AuthorClick(it)) },
                                 onBookNameClick = { onIntent(BookInfoIntent.BookNameClick(it)) },
                                 onOriginClick = { onIntent(BookInfoIntent.OriginClick) },
+                                onNetworkCoverLoadError = {
+                                    onNetworkCoverLoadError(book.coverPath)
+                                },
+                                usesDefaultCover = usesDefaultCover,
+                                blurCover = resolvedBackdropStyle.blurCover,
                                 sharedTransitionScope = sharedTransitionScope,
                                 animatedVisibilityScope = animatedVisibilityScope,
                                 sharedCoverKey = sharedCoverKey,
@@ -482,9 +516,12 @@ private fun BookInfoTransparentTopAppBar(
 @Composable
 private fun rememberBookInfoColorTheme(
     book: BookInfoBookUi?,
+    enabled: Boolean,
+    usesDefaultCover: Boolean,
 ): ThemeOverrideState? {
     if (
         book == null ||
+        !enabled ||
         !ThemeConfig.bookInfoFollowCoverColor
     ) {
         return null
@@ -492,18 +529,17 @@ private fun rememberBookInfoColorTheme(
 
     val imageLoader = koinInject<ImageLoader>()
     val isNight = LegadoTheme.isDark
-    val useDefaultCover = usesDefaultBookCover(book.coverPath)
     val defaultCoverPaths =
         if (isNight) CoverConfig.defaultCoverDark else CoverConfig.defaultCover
     val coverPath = remember(
         book.name,
         book.author,
         book.coverPath,
-        useDefaultCover,
+        usesDefaultCover,
         isNight,
         defaultCoverPaths,
     ) {
-        if (useDefaultCover) {
+        if (usesDefaultCover) {
             BookCoverModel.getRandomDefaultPath(
                 seed = book.name,
                 isNight = isNight,
@@ -512,8 +548,8 @@ private fun rememberBookInfoColorTheme(
             book.coverPath
         }
     } ?: return null
-    val sourceOrigin = if (useDefaultCover) null else book.origin
-    val loadOnlyWifi = !useDefaultCover && CoverConfig.loadCoverOnlyWifi
+    val sourceOrigin = if (usesDefaultCover) null else book.origin
+    val loadOnlyWifi = !usesDefaultCover && CoverConfig.loadCoverOnlyWifi
     val requestKey = remember(coverPath, sourceOrigin, loadOnlyWifi) {
         listOf(coverPath, sourceOrigin, loadOnlyWifi)
     }
@@ -530,6 +566,18 @@ private fun rememberBookInfoColorTheme(
     return rememberThemeOverride(seedColor)
 }
 
+private fun resolveBookInfoBackdropStyle(
+    book: BookInfoBookUi,
+    usesDefaultCover: Boolean,
+): BookInfoBackdropStyle {
+    val backgroundMode = if (usesDefaultCover) {
+        ThemeConfig.bookInfoDefaultCoverBackground
+    } else {
+        ThemeConfig.bookInfoNetworkCoverBackground
+    }
+    return resolveBookInfoBackdropStyle(backgroundMode)
+}
+
 @Composable
 private fun BookInfoTopBarActions(
     state: BookInfoUiState,
@@ -568,23 +616,22 @@ private fun BookInfoTopBarActions(
 @Composable
 private fun BookInfoBackdrop(
     book: BookInfoBookUi,
+    style: BookInfoBackdropStyle,
+    usesDefaultCover: Boolean,
+    onNetworkCoverLoadError: (String?) -> Unit,
 ) {
-    val backgroundMode = ThemeConfig.bookInfoBackgroundBlur
-    val showBackgroundCover =
-        backgroundMode != ThemeConfig.BOOK_INFO_BACKGROUND_COVER_HIDDEN
-    val blurBackground = backgroundMode != ThemeConfig.BOOK_INFO_BACKGROUND_BLUR_OFF
-
     val backdropState = remember(
         book.name,
         book.author,
         book.coverPath,
         book.origin,
+        usesDefaultCover,
     ) {
         BookInfoBackdropState(
             name = book.name,
             author = book.author,
-            coverPath = book.coverPath,
-            sourceOrigin = book.origin,
+            coverPath = if (usesDefaultCover) null else book.coverPath,
+            sourceOrigin = if (usesDefaultCover) null else book.origin,
         )
     }
     val seedOverlay = lerp(
@@ -597,7 +644,7 @@ private fun BookInfoBackdrop(
             .fillMaxSize()
             .clearAndSetSemantics { }
     ) {
-        if (showBackgroundCover) {
+        if (style.showCover) {
             Crossfade(
                 targetState = backdropState,
                 animationSpec = tween(800),
@@ -613,7 +660,7 @@ private fun BookInfoBackdrop(
                         .fillMaxWidth()
                         .height(480.dp)
                         .then(
-                            if (blurBackground) {
+                            if (style.blurCover) {
                                 Modifier.blur(24.dp)
                             } else {
                                 Modifier
@@ -621,17 +668,20 @@ private fun BookInfoBackdrop(
                         ),
                     contentScale = ContentScale.Crop,
                     showLoadingPlaceholder = false,
+                    onError = { onNetworkCoverLoadError(currentBook.coverPath) },
                     requestBuilder = {
                         size(Size(384, 384))
                     }
                 )
             }
         }
-        Box(
-            modifier = Modifier
-                .fillMaxSize()
-                .background(seedOverlay.copy(alpha = 0.34f))
-        )
+        if (style.blurCover) {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(seedOverlay.copy(alpha = 0.34f))
+            )
+        }
         Box(
             modifier = Modifier
                 .fillMaxSize()
@@ -639,8 +689,16 @@ private fun BookInfoBackdrop(
                     Brush.verticalGradient(
                         colorStops = arrayOf(
                             0f to Color.Transparent,
-                            0.20f to seedOverlay.copy(alpha = 0.10f),
-                            0.40f to seedOverlay.copy(alpha = 0.18f),
+                            0.20f to if (style.blurCover) {
+                                seedOverlay.copy(alpha = 0.10f)
+                            } else {
+                                Color.Transparent
+                            },
+                            0.40f to if (style.blurCover) {
+                                seedOverlay.copy(alpha = 0.18f)
+                            } else {
+                                LegadoTheme.colorScheme.surface.copy(alpha = 0.35f)
+                            },
                             0.60f to LegadoTheme.colorScheme.surface.copy(alpha = 0.85f),
                             0.80f to LegadoTheme.colorScheme.surface,
                             1f to LegadoTheme.colorScheme.surface,
@@ -760,6 +818,9 @@ private fun BookInfoHeader(
     onAuthorClick: (Boolean) -> Unit,
     onBookNameClick: (Boolean) -> Unit,
     onOriginClick: () -> Unit,
+    onNetworkCoverLoadError: () -> Unit,
+    usesDefaultCover: Boolean,
+    blurCover: Boolean,
     sharedTransitionScope: SharedTransitionScope?,
     animatedVisibilityScope: AnimatedVisibilityScope?,
     sharedCoverKey: String?,
@@ -772,8 +833,12 @@ private fun BookInfoHeader(
                 Brush.verticalGradient(
                     colors = listOf(
                         Color.Transparent,
-                        lerp(LegadoTheme.colorScheme.surface, LegadoTheme.seedColor, 0.08f)
-                            .copy(alpha = 0.5f),
+                        if (blurCover) {
+                            lerp(LegadoTheme.colorScheme.surface, LegadoTheme.seedColor, 0.08f)
+                                .copy(alpha = 0.5f)
+                        } else {
+                            LegadoTheme.colorScheme.surface.copy(alpha = 0.5f)
+                        },
                         LegadoTheme.colorScheme.surface,
                     )
                 )
@@ -802,8 +867,9 @@ private fun BookInfoHeader(
                     CoilBookCover(
                         name = book.name,
                         author = book.author,
-                        path = book.coverPath,
-                        sourceOrigin = book.origin,
+                        path = if (usesDefaultCover) null else book.coverPath,
+                        sourceOrigin = if (usesDefaultCover) null else book.origin,
+                        onError = onNetworkCoverLoadError,
                         modifier = Modifier
                             .width(112.dp)
                             .aspectRatio(5f / 7f),

+ 10 - 0
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfig.kt

@@ -68,6 +68,16 @@ object ThemeConfig {
         BOOK_INFO_BACKGROUND_BLUR_ON
     )
 
+    var bookInfoNetworkCoverBackground by prefDelegate(
+        PreferKey.bookInfoNetworkCoverBackground,
+        bookInfoBackgroundBlur
+    )
+
+    var bookInfoDefaultCoverBackground by prefDelegate(
+        PreferKey.bookInfoDefaultCoverBackground,
+        bookInfoBackgroundBlur
+    )
+
     var paletteStyle by prefDelegate(PreferKey.paletteStyle, "tonalSpot")
 
     //m3 or miuix

+ 10 - 3
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt

@@ -458,11 +458,18 @@ fun ThemeConfigScreen(
                         onCheckedChange = { ThemeConfig.bookInfoFollowCoverColor = it }
                     )
                     DropdownListSettingItem(
-                        title = stringResource(R.string.book_info_background_blur),
-                        selectedValue = ThemeConfig.bookInfoBackgroundBlur,
+                        title = stringResource(R.string.book_info_network_cover_background),
+                        selectedValue = ThemeConfig.bookInfoNetworkCoverBackground,
                         displayEntries = stringArrayResource(R.array.book_info_background_blur_entries),
                         entryValues = stringArrayResource(R.array.book_info_background_blur_values),
-                        onValueChange = { ThemeConfig.bookInfoBackgroundBlur = it }
+                        onValueChange = { ThemeConfig.bookInfoNetworkCoverBackground = it }
+                    )
+                    DropdownListSettingItem(
+                        title = stringResource(R.string.book_info_default_cover_background),
+                        selectedValue = ThemeConfig.bookInfoDefaultCoverBackground,
+                        displayEntries = stringArrayResource(R.array.book_info_background_blur_entries),
+                        entryValues = stringArrayResource(R.array.book_info_background_blur_values),
+                        onValueChange = { ThemeConfig.bookInfoDefaultCoverBackground = it }
                     )
                 }
 

+ 2 - 0
app/src/main/java/io/legado/app/ui/widget/components/image/cover/CoilBookCover.kt

@@ -196,6 +196,7 @@ fun CoilBookCover(
     modifier: Modifier = Modifier.width(64.dp),
     sourceOrigin: String? = null,
     onLoadFinish: (() -> Unit)? = null,
+    onError: (() -> Unit)? = null,
     ignoreUseDefaultCover: Boolean = false,
     showLoadingPlaceholder: Boolean = true,
     sharedTransitionScope: SharedTransitionScope? = null,
@@ -279,6 +280,7 @@ fun CoilBookCover(
             onError = {
                 isOnlineCoverLoaded = false
                 onlineCoverLoadFailed = true
+                onError?.invoke()
                 onLoadFinish?.invoke()
             },
             sharedCoverKey = sharedCoverKey

+ 3 - 2
app/src/main/res/values-zh-rCN/strings.xml

@@ -1481,8 +1481,9 @@
     <string name="theme_manage_section_blur">模糊效果</string>
     <string name="book_info_page">书籍详情页</string>
     <string name="book_info_follow_cover_color">界面颜色跟随封面取色</string>
-    <string name="book_info_follow_cover_color_summary">从书籍封面提取详情页配色</string>
-    <string name="book_info_background_blur">背景设置</string>
+    <string name="book_info_follow_cover_color_summary">仅在显示背景封面时生效</string>
+    <string name="book_info_network_cover_background">网络封面背景设置</string>
+    <string name="book_info_default_cover_background">默认封面背景设置</string>
     <string name="book_info_background_blur_off">关闭模糊封面</string>
     <string name="book_info_background_blur_on">开启模糊封面</string>
     <string name="book_info_background_blur_off_for_default">不显示背景封面</string>

+ 3 - 2
app/src/main/res/values-zh-rHK/strings.xml

@@ -2151,8 +2151,9 @@
     <string name="theme_manage_section_blur">模糊效果</string>
     <string name="book_info_page">書籍詳情頁</string>
     <string name="book_info_follow_cover_color">介面顏色跟隨封面取色</string>
-    <string name="book_info_follow_cover_color_summary">從書籍封面擷取詳情頁配色</string>
-    <string name="book_info_background_blur">背景設定</string>
+    <string name="book_info_follow_cover_color_summary">僅在顯示背景封面時生效</string>
+    <string name="book_info_network_cover_background">網路封面背景設定</string>
+    <string name="book_info_default_cover_background">預設封面背景設定</string>
     <string name="book_info_background_blur_off">關閉模糊封面</string>
     <string name="book_info_background_blur_on">開啟模糊封面</string>
     <string name="book_info_background_blur_off_for_default">不顯示背景封面</string>

+ 3 - 2
app/src/main/res/values-zh-rTW/strings.xml

@@ -1625,8 +1625,9 @@
     <string name="theme_manage_section_blur">模糊效果</string>
     <string name="book_info_page">書籍詳情頁</string>
     <string name="book_info_follow_cover_color">介面顏色跟隨封面取色</string>
-    <string name="book_info_follow_cover_color_summary">從書籍封面擷取詳情頁配色</string>
-    <string name="book_info_background_blur">背景設定</string>
+    <string name="book_info_follow_cover_color_summary">僅在顯示背景封面時生效</string>
+    <string name="book_info_network_cover_background">網路封面背景設定</string>
+    <string name="book_info_default_cover_background">預設封面背景設定</string>
     <string name="book_info_background_blur_off">關閉模糊封面</string>
     <string name="book_info_background_blur_on">開啟模糊封面</string>
     <string name="book_info_background_blur_off_for_default">不顯示背景封面</string>

+ 3 - 2
app/src/main/res/values/strings.xml

@@ -1516,8 +1516,9 @@
     <string name="theme_manage_section_blur">Blur Effects</string>
     <string name="book_info_page">Book details</string>
     <string name="book_info_follow_cover_color">Use colors from book cover</string>
-    <string name="book_info_follow_cover_color_summary">Generate the book details color scheme from the cover</string>
-    <string name="book_info_background_blur">Background setting</string>
+    <string name="book_info_follow_cover_color_summary">Applied only while a background cover is visible</string>
+    <string name="book_info_network_cover_background">Network cover background</string>
+    <string name="book_info_default_cover_background">Default cover background</string>
     <string name="book_info_background_blur_off">Show cover without blur</string>
     <string name="book_info_background_blur_on">Show blurred cover</string>
     <string name="book_info_background_blur_off_for_default">Hide background cover</string>

+ 23 - 0
app/src/test/java/io/legado/app/ui/book/info/BookInfoBackdropStyleTest.kt

@@ -0,0 +1,23 @@
+package io.legado.app.ui.book.info
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class BookInfoBackdropStyleTest {
+
+    @Test
+    fun backgroundModesKeepTheirExpectedCoverTreatment() {
+        assertEquals(
+            BookInfoBackdropStyle(showCover = true, blurCover = false),
+            resolveBookInfoBackdropStyle("off")
+        )
+        assertEquals(
+            BookInfoBackdropStyle(showCover = true, blurCover = true),
+            resolveBookInfoBackdropStyle("on")
+        )
+        assertEquals(
+            BookInfoBackdropStyle(showCover = false, blurCover = false),
+            resolveBookInfoBackdropStyle("off_for_default")
+        )
+    }
+}