From 6f87071a854bbf0c06c8f1c23c792692237b21d1 Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Thu, 2 Jul 2026 22:59:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E4=B9=A6=E7=B1=8D?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E5=B0=81=E9=9D=A2=E4=B8=BB=E9=A2=98=E4=B8=8E?= =?UTF-8?q?=E8=83=8C=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/constant/PreferKey.kt | 2 + .../app/help/config/ThemeImportExport.kt | 6 ++ .../legado/app/help/storage/BackupConfig.kt | 2 + .../legado/app/ui/book/info/BookInfoScreen.kt | 92 +++++++++++++++++-- .../app/ui/config/themeConfig/ThemeConfig.kt | 11 +++ .../config/themeConfig/ThemeConfigScreen.kt | 16 ++++ .../components/image/cover/CoilBookCover.kt | 61 +++++++++--- app/src/main/res/values-zh-rCN/strings.xml | 7 ++ app/src/main/res/values-zh-rHK/strings.xml | 7 ++ app/src/main/res/values-zh-rTW/strings.xml | 7 ++ app/src/main/res/values/array_values.xml | 8 +- app/src/main/res/values/arrays.xml | 6 ++ app/src/main/res/values/strings.xml | 7 ++ 13 files changed, 210 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index 3179671a6..d82bbd94f 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -204,6 +204,8 @@ object PreferKey { const val bottomBarBlurAlpha = "bottomBarBlurAlpha" const val bottomBarLensRadius = "bottomBarLensRadius" const val useFlexibleTopAppBar = "useFlexibleTopAppBar" + const val bookInfoFollowCoverColor = "bookInfoFollowCoverColor" + const val bookInfoBackgroundBlur = "bookInfoBackgroundBlur" const val cBackground = "colorBackground" const val cBBackground = "colorBottomBackground" const val enableDeepPersonalization = "enableDeepPersonalization" diff --git a/app/src/main/java/io/legado/app/help/config/ThemeImportExport.kt b/app/src/main/java/io/legado/app/help/config/ThemeImportExport.kt index f069d7fda..a9391a965 100644 --- a/app/src/main/java/io/legado/app/help/config/ThemeImportExport.kt +++ b/app/src/main/java/io/legado/app/help/config/ThemeImportExport.kt @@ -157,6 +157,8 @@ object ThemeImportExport { themeBackgroundColorNight = ThemeConfig.themeBackgroundColorNight, labelContainerColorNight = ThemeConfig.labelContainerColorNight, bookInfoInputColor = ThemeConfig.bookInfoInputColor, + bookInfoFollowCoverColor = ThemeConfig.bookInfoFollowCoverColor, + bookInfoBackgroundBlur = ThemeConfig.bookInfoBackgroundBlur, // 容器设置 containerOpacity = ThemeConfig.containerOpacity, @@ -334,6 +336,8 @@ object ThemeImportExport { ThemeConfig.themeBackgroundColorNight = data.themeBackgroundColorNight ThemeConfig.labelContainerColorNight = data.labelContainerColorNight ThemeConfig.bookInfoInputColor = data.bookInfoInputColor + ThemeConfig.bookInfoFollowCoverColor = data.bookInfoFollowCoverColor + ThemeConfig.bookInfoBackgroundBlur = data.bookInfoBackgroundBlur // 容器设置 ThemeConfig.containerOpacity = data.containerOpacity @@ -697,6 +701,8 @@ data class ThemeExportData( val themeBackgroundColorNight: Int = 0, val labelContainerColorNight: Int = 0, val bookInfoInputColor: Int = 0, + val bookInfoFollowCoverColor: Boolean = true, + val bookInfoBackgroundBlur: String = ThemeConfig.BOOK_INFO_BACKGROUND_BLUR_ON, // 容器设置 val containerOpacity: Int = 100, diff --git a/app/src/main/java/io/legado/app/help/storage/BackupConfig.kt b/app/src/main/java/io/legado/app/help/storage/BackupConfig.kt index 6cc4a928a..32537bec8 100644 --- a/app/src/main/java/io/legado/app/help/storage/BackupConfig.kt +++ b/app/src/main/java/io/legado/app/help/storage/BackupConfig.kt @@ -110,6 +110,8 @@ object BackupConfig { PreferKey.bottomBarBlurAlpha, PreferKey.bottomBarLensRadius, PreferKey.useFlexibleTopAppBar, + PreferKey.bookInfoFollowCoverColor, + PreferKey.bookInfoBackgroundBlur, PreferKey.cBackground, PreferKey.cBBackground, PreferKey.cNBackground, diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt index 7c2616a22..c36673dde 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt @@ -2,8 +2,11 @@ package io.legado.app.ui.book.info import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.Crossfade +import androidx.compose.animation.EnterExitState import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionScope +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -68,14 +71,16 @@ import coil.size.Size import io.legado.app.R import io.legado.app.constant.BookType import io.legado.app.data.entities.SearchBook -import io.legado.app.help.config.AppConfig import io.legado.app.ui.config.coverConfig.CoverConfig +import io.legado.app.ui.config.themeConfig.ThemeConfig import io.legado.app.ui.main.homepage.modules.BannerModule import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LocalHazeState -import io.legado.app.ui.theme.ProvideThemeOverride +import io.legado.app.ui.theme.LocalLegadoThemeColors +import io.legado.app.ui.theme.ProvideColorSchemeOverride import io.legado.app.ui.theme.ThemeOverrideState import io.legado.app.ui.theme.ThemeResolver +import io.legado.app.ui.theme.animateColorSchemeAsState import io.legado.app.ui.theme.fadingEdge import io.legado.app.ui.theme.rememberImageSeedColor import io.legado.app.ui.theme.rememberThemeOverride @@ -92,6 +97,7 @@ import io.legado.app.ui.widget.components.changeSource.ChangeSourceSheet import io.legado.app.ui.widget.components.icon.AppIcon import io.legado.app.ui.widget.components.image.cover.BookCoverImage import io.legado.app.ui.widget.components.image.cover.CoilBookCover +import io.legado.app.ui.widget.components.image.cover.usesDefaultBookCover import io.legado.app.ui.widget.components.log.AppLogSheet import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem @@ -121,7 +127,30 @@ fun BookInfoScreen( animatedVisibilityScope: AnimatedVisibilityScope? = null, sharedCoverKey: String? = null, ) { - val bookColorTheme = rememberBookInfoColorTheme(state.book) + val waitForSharedTransition = sharedCoverKey != null && animatedVisibilityScope != null + val transition = animatedVisibilityScope?.transition + val transitionSettled = transition?.let { + it.currentState == EnterExitState.Visible && + it.targetState == EnterExitState.Visible && + !it.isRunning + } == true + val sharedTransitionFinished = !waitForSharedTransition || transitionSettled + var canApplyCoverTheme by remember( + sharedCoverKey, + state.book?.bookUrl, + waitForSharedTransition, + ) { + mutableStateOf(!waitForSharedTransition) + } + LaunchedEffect(sharedTransitionFinished) { + if (sharedTransitionFinished) { + canApplyCoverTheme = true + } + } + val bookColorTheme = rememberBookInfoColorTheme( + book = state.book, + enabled = canApplyCoverTheme, + ) BookInfoColorTheme(theme = bookColorTheme) { BookInfoScreenContent( @@ -344,7 +373,26 @@ private fun BookInfoColorTheme( theme: ThemeOverrideState?, content: @Composable () -> Unit, ) { - ProvideThemeOverride(theme = theme, content = content) + val baseTheme = LocalLegadoThemeColors.current + val animationSpec = tween( + durationMillis = 400, + easing = FastOutSlowInEasing, + ) + val targetColorScheme = theme?.colorScheme ?: baseTheme.colorScheme + val targetSeedColor = theme?.seedColor ?: baseTheme.seedColor + val animatedColorScheme = targetColorScheme.animateColorSchemeAsState(animationSpec) + val animatedSeedColor by animateColorAsState( + targetValue = targetSeedColor, + animationSpec = animationSpec, + label = "book_info_theme_seed", + ) + + ProvideColorSchemeOverride( + colorScheme = animatedColorScheme, + seedColor = animatedSeedColor, + overrideIsDark = theme?.isDark ?: baseTheme.isDark, + content = content, + ) } @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @@ -418,9 +466,17 @@ private fun BookInfoTransparentTopAppBar( } @Composable -private fun rememberBookInfoColorTheme(book: BookInfoBookUi?): ThemeOverrideState? { - val useDefaultCover = AppConfig.useDefaultCover || book?.coverPath == "use_default_cover" - if (useDefaultCover) return null +private fun rememberBookInfoColorTheme( + book: BookInfoBookUi?, + enabled: Boolean, +): ThemeOverrideState? { + if ( + !enabled || + !ThemeConfig.bookInfoFollowCoverColor || + usesDefaultBookCover(book?.coverPath) + ) { + return null + } val imageLoader = koinInject() val coverPath = book?.coverPath @@ -481,6 +537,19 @@ private fun BookInfoTopBarActions( private fun BookInfoBackdrop( book: BookInfoBookUi, ) { + val initiallyUsesDefaultCover = usesDefaultBookCover(book.coverPath) + var usesDefaultCover by remember( + book.coverPath, + initiallyUsesDefaultCover, + ) { + mutableStateOf(initiallyUsesDefaultCover) + } + val blurBackground = when (ThemeConfig.bookInfoBackgroundBlur) { + ThemeConfig.BOOK_INFO_BACKGROUND_BLUR_OFF -> false + ThemeConfig.BOOK_INFO_BACKGROUND_BLUR_OFF_FOR_DEFAULT -> !usesDefaultCover + else -> true + } + val backdropState = remember( book.name, book.author, @@ -514,9 +583,16 @@ private fun BookInfoBackdrop( modifier = Modifier .fillMaxWidth() .height(480.dp) - .blur(24.dp), + .then( + if (blurBackground) { + Modifier.blur(24.dp) + } else { + Modifier + } + ), contentScale = ContentScale.Crop, showLoadingPlaceholder = false, + onDefaultCoverVisibilityChange = { usesDefaultCover = it }, requestBuilder = { size(Size(384, 384)) } diff --git a/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfig.kt b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfig.kt index c6727f103..1bd2a4634 100644 --- a/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfig.kt +++ b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfig.kt @@ -35,6 +35,10 @@ object ThemeConfig { private const val CUSTOM_APP_THEME = "12" + const val BOOK_INFO_BACKGROUND_BLUR_OFF = "off" + const val BOOK_INFO_BACKGROUND_BLUR_ON = "on" + const val BOOK_INFO_BACKGROUND_BLUR_OFF_FOR_DEFAULT = "off_for_default" + var containerOpacity by prefDelegate(PreferKey.containerOpacity, 100) var topBarOpacity by prefDelegate(PreferKey.topBarOpacity, 100) @@ -57,6 +61,13 @@ object ThemeConfig { var useFlexibleTopAppBar by prefDelegate(PreferKey.useFlexibleTopAppBar, true) + var bookInfoFollowCoverColor by prefDelegate(PreferKey.bookInfoFollowCoverColor, true) + + var bookInfoBackgroundBlur by prefDelegate( + PreferKey.bookInfoBackgroundBlur, + BOOK_INFO_BACKGROUND_BLUR_ON + ) + var paletteStyle by prefDelegate(PreferKey.paletteStyle, "tonalSpot") //m3 or miuix diff --git a/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt index dca4153a4..dda9a028d 100644 --- a/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt +++ b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt @@ -444,6 +444,22 @@ fun ThemeConfigScreen( ) } + SplicedColumnGroup(title = stringResource(R.string.book_info_page)) { + SwitchSettingItem( + title = stringResource(R.string.book_info_follow_cover_color), + description = stringResource(R.string.book_info_follow_cover_color_summary), + checked = ThemeConfig.bookInfoFollowCoverColor, + onCheckedChange = { ThemeConfig.bookInfoFollowCoverColor = it } + ) + DropdownListSettingItem( + title = stringResource(R.string.book_info_background_blur), + selectedValue = ThemeConfig.bookInfoBackgroundBlur, + displayEntries = stringArrayResource(R.array.book_info_background_blur_entries), + entryValues = stringArrayResource(R.array.book_info_background_blur_values), + onValueChange = { ThemeConfig.bookInfoBackgroundBlur = it } + ) + } + SplicedColumnGroup(title = stringResource(R.string.eye_protection)) { var eyeProtectionEnabled by remember { mutableStateOf(ThemeConfig.eyeProtectionEnabled) diff --git a/app/src/main/java/io/legado/app/ui/widget/components/image/cover/CoilBookCover.kt b/app/src/main/java/io/legado/app/ui/widget/components/image/cover/CoilBookCover.kt index 10f0a78e4..69cad7e1c 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/image/cover/CoilBookCover.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/image/cover/CoilBookCover.kt @@ -50,8 +50,13 @@ import org.koin.compose.koinInject import io.legado.app.model.BookCover as BookCoverModel private const val SharedCoverRadiusCacheMaxSize = 256 +private const val DefaultCoverPath = "use_default_cover" private val sharedCoverRadiusCache = mutableStateMapOf() +internal fun usesDefaultBookCover(path: String?): Boolean { + return CoverConfig.useDefaultCover || path.isNullOrBlank() || path == DefaultCoverPath +} + @Composable fun BookCoverImage( name: String?, @@ -66,13 +71,16 @@ fun BookCoverImage( onLoadFinish: (() -> Unit)? = null, onSuccess: (() -> Unit)? = null, onError: (() -> Unit)? = null, + onDefaultCoverVisibilityChange: ((Boolean) -> Unit)? = null, sharedCoverKey: String? = null, requestBuilder: ImageRequest.Builder.() -> Unit = {}, ) { val context = LocalContext.current val isNight = LegadoTheme.isDark - val useDefault = !ignoreUseDefaultCover && CoverConfig.useDefaultCover + val useDefault = (!ignoreUseDefaultCover && CoverConfig.useDefaultCover) || + path.isNullOrBlank() || + path == DefaultCoverPath val finalPath = if (useDefault) null else path val defaultCoverPaths = if (isNight) CoverConfig.defaultCoverDark else CoverConfig.defaultCover @@ -86,13 +94,27 @@ fun BookCoverImage( val hasCustomDefault = !randomPath.isNullOrBlank() var isOnlineCoverLoaded by remember(finalPath) { mutableStateOf(false) } + var onlineCoverLoadFailed by remember(finalPath) { mutableStateOf(false) } LaunchedEffect(finalPath) { if (finalPath == null) { isOnlineCoverLoaded = false + onlineCoverLoadFailed = false } } + val showDefaultIcon = !hasCustomDefault && + ( + finalPath == null || + onlineCoverLoadFailed || + (showLoadingPlaceholder && !isOnlineCoverLoaded) + ) + val isDefaultCoverVisible = + (hasCustomDefault && !isOnlineCoverLoaded) || showDefaultIcon + LaunchedEffect(isDefaultCoverVisible) { + onDefaultCoverVisibilityChange?.invoke(isDefaultCoverVisible) + } + Box(modifier = modifier) { if (hasCustomDefault && !isOnlineCoverLoaded) { AsyncImage( @@ -111,6 +133,17 @@ fun BookCoverImage( ) } + if (showDefaultIcon) { + Icon( + Icons.Default.Book, + contentDescription = null, + tint = LegadoTheme.colorScheme.secondary, + modifier = Modifier + .fillMaxSize(0.35f) + .align(Alignment.Center) + ) + } + if (finalPath != null) { AsyncImage( model = buildCoverImageRequest( @@ -128,11 +161,13 @@ fun BookCoverImage( modifier = Modifier.fillMaxSize(), onSuccess = { isOnlineCoverLoaded = true + onlineCoverLoadFailed = false onSuccess?.invoke() onLoadFinish?.invoke() }, onError = { isOnlineCoverLoaded = false + onlineCoverLoadFailed = true onError?.invoke() onLoadFinish?.invoke() } @@ -164,7 +199,9 @@ fun CoilBookCover( ) { val isNight = LegadoTheme.isDark - val useDefault = !ignoreUseDefaultCover && CoverConfig.useDefaultCover + val useDefault = (!ignoreUseDefaultCover && CoverConfig.useDefaultCover) || + path.isNullOrBlank() || + path == DefaultCoverPath val finalPath = if (useDefault) null else path val defaultCoverPaths = if (isNight) CoverConfig.defaultCoverDark else CoverConfig.defaultCover @@ -178,10 +215,12 @@ fun CoilBookCover( val hasCustomDefault = !randomPath.isNullOrBlank() var isOnlineCoverLoaded by remember(finalPath) { mutableStateOf(false) } + var onlineCoverLoadFailed by remember(finalPath) { mutableStateOf(false) } LaunchedEffect(finalPath) { if (finalPath == null) { isOnlineCoverLoaded = false + onlineCoverLoadFailed = false } } @@ -229,26 +268,22 @@ fun CoilBookCover( showLoadingPlaceholder = showLoadingPlaceholder, onSuccess = { isOnlineCoverLoaded = true + onlineCoverLoadFailed = false onLoadFinish?.invoke() }, onError = { isOnlineCoverLoaded = false + onlineCoverLoadFailed = true onLoadFinish?.invoke() }, sharedCoverKey = sharedCoverKey ) - if (showLoadingPlaceholder && !isOnlineCoverLoaded) { - if (!hasCustomDefault) { - Icon( - Icons.Default.Book, - contentDescription = null, - tint = LegadoTheme.colorScheme.secondary, - modifier = Modifier - .fillMaxSize(0.35f) - .align(Alignment.Center) - ) - } + if ( + finalPath == null || + onlineCoverLoadFailed || + (showLoadingPlaceholder && !isOnlineCoverLoaded) + ) { CoverTextOverlay( name = name, author = author, diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 98d0d4978..8c65590d5 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1473,6 +1473,13 @@ 翻页动画 标签显示 模糊效果 + 书籍详情页 + 界面颜色跟随封面取色 + 从书籍封面提取详情页配色 + 背景模糊 + 关闭 + 开启 + 显示默认封面时关闭 顶栏模糊半径 底栏模糊半径 顶栏模糊不透明度 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index a31462054..c0dea4870 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -2132,6 +2132,13 @@ 副文字色 基本設定 模糊效果 + 書籍詳情頁 + 介面顏色跟隨封面取色 + 從書籍封面擷取詳情頁配色 + 背景模糊 + 關閉 + 開啟 + 顯示預設封面時關閉 顏色 容器 介面佈局 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index a30883bcd..de1b88f56 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1620,6 +1620,13 @@ 翻頁動畫 標籤可見性 模糊效果 + 書籍詳情頁 + 介面顏色跟隨封面取色 + 從書籍封面擷取詳情頁配色 + 背景模糊 + 關閉 + 開啟 + 顯示預設封面時關閉 頂部工具列模糊半徑 底部工具列模糊半徑 頂部工具列模糊不透明度 diff --git a/app/src/main/res/values/array_values.xml b/app/src/main/res/values/array_values.xml index 32d2b7dd4..2a1ea049c 100644 --- a/app/src/main/res/values/array_values.xml +++ b/app/src/main/res/values/array_values.xml @@ -132,6 +132,12 @@ unlabeled + + off + on + off_for_default + + 0 1 @@ -156,4 +162,4 @@ webdav - \ No newline at end of file + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 6f4e3701d..4221bc519 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -329,6 +329,12 @@ Unlabeled + + @string/book_info_background_blur_off + @string/book_info_background_blur_on + @string/book_info_background_blur_off_for_default + + Default High diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d543b25f4..0e8a8f0e8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1508,6 +1508,13 @@ Page Turn Animation Label Visibility Blur Effects + Book details + Use colors from book cover + Generate the book details color scheme from the cover + Blurred cover background + Off + On + Off when showing the default cover Top Bar Blur Radius Bottom Bar Blur Radius Top Bar Blur Opacity