Răsfoiți Sursa

Fix import book slow

Horis 1 an în urmă
părinte
comite
77ba5282e9

+ 5 - 2
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)

+ 0 - 1
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

+ 14 - 0
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
+}

+ 10 - 6
app/src/main/java/io/legado/app/ui/book/import/local/ImportBookActivity.kt

@@ -101,7 +101,7 @@ class ImportBookActivity : BaseImportBookActivity<ImportBookViewModel>(),
 
     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<ImportBookViewModel>(),
 
     @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<ImportBookViewModel>(),
         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<ImportBookViewModel>(),
         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<ImportBookViewModel>(),
             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<ImportBookViewModel>(),
     }
 
     override fun upCountView() {
-        binding.selectActionBar.upCountView(adapter.selectedUris.size, adapter.checkableCount)
+        binding.selectActionBar.upCountView(adapter.selected.size, adapter.checkableCount)
     }
 
     override fun startRead(fileDoc: FileDoc) {

+ 28 - 22
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<FileDoc, ItemImportBookBinding>(context) {
-    val selectedUris = hashSetOf<String>()
+    RecyclerAdapter<ImportBook, ItemImportBookBinding>(context) {
+    val selected = hashSetOf<ImportBook>()
     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<Any>
     ) {
         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)
             }
         }

+ 23 - 26
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<List<FileDoc>> {
+    val dataFlow = callbackFlow<List<ImportBook>> {
 
-        val list = Collections.synchronizedList(ArrayList<FileDoc>())
+        val list = Collections.synchronizedList(ArrayList<ImportBook>())
 
         dataCallback = object : DataCallback {
 
             override fun setItems(fileDocs: List<FileDoc>) {
                 list.clear()
-                list.addAll(fileDocs)
+                fileDocs.mapTo(list) {
+                    ImportBook(it)
+                }
                 trySend(list)
             }
 
             override fun addItems(fileDocs: List<FileDoc>) {
-                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<FileDoc>({ !it.isDir }, { -it.lastModified })
+            2 -> compareBy<ImportBook>({ !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<String>, finally: () -> Unit) {
+    fun addToBookshelf(bookList: HashSet<ImportBook>, 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<String>, finally: () -> Unit) {
+    fun deleteDoc(bookList: HashSet<ImportBook>, 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()
+
     }
 
 }