Просмотр исходного кода

[新增] 书籍信息显示和文件夹数目显示

HapeLee 7 месяцев назад
Родитель
Сommit
191f639b29

+ 2 - 6
app/src/main/java/io/legado/app/data/repository/RemoteBookRepository.kt

@@ -23,18 +23,15 @@ class RemoteBookRepository(
 ) {
 
     suspend fun getWebDav(book: Book): RemoteBookWebDav? {
-        val remoteUrl = book.getRemoteUrl() ?: return null
-        val analyzeUrl = AnalyzeUrl(remoteUrl)
+        val remoteUrl = book.getRemoteUrl()
+        val serverId = remoteUrl?.let { AnalyzeUrl(it).serverID }
 
-        // 优先从书籍 origin 中解析 serverID
-        val serverId = analyzeUrl.serverID
         if (serverId != null && serverId != AppConst.DEFAULT_WEBDAV_ID) {
             appDb.serverDao.get(serverId)?.getWebDavConfig()?.let {
                 return RemoteBookWebDav(it.url, Authorization(it), serverId)
             }
         }
 
-        // 如果没有指定 serverID,尝试使用目前选择的 WebDAV 服务器
         val currentServerId = AppConfig.remoteServerId
         if (currentServerId != AppConst.DEFAULT_WEBDAV_ID) {
             appDb.serverDao.get(currentServerId)?.getWebDavConfig()?.let {
@@ -42,7 +39,6 @@ class RemoteBookRepository(
             }
         }
 
-        // 最后回退到默认的 WebDAV 配置
         return AppWebDav.defaultBookWebDav
     }
 

+ 5 - 0
app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt

@@ -43,6 +43,11 @@ object BookshelfConfig {
      */
     var showTip by prefDelegate(PreferKey.showTip, false)
 
+    /**
+     * 鏄惁鏄剧ず鍒嗙粍鍐呬功绫嶆暟閲?
+     */
+    var showBookCount by prefDelegate(PreferKey.showBookCount, true)
+
     /**
      * 是否在列表中显示最后更新时间
      */

+ 41 - 2
app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt

@@ -31,9 +31,11 @@ import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
+import io.legado.app.constant.BookType
 import io.legado.app.data.entities.BookGroup
 import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
 import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.widget.components.card.TextCard
 import io.legado.app.ui.widget.components.cover.BookCover
 import io.legado.app.ui.widget.components.cover.BookshelfCover
 import io.legado.app.ui.widget.components.text.AppText
@@ -201,6 +203,7 @@ fun BookshelfItem(
 fun BookGroupCover(
     books: List<BookShelfItem>,
     coverPath: String? = null,
+    leftBottomText: String? = null,
     modifier: Modifier = Modifier
 ) {
     Box(
@@ -284,6 +287,20 @@ fun BookGroupCover(
                 }
             }
         }
+
+        if (!leftBottomText.isNullOrEmpty()) {
+            TextCard(
+                text = leftBottomText,
+                backgroundColor = LegadoTheme.colorScheme.cardContainer,
+                contentColor = LegadoTheme.colorScheme.onCardContainer,
+                modifier = Modifier
+                    .align(Alignment.BottomStart)
+                    .padding(2.dp),
+                cornerRadius = 4.dp,
+                horizontalPadding = 4.dp,
+                verticalPadding = 0.dp
+            )
+        }
     }
 }
 
@@ -291,6 +308,7 @@ fun BookGroupCover(
 fun BookGroupItemGrid(
     group: BookGroup,
     previewBooks: List<BookShelfItem>,
+    countText: String? = null,
     gridStyle: Int = 0,
     titleSmallFont: Boolean = false,
     titleCenter: Boolean = true,
@@ -304,7 +322,14 @@ fun BookGroupItemGrid(
         isGrid = true,
         gridStyle = gridStyle,
         isCompact = false,
-        cover = { BookGroupCover(books = previewBooks, coverPath = group.cover, modifier = it) },
+        cover = {
+            BookGroupCover(
+                books = previewBooks,
+                coverPath = group.cover,
+                leftBottomText = countText,
+                modifier = it
+            )
+        },
         title = group.groupName,
         modifier = modifier,
         titleSmallFont = titleSmallFont,
@@ -320,6 +345,7 @@ fun BookGroupItemGrid(
 fun BookGroupItemList(
     group: BookGroup,
     previewBooks: List<BookShelfItem>,
+    countText: String? = null,
     isCompact: Boolean = false,
     titleSmallFont: Boolean = false,
     titleCenter: Boolean = true,
@@ -335,6 +361,7 @@ fun BookGroupItemList(
         isCompact = isCompact,
         cover = { BookGroupCover(books = previewBooks, coverPath = group.cover, modifier = it) },
         title = group.groupName,
+        subTitle = countText,
         titleSmallFont = titleSmallFont,
         titleCenter = titleCenter,
         titleMaxLines = titleMaxLines,
@@ -360,6 +387,17 @@ fun BookItem(
     onLongClick: () -> Unit
 ) {
     val unreadCount = book.getUnreadChapterNum()
+    val bookTypeLabel = if (BookshelfConfig.showTip) {
+        when {
+            book.isAudio -> stringResource(R.string.audio)
+            book.isImage -> stringResource(R.string.manga)
+            (book.type and BookType.webFile) > 0 -> stringResource(R.string.web_file)
+            book.isLocal -> stringResource(R.string.local)
+            else -> stringResource(R.string.noval)
+        }
+    } else {
+        null
+    }
 
     BookshelfItem(
         isGrid = layoutMode != 0,
@@ -373,7 +411,8 @@ fun BookItem(
                 isUpdating = isUpdating,
                 modifier = modifier,
                 badgeText = if (BookshelfConfig.showUnread && unreadCount > 0) unreadCount.toString() else null,
-                showBadgeDot = BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && book.isNew
+                showBadgeDot = BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && book.isNew,
+                leftBottomText = bookTypeLabel
             )
         },
         title = book.name,

+ 7 - 25
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt

@@ -9,18 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.Sort
-import androidx.compose.material.icons.automirrored.filled.ViewList
-import androidx.compose.material.icons.filled.BlurOn
-import androidx.compose.material.icons.filled.History
-import androidx.compose.material.icons.filled.Info
-import androidx.compose.material.icons.filled.Menu
-import androidx.compose.material.icons.filled.Notifications
-import androidx.compose.material.icons.filled.SortByAlpha
-import androidx.compose.material.icons.filled.Style
-import androidx.compose.material.icons.filled.TextFormat
-import androidx.compose.material.icons.filled.ViewCompact
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
@@ -67,7 +55,6 @@ fun BookshelfConfigSheet(
                 selectedValue = BookshelfConfig.bookGroupStyle.toString(),
                 displayEntries = stringArrayResource(R.array.group_style),
                 entryValues = Array(stringArrayResource(R.array.group_style).size) { it.toString() },
-                imageVector = Icons.Default.Style,
                 onValueChange = { BookshelfConfig.bookGroupStyle = it.toInt() }
             )
 
@@ -77,7 +64,6 @@ fun BookshelfConfigSheet(
                 selectedValue = BookshelfConfig.bookshelfSort.toString(),
                 displayEntries = stringArrayResource(R.array.bookshelf_px_array),
                 entryValues = Array(stringArrayResource(R.array.bookshelf_px_array).size) { it.toString() },
-                imageVector = Icons.AutoMirrored.Filled.Sort,
                 onValueChange = { BookshelfConfig.bookshelfSort = it.toInt() }
             )
 
@@ -90,7 +76,6 @@ fun BookshelfConfigSheet(
                     stringResource(R.string.descending_order)
                 ),
                 entryValues = arrayOf("0", "1"),
-                imageVector = Icons.Default.SortByAlpha,
                 onValueChange = { BookshelfConfig.bookshelfSortOrder = it.toInt() }
             )
 
@@ -105,7 +90,6 @@ fun BookshelfConfigSheet(
                 selectedValue = layoutMode.toString(),
                 displayEntries = arrayOf("列表", "网格"),
                 entryValues = arrayOf("0", "1"),
-                imageVector = Icons.AutoMirrored.Filled.ViewList,
                 onValueChange = {
                     if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscape = it.toInt()
                     else BookshelfConfig.bookshelfLayoutModePortrait = it.toInt()
@@ -123,7 +107,6 @@ fun BookshelfConfigSheet(
                         selectedValue = BookshelfConfig.bookshelfGridLayout.toString(),
                         displayEntries = stringArrayResource(R.array.bookshelf_grid_layout),
                         entryValues = Array(stringArrayResource(R.array.bookshelf_grid_layout).size) { it.toString() },
-                        imageVector = Icons.Default.ViewCompact,
                         onValueChange = { BookshelfConfig.bookshelfGridLayout = it.toInt() }
                     )
 
@@ -144,7 +127,6 @@ fun BookshelfConfigSheet(
                     CompactSwitchSettingItem(
                         title = "标题小字体",
                         checked = BookshelfConfig.bookshelfTitleSmallFont,
-                        imageVector = Icons.Default.TextFormat,
                         color = MaterialTheme.colorScheme.surface,
                         onCheckedChange = { BookshelfConfig.bookshelfTitleSmallFont = it }
                     )
@@ -167,7 +149,6 @@ fun BookshelfConfigSheet(
                     CompactSwitchSettingItem(
                         title = "紧凑模式",
                         checked = BookshelfConfig.bookshelfLayoutCompact,
-                        imageVector = Icons.Default.ViewCompact,
                         color = MaterialTheme.colorScheme.surface,
                         onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
                     )
@@ -175,7 +156,6 @@ fun BookshelfConfigSheet(
                     CompactSwitchSettingItem(
                         title = "显示分隔线",
                         checked = BookshelfConfig.bookshelfShowDivider,
-                        imageVector = Icons.Default.ViewCompact,
                         color = MaterialTheme.colorScheme.surface,
                         onCheckedChange = { BookshelfConfig.bookshelfShowDivider = it }
                     )
@@ -207,7 +187,6 @@ fun BookshelfConfigSheet(
             CompactSwitchSettingItem(
                 title = "封面阴影",
                 checked = BookshelfConfig.bookshelfCoverShadow,
-                imageVector = Icons.Default.BlurOn,
                 color = MaterialTheme.colorScheme.surface,
                 onCheckedChange = { BookshelfConfig.bookshelfCoverShadow = it }
             )
@@ -216,7 +195,6 @@ fun BookshelfConfigSheet(
             CompactSwitchSettingItem(
                 title = stringResource(R.string.show_unread),
                 checked = BookshelfConfig.showUnread,
-                imageVector = Icons.Default.Notifications,
                 color = MaterialTheme.colorScheme.surface,
                 onCheckedChange = { BookshelfConfig.showUnread = it }
             )
@@ -231,15 +209,20 @@ fun BookshelfConfigSheet(
             CompactSwitchSettingItem(
                 title = stringResource(R.string.show_tip),
                 checked = BookshelfConfig.showTip,
-                imageVector = Icons.Default.Info,
                 color = MaterialTheme.colorScheme.surface,
                 onCheckedChange = { BookshelfConfig.showTip = it }
             )
 
+            CompactSwitchSettingItem(
+                title = stringResource(R.string.show_book_count),
+                checked = BookshelfConfig.showBookCount,
+                color = MaterialTheme.colorScheme.surface,
+                onCheckedChange = { BookshelfConfig.showBookCount = it }
+            )
+
             CompactSwitchSettingItem(
                 title = stringResource(R.string.show_last_update_time),
                 checked = BookshelfConfig.showLastUpdateTime,
-                imageVector = Icons.Default.History,
                 color = MaterialTheme.colorScheme.surface,
                 onCheckedChange = { BookshelfConfig.showLastUpdateTime = it }
             )
@@ -261,7 +244,6 @@ fun BookshelfConfigSheet(
             CompactSwitchSettingItem(
                 title = stringResource(R.string.show_bookshelf_tab_menu),
                 checked = BookshelfConfig.shouldShowExpandButton,
-                imageVector = Icons.Default.Menu,
                 color = MaterialTheme.colorScheme.surface,
                 onCheckedChange = { BookshelfConfig.shouldShowExpandButton = it }
             )

+ 12 - 1
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt

@@ -400,8 +400,10 @@ fun BookshelfScreen(
                 label = "FolderTransition"
             ) { isRoot ->
                 if (bookGroupStyle == 2 && isRoot) {
+                    val folderColumns =
+                        if (bookshelfLayoutMode == 0) bookshelfLayoutList else bookshelfLayoutGrid
                     FastScrollLazyVerticalGrid(
-                        columns = GridCells.Fixed(bookshelfLayoutGrid.coerceAtLeast(1)),
+                        columns = GridCells.Fixed(folderColumns.coerceAtLeast(1)),
                         modifier = Modifier.fillMaxSize(),
                         contentPadding = adaptiveContentPadding(
                             top = paddingValues.calculateTopPadding(),
@@ -414,11 +416,19 @@ fun BookshelfScreen(
                         itemsIndexed(
                             uiState.groups,
                             key = { _, it -> it.groupId }) { index, group ->
+                            val countText = if (BookshelfConfig.showBookCount) {
+                                uiState.groupBookCounts[group.groupId]?.let {
+                                    stringResource(R.string.book_count, it)
+                                }
+                            } else {
+                                null
+                            }
                             if (bookshelfLayoutMode == 0) {
                                 BookGroupItemList(
                                     group = group,
                                     previewBooks = uiState.groupPreviews[group.groupId]
                                         ?: emptyList(),
+                                    countText = countText,
                                     isCompact = BookshelfConfig.bookshelfLayoutCompact,
                                     titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont,
                                     titleCenter = BookshelfConfig.bookshelfTitleCenter,
@@ -434,6 +444,7 @@ fun BookshelfScreen(
                                     group = group,
                                     previewBooks = uiState.groupPreviews[group.groupId]
                                         ?: emptyList(),
+                                    countText = countText,
                                     gridStyle = BookshelfConfig.bookshelfGridLayout,
                                     titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont,
                                     titleCenter = BookshelfConfig.bookshelfTitleCenter,

+ 1 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfUiState.kt

@@ -11,6 +11,7 @@ data class BookshelfUiState(
     override val isLoading: Boolean = false,
     val groups: List<BookGroup> = emptyList(),
     val groupPreviews: Map<Long, List<BookShelfItem>> = emptyMap(),
+    val groupBookCounts: Map<Long, Int> = emptyMap(),
     val selectedGroupIndex: Int = 0,
     val loadingText: String? = null,
     val updatingBooks: Set<String> = emptySet()

+ 61 - 46
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt

@@ -126,6 +126,11 @@ class BookshelfViewModel(
         .distinctUntilChanged()
         .flowOn(Dispatchers.Default)
 
+    private data class GroupPreviewState(
+        val previews: Map<Long, List<BookShelfItem>>,
+        val counts: Map<Long, Int>
+    )
+
     @OptIn(ExperimentalCoroutinesApi::class)
     val booksFlow = combine(groupIdFlow, refreshTrigger) { groupId, _ -> groupId }
         .flatMapLatest { groupId ->
@@ -138,59 +143,68 @@ class BookshelfViewModel(
 
     private val groupPreviewsFlow =
         combine(groupsFlow, allBooksFlow, refreshTrigger) { groups, allBooks, _ ->
-        if (BookshelfConfig.bookGroupStyle in 2..3) {
-            groups.associate { group ->
-                val groupBooks = when (group.groupId) {
-                    BookGroup.IdRoot -> {
-                        val sumUserGroupIds = groups.filter { it.groupId > 0 }.sumOf { it.groupId }
-                        allBooks.filter { book ->
-                            (book.type and BookType.text) > 0 &&
-                                    (book.type and BookType.local) == 0 &&
-                                    (sumUserGroupIds and book.group) == 0L
+            if (BookshelfConfig.bookGroupStyle in 2..3) {
+                val previews = HashMap<Long, List<BookShelfItem>>(groups.size)
+                val counts = HashMap<Long, Int>(groups.size)
+                groups.forEach { group ->
+                    val groupBooks = when (group.groupId) {
+                        BookGroup.IdRoot -> {
+                            val sumUserGroupIds =
+                                groups.filter { it.groupId > 0 }.sumOf { it.groupId }
+                            allBooks.filter { book ->
+                                (book.type and BookType.text) > 0 &&
+                                        (book.type and BookType.local) == 0 &&
+                                        (sumUserGroupIds and book.group) == 0L
+                            }
                         }
-                    }
 
-                    BookGroup.IdAll -> allBooks
-                    BookGroup.IdLocal -> allBooks.filter { (it.type and BookType.local) > 0 }
-                    BookGroup.IdAudio -> allBooks.filter { (it.type and BookType.audio) > 0 }
-                    BookGroup.IdNetNone -> {
-                        val sumUserGroupIds = groups.filter { it.groupId > 0 }.sumOf { it.groupId }
-                        allBooks.filter { book ->
-                            (book.type and BookType.audio) == 0 &&
-                                    (book.type and BookType.local) == 0 &&
-                                    (sumUserGroupIds and book.group) == 0L
+                        BookGroup.IdAll -> allBooks
+                        BookGroup.IdLocal -> allBooks.filter { (it.type and BookType.local) > 0 }
+                        BookGroup.IdAudio -> allBooks.filter { (it.type and BookType.audio) > 0 }
+                        BookGroup.IdNetNone -> {
+                            val sumUserGroupIds =
+                                groups.filter { it.groupId > 0 }.sumOf { it.groupId }
+                            allBooks.filter { book ->
+                                (book.type and BookType.audio) == 0 &&
+                                        (book.type and BookType.local) == 0 &&
+                                        (sumUserGroupIds and book.group) == 0L
+                            }
                         }
-                    }
 
-                    BookGroup.IdLocalNone -> {
-                        val sumUserGroupIds = groups.filter { it.groupId > 0 }.sumOf { it.groupId }
-                        allBooks.filter { book ->
-                            (book.type and BookType.local) > 0 &&
-                                    (sumUserGroupIds and book.group) == 0L
+                        BookGroup.IdLocalNone -> {
+                            val sumUserGroupIds =
+                                groups.filter { it.groupId > 0 }.sumOf { it.groupId }
+                            allBooks.filter { book ->
+                                (book.type and BookType.local) > 0 &&
+                                        (sumUserGroupIds and book.group) == 0L
+                            }
                         }
-                    }
 
-                    BookGroup.IdManga -> allBooks.filter { (it.type and BookType.image) > 0 }
-                    BookGroup.IdText -> allBooks.filter { (it.type and BookType.text) > 0 }
-                    BookGroup.IdError -> allBooks.filter { (it.type and BookType.updateError) > 0 }
-                    BookGroup.IdUnread -> allBooks.filter { it.durChapterIndex == 0 && it.durChapterPos == 0 }
-                    BookGroup.IdReading -> allBooks.filter { it.totalChapterNum > 0 && it.durChapterIndex > 0 && it.durChapterIndex < it.totalChapterNum - 1 }
-                    BookGroup.IdReadFinished -> allBooks.filter { it.totalChapterNum > 0 && it.durChapterIndex >= it.totalChapterNum - 1 }
-                    else -> allBooks.filter { (it.group and group.groupId) != 0L }
-                }
-                val sortedBooks = sortBooks(groupBooks, group)
-                val booksWithCover = sortedBooks.filter { it.getDisplayCover() != null }
-                val result = if (booksWithCover.size >= 4) {
-                    booksWithCover.take(4)
-                } else {
-                    (booksWithCover + sortedBooks.filter { it.getDisplayCover() == null }).take(4)
+                        BookGroup.IdManga -> allBooks.filter { (it.type and BookType.image) > 0 }
+                        BookGroup.IdText -> allBooks.filter { (it.type and BookType.text) > 0 }
+                        BookGroup.IdError -> allBooks.filter { (it.type and BookType.updateError) > 0 }
+                        BookGroup.IdUnread -> allBooks.filter { it.durChapterIndex == 0 && it.durChapterPos == 0 }
+                        BookGroup.IdReading -> allBooks.filter { it.totalChapterNum > 0 && it.durChapterIndex > 0 && it.durChapterIndex < it.totalChapterNum - 1 }
+                        BookGroup.IdReadFinished -> allBooks.filter { it.totalChapterNum > 0 && it.durChapterIndex >= it.totalChapterNum - 1 }
+                        else -> allBooks.filter { (it.group and group.groupId) != 0L }
+                    }
+                    counts[group.groupId] = groupBooks.size
+                    val sortedBooks = sortBooks(groupBooks, group)
+                    val booksWithCover = sortedBooks.filter { it.getDisplayCover() != null }
+                    val result = if (booksWithCover.size >= 4) {
+                        booksWithCover.take(4)
+                    } else {
+                        (booksWithCover + sortedBooks.filter { it.getDisplayCover() == null }).take(
+                            4
+                        )
+                    }
+                    previews[group.groupId] = result
                 }
-                group.groupId to result
+                GroupPreviewState(previews, counts)
+            } else {
+                GroupPreviewState(emptyMap(), emptyMap())
             }
-        } else {
-            emptyMap()
-        }
-    }.distinctUntilChanged().flowOn(Dispatchers.Default)
+        }.distinctUntilChanged().flowOn(Dispatchers.Default)
 
     private val internalStateFlow = combine(
         groupIdFlow,
@@ -228,7 +242,8 @@ class BookshelfViewModel(
         BookshelfUiState(
             items = filteredBooks,
             groups = groups,
-            groupPreviews = previews,
+            groupPreviews = previews.previews,
+            groupBookCounts = previews.counts,
             selectedGroupIndex = groups.indexOfFirst { it.groupId == internal.groupId }
                 .coerceAtLeast(0),
             searchKey = internal.searchKey,

+ 15 - 0
app/src/main/java/io/legado/app/ui/widget/components/cover/BookshelfCover.kt

@@ -24,6 +24,7 @@ fun BookshelfCover(
     isUpdating: Boolean = false,
     badgeText: String? = null,
     showBadgeDot: Boolean = false,
+    leftBottomText: String? = null,
     sourceOrigin: String? = null,
     onLoadFinish: (() -> Unit)? = null
 ) {
@@ -52,6 +53,20 @@ fun BookshelfCover(
             )
         }
 
+        if (!leftBottomText.isNullOrEmpty()) {
+            TextCard(
+                text = leftBottomText,
+                backgroundColor = LegadoTheme.colorScheme.cardContainer,
+                contentColor = LegadoTheme.colorScheme.onCardContainer,
+                modifier = Modifier
+                    .align(Alignment.BottomStart)
+                    .padding(2.dp),
+                cornerRadius = 4.dp,
+                horizontalPadding = 4.dp,
+                verticalPadding = 0.dp
+            )
+        }
+
         if (isUpdating) {
             LinearProgressIndicator(
                 modifier = Modifier