HapeLee 5 月之前
父节点
当前提交
68c16cbc5a

+ 17 - 51
app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt

@@ -59,13 +59,11 @@ import androidx.compose.ui.graphics.lerp
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import coil.ImageLoader
 import coil.ImageLoader
-import coil.compose.AsyncImage
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.constant.BookType
 import io.legado.app.constant.BookType
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
@@ -73,7 +71,6 @@ import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.help.book.isLocal
 import io.legado.app.help.book.isLocal
 import io.legado.app.help.config.AppConfig
 import io.legado.app.help.config.AppConfig
-import io.legado.app.model.BookCover
 import io.legado.app.ui.config.coverConfig.CoverConfig
 import io.legado.app.ui.config.coverConfig.CoverConfig
 import io.legado.app.ui.main.homepage.modules.BannerModule
 import io.legado.app.ui.main.homepage.modules.BannerModule
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.LegadoTheme
@@ -93,8 +90,8 @@ import io.legado.app.ui.widget.components.button.series.SmallTonalButton
 import io.legado.app.ui.widget.components.card.GlassCard
 import io.legado.app.ui.widget.components.card.GlassCard
 import io.legado.app.ui.widget.components.card.TextCard
 import io.legado.app.ui.widget.components.card.TextCard
 import io.legado.app.ui.widget.components.icon.AppIcon
 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.CoilBookCover
-import io.legado.app.ui.widget.components.image.cover.buildCoverImageRequest
 import io.legado.app.ui.widget.components.log.AppLogSheet
 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.RoundDropdownMenu
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
@@ -192,10 +189,7 @@ private fun BookInfoScreenContent(
         } else {
         } else {
             Box(modifier = Modifier.fillMaxSize()) {
             Box(modifier = Modifier.fillMaxSize()) {
                 BookInfoBackdrop(
                 BookInfoBackdrop(
-                    bookUrl = book.bookUrl,
-                    coverUrl = book.coverUrl,
-                    customCoverUrl = book.customCoverUrl,
-                    origin = book.origin,
+                    book = book,
                 )
                 )
                 AppPullToRefresh(
                 AppPullToRefresh(
                     modifier = Modifier.fillMaxSize(),
                     modifier = Modifier.fillMaxSize(),
@@ -479,26 +473,8 @@ private fun BookInfoTopBarActions(
 
 
 @Composable
 @Composable
 private fun BookInfoBackdrop(
 private fun BookInfoBackdrop(
-    bookUrl: String,
-    coverUrl: String?,
-    customCoverUrl: String?,
-    origin: String,
+    book: Book,
 ) {
 ) {
-    val useDefaultCover = AppConfig.useDefaultCover || customCoverUrl == "use_default_cover"
-    val isNight = AppConfig.isNightTheme
-
-    val cover = remember(bookUrl, customCoverUrl, coverUrl, isNight) {
-        if (useDefaultCover) {
-            BookCover.getRandomDefaultPath(bookUrl, isNight)
-        } else {
-            if (customCoverUrl.isNullOrEmpty()) coverUrl else customCoverUrl
-        }
-    }
-    val sourceOrigin = if (!useDefaultCover) origin else null
-    val loadOnlyWifi = CoverConfig.loadCoverOnlyWifi
-    val context = LocalContext.current
-    val imageLoader = koinInject<ImageLoader>()
-
     val seedOverlay = lerp(
     val seedOverlay = lerp(
         LegadoTheme.colorScheme.secondaryContainer,
         LegadoTheme.colorScheme.secondaryContainer,
         LegadoTheme.seedColor,
         LegadoTheme.seedColor,
@@ -506,32 +482,22 @@ private fun BookInfoBackdrop(
     )
     )
     Box(modifier = Modifier.fillMaxSize()) {
     Box(modifier = Modifier.fillMaxSize()) {
         Crossfade(
         Crossfade(
-            targetState = cover,
+            targetState = book,
             animationSpec = tween(800),
             animationSpec = tween(800),
             label = "BackdropCrossfade"
             label = "BackdropCrossfade"
-        ) { currentCover ->
-            if (!currentCover.isNullOrBlank()) {
-                val backdropRequest = remember(currentCover, sourceOrigin, loadOnlyWifi, context) {
-                    buildCoverImageRequest(
-                        context = context,
-                        data = currentCover,
-                        sourceOrigin = sourceOrigin,
-                        loadOnlyWifi = loadOnlyWifi,
-                        crossfade = true,
-                        memoryCacheKey = currentCover,
-                    )
-                }
-                AsyncImage(
-                    model = backdropRequest,
-                    imageLoader = imageLoader,
-                    contentDescription = null,
-                    modifier = Modifier
-                        .fillMaxWidth()
-                        .height(480.dp)
-                        .blur(24.dp),
-                    contentScale = ContentScale.Crop,
-                )
-            }
+        ) { currentBook ->
+            BookCoverImage(
+                name = currentBook.name,
+                author = currentBook.author,
+                path = currentBook.getDisplayCover(),
+                sourceOrigin = currentBook.origin,
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .height(480.dp)
+                    .blur(24.dp),
+                contentScale = ContentScale.Crop,
+                showLoadingPlaceholder = false,
+            )
         }
         }
         Box(
         Box(
             modifier = Modifier
             modifier = Modifier

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

@@ -52,6 +52,97 @@ import io.legado.app.model.BookCover as BookCoverModel
 private const val SharedCoverRadiusCacheMaxSize = 256
 private const val SharedCoverRadiusCacheMaxSize = 256
 private val sharedCoverRadiusCache = mutableStateMapOf<String, Dp>()
 private val sharedCoverRadiusCache = mutableStateMapOf<String, Dp>()
 
 
+@Composable
+fun BookCoverImage(
+    name: String?,
+    author: String?,
+    path: String?,
+    modifier: Modifier = Modifier,
+    sourceOrigin: String? = null,
+    ignoreUseDefaultCover: Boolean = false,
+    showLoadingPlaceholder: Boolean = true,
+    contentScale: ContentScale = ContentScale.Crop,
+    onLoadFinish: (() -> Unit)? = null,
+    onSuccess: (() -> Unit)? = null,
+    onError: (() -> Unit)? = null,
+    sharedCoverKey: String? = null,
+) {
+    val context = LocalContext.current
+    val isNight = isSystemInDarkTheme()
+
+    val useDefault = !ignoreUseDefaultCover && CoverConfig.useDefaultCover
+    val finalPath = if (useDefault) null else path
+
+    val randomPath = remember(name, author, path, isNight) {
+        BookCoverModel.getRandomDefaultPath(
+            seed = name ?: author ?: path ?: "",
+            isNight = isNight
+        )
+    }
+
+    val hasCustomDefault = !randomPath.isNullOrBlank()
+    var isOnlineCoverLoaded by remember(finalPath) {
+        mutableStateOf(sharedCoverKey != null && finalPath != null)
+    }
+
+    LaunchedEffect(finalPath) {
+        if (finalPath == null) {
+            isOnlineCoverLoaded = false
+        }
+    }
+
+    Box(modifier = modifier) {
+        if (hasCustomDefault && !isOnlineCoverLoaded) {
+            AsyncImage(
+                model = buildCoverImageRequest(
+                    context = context,
+                    data = randomPath,
+                    sourceOrigin = null,
+                    loadOnlyWifi = false,
+                    crossfade = showLoadingPlaceholder,
+                    memoryCacheKey = randomPath,
+                ),
+                contentDescription = null,
+                imageLoader = koinInject(),
+                contentScale = contentScale,
+                modifier = Modifier.fillMaxSize()
+            )
+        }
+
+        if (finalPath != null) {
+            AsyncImage(
+                model = buildCoverImageRequest(
+                    context = context,
+                    data = finalPath,
+                    sourceOrigin = sourceOrigin,
+                    loadOnlyWifi = CoverConfig.loadCoverOnlyWifi,
+                    crossfade = showLoadingPlaceholder,
+                    memoryCacheKey = finalPath,
+                ),
+                contentDescription = null,
+                imageLoader = koinInject(),
+                contentScale = contentScale,
+                modifier = Modifier.fillMaxSize(),
+                onSuccess = {
+                    isOnlineCoverLoaded = true
+                    onSuccess?.invoke()
+                    onLoadFinish?.invoke()
+                },
+                onError = {
+                    isOnlineCoverLoaded = false
+                    onError?.invoke()
+                    onLoadFinish?.invoke()
+                }
+            )
+        } else {
+            LaunchedEffect(Unit) {
+                onLoadFinish?.invoke()
+            }
+        }
+    }
+}
+
+// 改成BookCover
 @OptIn(ExperimentalSharedTransitionApi::class)
 @OptIn(ExperimentalSharedTransitionApi::class)
 @Composable
 @Composable
 fun CoilBookCover(
 fun CoilBookCover(
@@ -68,7 +159,6 @@ fun CoilBookCover(
     animatedVisibilityScope: AnimatedVisibilityScope? = null,
     animatedVisibilityScope: AnimatedVisibilityScope? = null,
     sharedCoverKey: String? = null,
     sharedCoverKey: String? = null,
 ) {
 ) {
-    val context = LocalContext.current
     val isNight = isSystemInDarkTheme()
     val isNight = isSystemInDarkTheme()
 
 
     val useDefault = !ignoreUseDefaultCover && CoverConfig.useDefaultCover
     val useDefault = !ignoreUseDefaultCover && CoverConfig.useDefaultCover
@@ -126,54 +216,24 @@ fun CoilBookCover(
             )
             )
             .clip(shape)
             .clip(shape)
     ) {
     ) {
-        Box(modifier = Modifier.fillMaxSize()) {
-            if (hasCustomDefault && !isOnlineCoverLoaded) {
-                AsyncImage(
-                    model = buildCoverImageRequest(
-                        context = context,
-                        data = randomPath,
-                        sourceOrigin = null,
-                        loadOnlyWifi = false,
-                        crossfade = showLoadingPlaceholder,
-                        memoryCacheKey = randomPath,
-                    ),
-                    contentDescription = null,
-                    imageLoader = koinInject(),
-                    contentScale = ContentScale.Crop,
-                    modifier = Modifier
-                        .fillMaxSize()
-                )
-            }
-
-            if (finalPath != null) {
-                AsyncImage(
-                    model = buildCoverImageRequest(
-                        context = context,
-                        data = finalPath,
-                        sourceOrigin = sourceOrigin,
-                        loadOnlyWifi = CoverConfig.loadCoverOnlyWifi,
-                        crossfade = showLoadingPlaceholder,
-                        memoryCacheKey = finalPath,
-                    ),
-                    contentDescription = null,
-                    imageLoader = koinInject(),
-                    contentScale = ContentScale.Crop,
-                    modifier = Modifier.fillMaxSize(),
-                    onSuccess = {
-                        isOnlineCoverLoaded = true
-                        onLoadFinish?.invoke()
-                    },
-                    onError = {
-                        isOnlineCoverLoaded = false
-                        onLoadFinish?.invoke()
-                    }
-                )
-            } else {
-                LaunchedEffect(Unit) {
-                    onLoadFinish?.invoke()
-                }
-            }
-        }
+        BookCoverImage(
+            name = name,
+            author = author,
+            path = path,
+            modifier = Modifier.fillMaxSize(),
+            sourceOrigin = sourceOrigin,
+            ignoreUseDefaultCover = ignoreUseDefaultCover,
+            showLoadingPlaceholder = showLoadingPlaceholder,
+            onSuccess = {
+                isOnlineCoverLoaded = true
+                onLoadFinish?.invoke()
+            },
+            onError = {
+                isOnlineCoverLoaded = false
+                onLoadFinish?.invoke()
+            },
+            sharedCoverKey = sharedCoverKey
+        )
 
 
         if (showLoadingPlaceholder && !isOnlineCoverLoaded) {
         if (showLoadingPlaceholder && !isOnlineCoverLoaded) {
             if (!hasCustomDefault) {
             if (!hasCustomDefault) {