diff --git a/app/src/main/java/io/legado/app/data/repository/RemoteBookRepository.kt b/app/src/main/java/io/legado/app/data/repository/RemoteBookRepository.kt index f81885eca..154dbf79f 100644 --- a/app/src/main/java/io/legado/app/data/repository/RemoteBookRepository.kt +++ b/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 } diff --git a/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt b/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt index 72b99fa9e..4c8be692b 100644 --- a/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt +++ b/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) + /** * 是否在列表中显示最后更新时间 */ diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt index 19cdf003d..2a8e48c26 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt +++ b/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, 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, + 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, + 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, diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt index 53f59539c..c32255d83 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt +++ b/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 } ) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt index 55923aee6..b7018024e 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt +++ b/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, diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfUiState.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfUiState.kt index 757a8cd39..d8bced5ae 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfUiState.kt +++ b/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 = emptyList(), val groupPreviews: Map> = emptyMap(), + val groupBookCounts: Map = emptyMap(), val selectedGroupIndex: Int = 0, val loadingText: String? = null, val updatingBooks: Set = emptySet() diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt index 9810e884c..6871adcbb 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt +++ b/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>, + val counts: Map + ) + @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>(groups.size) + val counts = HashMap(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 } + 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 } - 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) - } - 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, diff --git a/app/src/main/java/io/legado/app/ui/widget/components/cover/BookshelfCover.kt b/app/src/main/java/io/legado/app/ui/widget/components/cover/BookshelfCover.kt index 299dc7146..142e4d834 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/cover/BookshelfCover.kt +++ b/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