[优化] 优化书架性能
This commit is contained in:
7 files changed
+188
-42
No files matched your search
@@ -12,6 +12,7 @@ import io.legado.app.data.entities.Book
|
|||||||
import io.legado.app.data.entities.BookGroup
|
import io.legado.app.data.entities.BookGroup
|
||||||
import io.legado.app.data.entities.BookSource
|
import io.legado.app.data.entities.BookSource
|
||||||
import io.legado.app.help.book.isNotShelf
|
import io.legado.app.help.book.isNotShelf
|
||||||
|
import io.legado.app.ui.main.bookshelf.BookShelfItem
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
@@ -51,6 +52,31 @@ interface BookDao {
|
|||||||
@Query("SELECT * FROM books order by durChapterTime desc")
|
@Query("SELECT * FROM books order by durChapterTime desc")
|
||||||
fun flowAll(): Flow<List<Book>>
|
fun flowAll(): Flow<List<Book>>
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
bookUrl,
|
||||||
|
name,
|
||||||
|
author,
|
||||||
|
coverUrl,
|
||||||
|
customCoverUrl,
|
||||||
|
durChapterTitle,
|
||||||
|
durChapterTime,
|
||||||
|
durChapterPos,
|
||||||
|
latestChapterTitle,
|
||||||
|
latestChapterTime,
|
||||||
|
totalChapterNum,
|
||||||
|
durChapterIndex,
|
||||||
|
type,
|
||||||
|
`group`,
|
||||||
|
`order`,
|
||||||
|
canUpdate
|
||||||
|
FROM books
|
||||||
|
ORDER BY durChapterTime DESC
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun flowBookShelf(): Flow<List<BookShelfItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM books WHERE type & ${BookType.audio} > 0")
|
@Query("SELECT * FROM books WHERE type & ${BookType.audio} > 0")
|
||||||
fun flowAudio(): Flow<List<Book>>
|
fun flowAudio(): Flow<List<Book>>
|
||||||
|
|
||||||
|
|||||||
@@ -32,9 +32,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.data.entities.Book
|
|
||||||
import io.legado.app.data.entities.BookGroup
|
import io.legado.app.data.entities.BookGroup
|
||||||
import io.legado.app.help.book.isLocal
|
|
||||||
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
|
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
|
||||||
import io.legado.app.ui.widget.components.cover.BookCover
|
import io.legado.app.ui.widget.components.cover.BookCover
|
||||||
import io.legado.app.ui.widget.components.cover.BookCoverWithProgress
|
import io.legado.app.ui.widget.components.cover.BookCoverWithProgress
|
||||||
@@ -197,7 +195,7 @@ fun BookshelfItem(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BookGroupCover(
|
fun BookGroupCover(
|
||||||
books: List<Book>,
|
books: List<BookShelfItem>,
|
||||||
coverPath: String? = null,
|
coverPath: String? = null,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
@@ -289,7 +287,7 @@ fun BookGroupCover(
|
|||||||
@Composable
|
@Composable
|
||||||
fun BookGroupItemGrid(
|
fun BookGroupItemGrid(
|
||||||
group: BookGroup,
|
group: BookGroup,
|
||||||
previewBooks: List<Book>,
|
previewBooks: List<BookShelfItem>,
|
||||||
gridStyle: Int = 0,
|
gridStyle: Int = 0,
|
||||||
titleSmallFont: Boolean = false,
|
titleSmallFont: Boolean = false,
|
||||||
titleCenter: Boolean = true,
|
titleCenter: Boolean = true,
|
||||||
@@ -318,7 +316,7 @@ fun BookGroupItemGrid(
|
|||||||
@Composable
|
@Composable
|
||||||
fun BookGroupItemList(
|
fun BookGroupItemList(
|
||||||
group: BookGroup,
|
group: BookGroup,
|
||||||
previewBooks: List<Book>,
|
previewBooks: List<BookShelfItem>,
|
||||||
isCompact: Boolean = false,
|
isCompact: Boolean = false,
|
||||||
titleSmallFont: Boolean = false,
|
titleSmallFont: Boolean = false,
|
||||||
titleCenter: Boolean = true,
|
titleCenter: Boolean = true,
|
||||||
@@ -348,7 +346,7 @@ fun BookGroupItemList(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BookItem(
|
fun BookItem(
|
||||||
book: Book,
|
book: BookShelfItem,
|
||||||
layoutMode: Int,
|
layoutMode: Int,
|
||||||
gridStyle: Int = 0,
|
gridStyle: Int = 0,
|
||||||
isCompact: Boolean = false,
|
isCompact: Boolean = false,
|
||||||
@@ -374,7 +372,7 @@ fun BookItem(
|
|||||||
isUpdating = isUpdating,
|
isUpdating = isUpdating,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
badgeText = if (BookshelfConfig.showUnread && unreadCount > 0) unreadCount.toString() else null,
|
badgeText = if (BookshelfConfig.showUnread && unreadCount > 0) unreadCount.toString() else null,
|
||||||
showBadgeDot = !BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && unreadCount > 0 && book.lastCheckCount > 0
|
showBadgeDot = !BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && unreadCount > 0
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
title = book.name,
|
title = book.name,
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package io.legado.app.ui.main.bookshelf
|
||||||
|
|
||||||
|
import io.legado.app.constant.BookType
|
||||||
|
import io.legado.app.data.entities.Book
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
data class BookShelfItem(
|
||||||
|
val bookUrl: String,
|
||||||
|
val name: String,
|
||||||
|
val author: String,
|
||||||
|
val coverUrl: String?,
|
||||||
|
val customCoverUrl: String?,
|
||||||
|
val durChapterTitle: String?,
|
||||||
|
val durChapterTime: Long,
|
||||||
|
val durChapterPos: Int,
|
||||||
|
val latestChapterTitle: String?,
|
||||||
|
val latestChapterTime: Long,
|
||||||
|
val totalChapterNum: Int,
|
||||||
|
val durChapterIndex: Int,
|
||||||
|
val type: Int,
|
||||||
|
val group: Long,
|
||||||
|
val order: Int,
|
||||||
|
val canUpdate: Boolean = true
|
||||||
|
) {
|
||||||
|
fun getDisplayCover() = if (customCoverUrl.isNullOrEmpty()) coverUrl else customCoverUrl
|
||||||
|
|
||||||
|
val isLocal: Boolean get() = (type and BookType.local) > 0
|
||||||
|
|
||||||
|
val isAudio: Boolean get() = (type and BookType.audio) > 0
|
||||||
|
|
||||||
|
val isImage: Boolean get() = (type and BookType.image) > 0
|
||||||
|
|
||||||
|
fun getUnreadChapterNum() = max(totalChapterNum - durChapterIndex - 1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Book.toBookShelfItem(): BookShelfItem {
|
||||||
|
return BookShelfItem(
|
||||||
|
bookUrl = bookUrl,
|
||||||
|
name = name,
|
||||||
|
author = author,
|
||||||
|
coverUrl = coverUrl,
|
||||||
|
customCoverUrl = customCoverUrl,
|
||||||
|
durChapterTitle = durChapterTitle,
|
||||||
|
durChapterTime = durChapterTime,
|
||||||
|
durChapterPos = durChapterPos,
|
||||||
|
latestChapterTitle = latestChapterTitle,
|
||||||
|
latestChapterTime = latestChapterTime,
|
||||||
|
totalChapterNum = totalChapterNum,
|
||||||
|
durChapterIndex = durChapterIndex,
|
||||||
|
type = type,
|
||||||
|
group = group,
|
||||||
|
order = order,
|
||||||
|
canUpdate = canUpdate
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -11,14 +11,19 @@ import androidx.compose.foundation.layout.Arrangement
|
|||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.FormatListBulleted
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Download
|
import androidx.compose.material.icons.filled.Download
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
import androidx.compose.material.icons.filled.FileOpen
|
import androidx.compose.material.icons.filled.FileOpen
|
||||||
@@ -65,7 +70,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.BaseRuleEvent
|
import io.legado.app.base.BaseRuleEvent
|
||||||
import io.legado.app.data.entities.Book
|
|
||||||
import io.legado.app.ui.about.AppLogSheet
|
import io.legado.app.ui.about.AppLogSheet
|
||||||
import io.legado.app.ui.book.cache.CacheActivity
|
import io.legado.app.ui.book.cache.CacheActivity
|
||||||
import io.legado.app.ui.book.import.local.ImportBookActivity
|
import io.legado.app.ui.book.import.local.ImportBookActivity
|
||||||
@@ -74,10 +78,12 @@ import io.legado.app.ui.book.manage.BookshelfManageActivity
|
|||||||
import io.legado.app.ui.book.search.SearchActivity
|
import io.legado.app.ui.book.search.SearchActivity
|
||||||
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
|
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
|
||||||
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
|
||||||
|
import io.legado.app.ui.widget.components.button.SmallOutlinedIconToggleButton
|
||||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||||
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
||||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyVerticalGrid
|
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyVerticalGrid
|
||||||
import io.legado.app.ui.widget.components.list.ListScaffold
|
import io.legado.app.ui.widget.components.list.ListScaffold
|
||||||
|
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
|
||||||
import io.legado.app.utils.readText
|
import io.legado.app.utils.readText
|
||||||
import io.legado.app.utils.startActivity
|
import io.legado.app.utils.startActivity
|
||||||
@@ -94,8 +100,8 @@ import org.koin.androidx.compose.koinViewModel
|
|||||||
@Composable
|
@Composable
|
||||||
fun BookshelfScreen(
|
fun BookshelfScreen(
|
||||||
viewModel: BookshelfViewModel = koinViewModel(),
|
viewModel: BookshelfViewModel = koinViewModel(),
|
||||||
onBookClick: (Book) -> Unit,
|
onBookClick: (BookShelfItem) -> Unit,
|
||||||
onBookLongClick: (Book) -> Unit
|
onBookLongClick: (BookShelfItem) -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val uiState by viewModel.uiState.collectAsState()
|
val uiState by viewModel.uiState.collectAsState()
|
||||||
@@ -107,6 +113,7 @@ fun BookshelfScreen(
|
|||||||
var showConfigSheet by remember { mutableStateOf(false) }
|
var showConfigSheet by remember { mutableStateOf(false) }
|
||||||
var showGroupManageSheet by remember { mutableStateOf(false) }
|
var showGroupManageSheet by remember { mutableStateOf(false) }
|
||||||
var showLogSheet by remember { mutableStateOf(false) }
|
var showLogSheet by remember { mutableStateOf(false) }
|
||||||
|
var showGroupMenu by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val clipboardManager = LocalClipboard.current
|
val clipboardManager = LocalClipboard.current
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
@@ -309,12 +316,17 @@ fun BookshelfScreen(
|
|||||||
if (uiState.groups.isNotEmpty()) {
|
if (uiState.groups.isNotEmpty()) {
|
||||||
val selectedTabIndex =
|
val selectedTabIndex =
|
||||||
pagerState.currentPage.coerceIn(0, uiState.groups.size - 1)
|
pagerState.currentPage.coerceIn(0, uiState.groups.size - 1)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
PrimaryScrollableTabRow(
|
PrimaryScrollableTabRow(
|
||||||
selectedTabIndex = selectedTabIndex,
|
selectedTabIndex = selectedTabIndex,
|
||||||
edgePadding = 0.dp,
|
edgePadding = 0.dp,
|
||||||
divider = { },
|
divider = { },
|
||||||
containerColor = GlassTopAppBarDefaults.containerColor(),
|
containerColor = GlassTopAppBarDefaults.containerColor(),
|
||||||
minTabWidth = 0.dp
|
minTabWidth = 0.dp,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
) {
|
) {
|
||||||
uiState.groups.forEachIndexed { index, group ->
|
uiState.groups.forEachIndexed { index, group ->
|
||||||
Tab(
|
Tab(
|
||||||
@@ -334,6 +346,41 @@ fun BookshelfScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (BookshelfConfig.shouldShowExpandButton) {
|
||||||
|
Box(modifier = Modifier.padding(end = 16.dp)) {
|
||||||
|
SmallOutlinedIconToggleButton(
|
||||||
|
checked = showGroupMenu,
|
||||||
|
onCheckedChange = { showGroupMenu = it },
|
||||||
|
icon = Icons.AutoMirrored.Filled.FormatListBulleted,
|
||||||
|
contentDescription = stringResource(R.string.group_manage)
|
||||||
|
)
|
||||||
|
RoundDropdownMenu(
|
||||||
|
expanded = showGroupMenu,
|
||||||
|
onDismissRequest = { showGroupMenu = false }
|
||||||
|
) { dismiss ->
|
||||||
|
uiState.groups.forEachIndexed { index, group ->
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = { Text(group.groupName) },
|
||||||
|
onClick = {
|
||||||
|
scope.launch { pagerState.animateScrollToPage(index) }
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
trailingIcon = {
|
||||||
|
if (selectedTabIndex == index) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Check,
|
||||||
|
null,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else null
|
} else null
|
||||||
@@ -533,12 +580,12 @@ fun BookshelfScreen(
|
|||||||
@Composable
|
@Composable
|
||||||
fun BookshelfPage(
|
fun BookshelfPage(
|
||||||
paddingValues: PaddingValues,
|
paddingValues: PaddingValues,
|
||||||
books: List<Book>,
|
books: List<BookShelfItem>,
|
||||||
uiState: BookshelfUiState,
|
uiState: BookshelfUiState,
|
||||||
bookshelfLayoutMode: Int,
|
bookshelfLayoutMode: Int,
|
||||||
bookshelfLayoutGrid: Int,
|
bookshelfLayoutGrid: Int,
|
||||||
onBookClick: (Book) -> Unit,
|
onBookClick: (BookShelfItem) -> Unit,
|
||||||
onBookLongClick: (Book) -> Unit
|
onBookLongClick: (BookShelfItem) -> Unit
|
||||||
) {
|
) {
|
||||||
FastScrollLazyVerticalGrid(
|
FastScrollLazyVerticalGrid(
|
||||||
columns = GridCells.Fixed(bookshelfLayoutGrid.coerceAtLeast(1)),
|
columns = GridCells.Fixed(bookshelfLayoutGrid.coerceAtLeast(1)),
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package io.legado.app.ui.main.bookshelf
|
package io.legado.app.ui.main.bookshelf
|
||||||
|
|
||||||
import io.legado.app.data.entities.Book
|
|
||||||
import io.legado.app.data.entities.BookGroup
|
import io.legado.app.data.entities.BookGroup
|
||||||
import io.legado.app.ui.widget.components.list.ListUiState
|
import io.legado.app.ui.widget.components.list.ListUiState
|
||||||
|
|
||||||
data class BookshelfUiState(
|
data class BookshelfUiState(
|
||||||
override val items: List<Book> = emptyList(),
|
override val items: List<BookShelfItem> = emptyList(),
|
||||||
override val selectedIds: Set<Any> = emptySet(),
|
override val selectedIds: Set<Any> = emptySet(),
|
||||||
override val searchKey: String = "",
|
override val searchKey: String = "",
|
||||||
override val isSearch: Boolean = false,
|
override val isSearch: Boolean = false,
|
||||||
override val isLoading: Boolean = false,
|
override val isLoading: Boolean = false,
|
||||||
val groups: List<BookGroup> = emptyList(),
|
val groups: List<BookGroup> = emptyList(),
|
||||||
val groupPreviews: Map<Long, List<Book>> = emptyMap(),
|
val groupPreviews: Map<Long, List<BookShelfItem>> = emptyMap(),
|
||||||
val selectedGroupIndex: Int = 0,
|
val selectedGroupIndex: Int = 0,
|
||||||
val loadingText: String? = null,
|
val loadingText: String? = null,
|
||||||
val updatingBooks: Set<String> = emptySet()
|
val updatingBooks: Set<String> = emptySet()
|
||||||
) : ListUiState<Book>
|
) : ListUiState<BookShelfItem>
|
||||||
@@ -22,7 +22,6 @@ import io.legado.app.data.repository.UploadRepository
|
|||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.help.book.BookHelp
|
import io.legado.app.help.book.BookHelp
|
||||||
import io.legado.app.help.book.addType
|
import io.legado.app.help.book.addType
|
||||||
import io.legado.app.help.book.isLocal
|
|
||||||
import io.legado.app.help.book.isUpError
|
import io.legado.app.help.book.isUpError
|
||||||
import io.legado.app.help.book.removeType
|
import io.legado.app.help.book.removeType
|
||||||
import io.legado.app.help.book.sync
|
import io.legado.app.help.book.sync
|
||||||
@@ -123,13 +122,15 @@ class BookshelfViewModel(
|
|||||||
val allGroupsFlow: StateFlow<List<BookGroup>> = bookGroupRepository.flowAll()
|
val allGroupsFlow: StateFlow<List<BookGroup>> = bookGroupRepository.flowAll()
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
||||||
|
|
||||||
private val allBooksFlow = appDb.bookDao.flowAll().flowOn(Dispatchers.Default)
|
private val allBooksFlow = appDb.bookDao.flowBookShelf().flowOn(Dispatchers.Default)
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
val booksFlow = combine(groupIdFlow, refreshTrigger) { groupId, _ -> groupId }
|
val booksFlow = combine(groupIdFlow, refreshTrigger) { groupId, _ -> groupId }
|
||||||
.flatMapLatest { groupId ->
|
.flatMapLatest { groupId ->
|
||||||
appDb.bookDao.flowByGroup(groupId).map { list ->
|
appDb.bookDao.flowByGroup(groupId).map { list ->
|
||||||
sortBooks(list, groupsFlow.value.find { it.groupId == groupId })
|
sortBooks(
|
||||||
|
list.map { it.toBookShelfItem() },
|
||||||
|
groupsFlow.value.find { it.groupId == groupId })
|
||||||
}
|
}
|
||||||
}.flowOn(Dispatchers.Default)
|
}.flowOn(Dispatchers.Default)
|
||||||
|
|
||||||
@@ -269,7 +270,7 @@ class BookshelfViewModel(
|
|||||||
upTocPool.close()
|
upTocPool.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortBooks(list: List<Book>, group: BookGroup?): List<Book> {
|
private fun sortBooks(list: List<BookShelfItem>, group: BookGroup?): List<BookShelfItem> {
|
||||||
val bookSort = group?.getRealBookSort() ?: BookshelfConfig.bookshelfSort
|
val bookSort = group?.getRealBookSort() ?: BookshelfConfig.bookshelfSort
|
||||||
val isDescending = BookshelfConfig.bookshelfSortOrder == 1
|
val isDescending = BookshelfConfig.bookshelfSortOrder == 1
|
||||||
|
|
||||||
@@ -304,7 +305,7 @@ class BookshelfViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun getBooksFlow(groupId: Long): Flow<List<Book>> {
|
fun getBooksFlow(groupId: Long): Flow<List<BookShelfItem>> {
|
||||||
return combine(
|
return combine(
|
||||||
appDb.bookDao.flowByGroup(groupId),
|
appDb.bookDao.flowByGroup(groupId),
|
||||||
searchKeyFlow,
|
searchKeyFlow,
|
||||||
@@ -312,10 +313,11 @@ class BookshelfViewModel(
|
|||||||
refreshTrigger
|
refreshTrigger
|
||||||
) { books, searchKey, groups, _ ->
|
) { books, searchKey, groups, _ ->
|
||||||
val group = groups.find { it.groupId == groupId }
|
val group = groups.find { it.groupId == groupId }
|
||||||
|
val bookshelfItems = books.map { it.toBookShelfItem() }
|
||||||
val filtered = if (searchKey.isEmpty()) {
|
val filtered = if (searchKey.isEmpty()) {
|
||||||
books
|
bookshelfItems
|
||||||
} else {
|
} else {
|
||||||
books.filter {
|
bookshelfItems.filter {
|
||||||
it.name.contains(searchKey, true) || it.author.contains(searchKey, true)
|
it.name.contains(searchKey, true) || it.author.contains(searchKey, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,13 +351,11 @@ class BookshelfViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun upToc(books: List<Book>) {
|
fun upToc(books: List<BookShelfItem>) {
|
||||||
execute(context = upTocPool) {
|
execute(context = upTocPool) {
|
||||||
books.filter {
|
val bookUrls = books.filter { !it.isLocal && it.canUpdate }.map { it.bookUrl }
|
||||||
!it.isLocal && it.canUpdate
|
val fullBooks = bookUrls.mapNotNull { appDb.bookDao.getBook(it) }
|
||||||
}.let {
|
addToWaitUp(fullBooks)
|
||||||
addToWaitUp(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,17 +562,20 @@ class BookshelfViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exportToUri(uri: Uri, books: List<Book>) {
|
fun exportToUri(uri: Uri, items: List<BookShelfItem>) {
|
||||||
execute {
|
execute {
|
||||||
context.contentResolver.openOutputStream(uri)?.use { out ->
|
context.contentResolver.openOutputStream(uri)?.use { out ->
|
||||||
val writer = JsonWriter(OutputStreamWriter(out, "UTF-8"))
|
val writer = JsonWriter(OutputStreamWriter(out, "UTF-8"))
|
||||||
writer.setIndent(" ")
|
writer.setIndent(" ")
|
||||||
writer.beginArray()
|
writer.beginArray()
|
||||||
books.forEach {
|
items.forEach {
|
||||||
val bookMap = hashMapOf<String, String?>()
|
val bookMap = hashMapOf<String, String?>()
|
||||||
bookMap["name"] = it.name
|
bookMap["name"] = it.name
|
||||||
bookMap["author"] = it.author
|
bookMap["author"] = it.author
|
||||||
bookMap["intro"] = it.getDisplayIntro()
|
// intro is not in BookShelfItem, fetch from DB if needed or skip
|
||||||
|
// For now, let's keep it simple and skip intro or fetch it
|
||||||
|
val fullBook = appDb.bookDao.getBook(it.bookUrl)
|
||||||
|
bookMap["intro"] = fullBook?.getDisplayIntro()
|
||||||
GSON.toJson(bookMap, bookMap::class.java, writer)
|
GSON.toJson(bookMap, bookMap::class.java, writer)
|
||||||
}
|
}
|
||||||
writer.endArray()
|
writer.endArray()
|
||||||
@@ -585,14 +588,15 @@ class BookshelfViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun uploadBookshelf(books: List<Book>) {
|
fun uploadBookshelf(items: List<BookShelfItem>) {
|
||||||
execute {
|
execute {
|
||||||
val json = withContext(Dispatchers.Default) {
|
val json = withContext(Dispatchers.Default) {
|
||||||
val list = books.map {
|
val list = items.map {
|
||||||
val bookMap = hashMapOf<String, String?>()
|
val bookMap = hashMapOf<String, String?>()
|
||||||
bookMap["name"] = it.name
|
bookMap["name"] = it.name
|
||||||
bookMap["author"] = it.author
|
bookMap["author"] = it.author
|
||||||
bookMap["intro"] = it.getDisplayIntro()
|
val fullBook = appDb.bookDao.getBook(it.bookUrl)
|
||||||
|
bookMap["intro"] = fullBook?.getDisplayIntro()
|
||||||
bookMap
|
bookMap
|
||||||
}
|
}
|
||||||
GSON.toJson(list)
|
GSON.toJson(list)
|
||||||
@@ -619,9 +623,9 @@ class BookshelfViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exportBookshelf(books: List<Book>?, success: (file: File) -> Unit) {
|
fun exportBookshelf(items: List<BookShelfItem>?, success: (file: File) -> Unit) {
|
||||||
execute {
|
execute {
|
||||||
books?.let {
|
items?.let {
|
||||||
val path = "${context.filesDir}/books.json"
|
val path = "${context.filesDir}/books.json"
|
||||||
FileUtils.delete(path)
|
FileUtils.delete(path)
|
||||||
val file = FileUtils.createFileWithReplace(path)
|
val file = FileUtils.createFileWithReplace(path)
|
||||||
@@ -629,11 +633,12 @@ class BookshelfViewModel(
|
|||||||
val writer = JsonWriter(OutputStreamWriter(out, "UTF-8"))
|
val writer = JsonWriter(OutputStreamWriter(out, "UTF-8"))
|
||||||
writer.setIndent(" ")
|
writer.setIndent(" ")
|
||||||
writer.beginArray()
|
writer.beginArray()
|
||||||
books.forEach {
|
items.forEach {
|
||||||
val bookMap = hashMapOf<String, String?>()
|
val bookMap = hashMapOf<String, String?>()
|
||||||
bookMap["name"] = it.name
|
bookMap["name"] = it.name
|
||||||
bookMap["author"] = it.author
|
bookMap["author"] = it.author
|
||||||
bookMap["intro"] = it.getDisplayIntro()
|
val fullBook = appDb.bookDao.getBook(it.bookUrl)
|
||||||
|
bookMap["intro"] = fullBook?.getDisplayIntro()
|
||||||
GSON.toJson(bookMap, bookMap::class.java, writer)
|
GSON.toJson(bookMap, bookMap::class.java, writer)
|
||||||
}
|
}
|
||||||
writer.endArray()
|
writer.endArray()
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import io.legado.app.help.config.AppConfig
|
|||||||
import io.legado.app.ui.book.audio.AudioPlayActivity
|
import io.legado.app.ui.book.audio.AudioPlayActivity
|
||||||
import io.legado.app.ui.book.manga.ReadMangaActivity
|
import io.legado.app.ui.book.manga.ReadMangaActivity
|
||||||
import io.legado.app.ui.book.read.ReadBookActivity
|
import io.legado.app.ui.book.read.ReadBookActivity
|
||||||
|
import io.legado.app.ui.main.bookshelf.BookShelfItem
|
||||||
import splitties.systemservices.clipboardManager
|
import splitties.systemservices.clipboardManager
|
||||||
import splitties.systemservices.connectivityManager
|
import splitties.systemservices.connectivityManager
|
||||||
import splitties.systemservices.uiModeManager
|
import splitties.systemservices.uiModeManager
|
||||||
@@ -80,6 +81,22 @@ fun Context.startActivityForBook(
|
|||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.startActivityForBook(
|
||||||
|
book: BookShelfItem,
|
||||||
|
configIntent: Intent.() -> Unit = {},
|
||||||
|
) {
|
||||||
|
val cls = when {
|
||||||
|
book.isAudio -> AudioPlayActivity::class.java
|
||||||
|
!book.isLocal && book.isImage && AppConfig.showMangaUi -> ReadMangaActivity::class.java
|
||||||
|
else -> ReadBookActivity::class.java
|
||||||
|
}
|
||||||
|
val intent = Intent(this, cls)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
intent.putExtra("bookUrl", book.bookUrl)
|
||||||
|
intent.apply(configIntent)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
inline fun <reified T : Service> Context.startService(configIntent: Intent.() -> Unit = {}) {
|
inline fun <reified T : Service> Context.startService(configIntent: Intent.() -> Unit = {}) {
|
||||||
startService(Intent(this, T::class.java).apply(configIntent))
|
startService(Intent(this, T::class.java).apply(configIntent))
|
||||||
}
|
}
|
||||||
@@ -464,4 +481,3 @@ fun Context.themeColor(attr: Int): Int {
|
|||||||
typedValue.data
|
typedValue.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user