From c37c5d40857d20bb6bebc49c5b469cad066b26f8 Mon Sep 17 00:00:00 2001 From: Horis <8674809+821938089@users.noreply.github.com> Date: Tue, 21 Jan 2025 20:30:26 +0800 Subject: [PATCH] Fix import book slow --- .../java/io/legado/app/data/dao/BookDao.kt | 7 ++- .../java/io/legado/app/data/entities/Book.kt | 1 - .../app/ui/book/import/local/ImportBook.kt | 14 ++++++ .../book/import/local/ImportBookActivity.kt | 16 +++--- .../ui/book/import/local/ImportBookAdapter.kt | 50 +++++++++++-------- .../book/import/local/ImportBookViewModel.kt | 49 +++++++++--------- 6 files changed, 80 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/book/import/local/ImportBook.kt diff --git a/app/src/main/java/io/legado/app/data/dao/BookDao.kt b/app/src/main/java/io/legado/app/data/dao/BookDao.kt index 87316b9d9..be66d5495 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookDao.kt @@ -132,8 +132,11 @@ interface BookDao { @Query("select 1 from books where bookUrl = :bookUrl") fun has(bookUrl: String): Boolean? - @Query("select 1 from books where originName = :fileName or origin like '%' || :fileName") - fun hasFile(fileName: String): Boolean? + @Query( + """select exists(select 1 from books where type & ${BookType.local} > 0 + and (originName = :fileName or (origin != '${BookType.localTag}' and origin like '%' || :fileName)))""" + ) + fun hasFile(fileName: String): Boolean @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg book: Book) diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index 9e4a8296f..049c5c918 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -16,7 +16,6 @@ import io.legado.app.help.book.BookHelp import io.legado.app.help.book.ContentProcessor import io.legado.app.help.book.isEpub import io.legado.app.help.book.isImage -import io.legado.app.help.book.isPdf import io.legado.app.help.book.simulatedTotalChapterNum import io.legado.app.help.config.AppConfig import io.legado.app.help.config.ReadBookConfig diff --git a/app/src/main/java/io/legado/app/ui/book/import/local/ImportBook.kt b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBook.kt new file mode 100644 index 000000000..0ffa129e7 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBook.kt @@ -0,0 +1,14 @@ +package io.legado.app.ui.book.import.local + +import io.legado.app.model.localBook.LocalBook +import io.legado.app.utils.FileDoc + +data class ImportBook( + val file: FileDoc, + var isOnBookShelf: Boolean = LocalBook.isOnBookShelf(file.name) +) { + val name get() = file.name + val isDir get() = file.isDir + val size get() = file.size + val lastModified get() = file.lastModified +} diff --git a/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookActivity.kt index e1db889a0..b02d9280c 100644 --- a/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookActivity.kt @@ -101,7 +101,7 @@ class ImportBookActivity : BaseImportBookActivity(), override fun onMenuItemClick(item: MenuItem?): Boolean { when (item?.itemId) { - R.id.menu_del_selection -> viewModel.deleteDoc(adapter.selectedUris) { + R.id.menu_del_selection -> viewModel.deleteDoc(adapter.selected) { adapter.removeSelection() } } @@ -118,8 +118,11 @@ class ImportBookActivity : BaseImportBookActivity(), @SuppressLint("NotifyDataSetChanged") override fun onClickSelectBarMainAction() { - viewModel.addToBookshelf(adapter.selectedUris) { - adapter.selectedUris.clear() + viewModel.addToBookshelf(adapter.selected) { + adapter.selected.forEach { + it.isOnBookShelf = true + } + adapter.selected.clear() adapter.notifyDataSetChanged() } } @@ -129,6 +132,7 @@ class ImportBookActivity : BaseImportBookActivity(), binding.tvEmptyMsg.setText(R.string.empty_msg_import_book) binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.adapter = adapter + binding.recyclerView.recycledViewPool.setMaxRecycledViews(0, 15) binding.selectActionBar.setMainActionText(R.string.add_to_bookshelf) binding.selectActionBar.inflateMenu(R.menu.import_book_sel) binding.selectActionBar.setOnMenuItemClickListener(this) @@ -212,7 +216,7 @@ class ImportBookActivity : BaseImportBookActivity(), viewModel.sort = sort putPrefInt(PreferKey.localBookImportSort, sort) if (scanDocJob?.isActive != true) { - viewModel.dataCallback?.setItems(adapter.getItems()) + viewModel.dataCallback?.upAdapter() } } @@ -234,7 +238,7 @@ class ImportBookActivity : BaseImportBookActivity(), path = path + doc.name + File.separator } binding.tvPath.text = path - adapter.selectedUris.clear() + adapter.selected.clear() adapter.clearItems() viewModel.loadDoc(lastDoc) } @@ -295,7 +299,7 @@ class ImportBookActivity : BaseImportBookActivity(), } override fun upCountView() { - binding.selectActionBar.upCountView(adapter.selectedUris.size, adapter.checkableCount) + binding.selectActionBar.upCountView(adapter.selected.size, adapter.checkableCount) } override fun startRead(fileDoc: FileDoc) { diff --git a/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookAdapter.kt b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookAdapter.kt index 7b59a526f..42a303a38 100644 --- a/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookAdapter.kt @@ -9,12 +9,18 @@ import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.constant.AppConst import io.legado.app.databinding.ItemImportBookBinding import io.legado.app.model.localBook.LocalBook -import io.legado.app.utils.* +import io.legado.app.utils.ConvertUtils +import io.legado.app.utils.FileDoc +import io.legado.app.utils.gone +import io.legado.app.utils.invisible +import io.legado.app.utils.visible +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job class ImportBookAdapter(context: Context, val callBack: CallBack) : - RecyclerAdapter(context) { - val selectedUris = hashSetOf() + RecyclerAdapter(context) { + val selected = hashSetOf() var checkableCount = 0 override fun getViewBinding(parent: ViewGroup): ItemImportBookBinding { @@ -28,7 +34,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) : override fun convert( holder: ItemViewHolder, binding: ItemImportBookBinding, - item: FileDoc, + item: ImportBook, payloads: MutableList ) { binding.run { @@ -40,7 +46,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) : llBrief.gone() cbSelect.isChecked = false } else { - if (isOnBookShelf(item)) { + if (item.isOnBookShelf) { ivIcon.setImageResource(R.drawable.ic_book_has) ivIcon.visible() cbSelect.invisible() @@ -52,11 +58,11 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) : tvTag.text = item.name.substringAfterLast(".") tvSize.text = ConvertUtils.formatFileSize(item.size) tvDate.text = AppConst.dateFormat.format(item.lastModified) - cbSelect.isChecked = selectedUris.contains(item.toString()) + cbSelect.isChecked = selected.contains(item) } tvName.text = item.name } else { - cbSelect.isChecked = selectedUris.contains(item.toString()) + cbSelect.isChecked = selected.contains(item) } } } @@ -65,18 +71,18 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) : holder.itemView.setOnClickListener { getItem(holder.layoutPosition)?.let { if (it.isDir) { - callBack.nextDoc(it) - } else if (!isOnBookShelf(it)) { - if (!selectedUris.contains(it.toString())) { - selectedUris.add(it.toString()) + callBack.nextDoc(it.file) + } else if (!it.isOnBookShelf) { + if (!selected.contains(it)) { + selected.add(it) } else { - selectedUris.remove(it.toString()) + selected.remove(it) } notifyItemChanged(holder.layoutPosition, true) callBack.upCountView() } else { /* 点击开始阅读 */ - callBack.startRead(it) + callBack.startRead(it.file) } } } @@ -89,7 +95,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) : private fun upCheckableCount() { checkableCount = 0 getItems().forEach { - if (!it.isDir && !isOnBookShelf(it)) { + if (!it.isDir && !it.isOnBookShelf) { checkableCount++ } } @@ -100,12 +106,12 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) : fun selectAll(selectAll: Boolean) { if (selectAll) { getItems().forEach { - if (!it.isDir && !isOnBookShelf(it)) { - selectedUris.add(it.toString()) + if (!it.isDir && !it.isOnBookShelf) { + selected.add(it) } } } else { - selectedUris.clear() + selected.clear() } notifyDataSetChanged() callBack.upCountView() @@ -113,11 +119,11 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) : fun revertSelection() { getItems().forEach { - if (!it.isDir && !isOnBookShelf(it)) { - if (selectedUris.contains(it.toString())) { - selectedUris.remove(it.toString()) + if (!it.isDir && !it.isOnBookShelf) { + if (selected.contains(it)) { + selected.remove(it) } else { - selectedUris.add(it.toString()) + selected.add(it) } } } @@ -127,7 +133,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) : fun removeSelection() { for (i in getItems().lastIndex downTo 0) { - if (getItem(i)?.toString() in selectedUris) { + if (getItem(i) in selected) { removeItem(i) } } diff --git a/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookViewModel.kt index fe9faa0cf..33d148b81 100644 --- a/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/import/local/ImportBookViewModel.kt @@ -1,8 +1,6 @@ package io.legado.app.ui.book.import.local import android.app.Application -import android.net.Uri -import androidx.documentfile.provider.DocumentFile import io.legado.app.base.BaseViewModel import io.legado.app.constant.AppLog import io.legado.app.constant.AppPattern.archiveFileRegex @@ -11,19 +9,19 @@ import io.legado.app.constant.PreferKey import io.legado.app.model.localBook.LocalBook import io.legado.app.utils.AlphanumComparator import io.legado.app.utils.FileDoc +import io.legado.app.utils.delete import io.legado.app.utils.getPrefInt -import io.legado.app.utils.isContentScheme import io.legado.app.utils.list import io.legado.app.utils.toastOnUi import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext -import java.io.File import java.util.Collections import kotlin.coroutines.coroutineContext @@ -33,20 +31,24 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application) var sort = context.getPrefInt(PreferKey.localBookImportSort) var dataCallback: DataCallback? = null var dataFlowStart: (() -> Unit)? = null - val dataFlow = callbackFlow> { + val dataFlow = callbackFlow> { - val list = Collections.synchronizedList(ArrayList()) + val list = Collections.synchronizedList(ArrayList()) dataCallback = object : DataCallback { override fun setItems(fileDocs: List) { list.clear() - list.addAll(fileDocs) + fileDocs.mapTo(list) { + ImportBook(it) + } trySend(list) } override fun addItems(fileDocs: List) { - list.addAll(fileDocs) + fileDocs.mapTo(list) { + ImportBook(it) + } trySend(list) } @@ -64,6 +66,10 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application) ) } } + + override fun upAdapter() { + trySend(list) + } } withContext(Main) { @@ -76,21 +82,17 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application) }.map { docList -> val comparator = when (sort) { - 2 -> compareBy({ !it.isDir }, { -it.lastModified }) + 2 -> compareBy({ !it.isDir }, { -it.lastModified }) 1 -> compareBy({ !it.isDir }, { -it.size }) else -> compareBy { !it.isDir } } then compareBy(AlphanumComparator) { it.name } docList.sortedWith(comparator) }.flowOn(IO) - fun addToBookshelf(uriList: HashSet, finally: () -> Unit) { + fun addToBookshelf(bookList: HashSet, finally: () -> Unit) { execute { - val fileUris = uriList.map { - if (it.isContentScheme()) { - Uri.parse(it) - } else { - Uri.fromFile(File(it)) - } + val fileUris = bookList.map { + it.file.uri } LocalBook.importFiles(fileUris) }.onError { @@ -101,17 +103,10 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application) } } - fun deleteDoc(uriList: HashSet, finally: () -> Unit) { + fun deleteDoc(bookList: HashSet, finally: () -> Unit) { execute { - uriList.forEach { - val uri = Uri.parse(it) - if (uri.isContentScheme()) { - DocumentFile.fromSingleUri(context, uri)?.delete() - } else { - uri.path?.let { path -> - File(path).delete() - } - } + bookList.forEach { + it.file.delete() } }.onFinally { finally.invoke() @@ -190,6 +185,8 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application) fun screen(key: String?) + fun upAdapter() + } } \ No newline at end of file