kunfei 4 лет назад
Родитель
Сommit
ac509a499a

+ 24 - 53
app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt

@@ -25,10 +25,9 @@ import io.legado.app.ui.widget.SelectActionBar
 import io.legado.app.utils.*
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.Dispatchers.Main
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
 import java.io.File
 
 /**
@@ -41,10 +40,10 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
 
     override val binding by viewBinding(ActivityImportBookBinding::inflate)
     override val viewModel by viewModels<ImportBookViewModel>()
-    private val bookFileRegex = Regex("(?i).*\\.(txt|epub|umd)")
     private var rootDoc: FileDoc? = null
     private val subDocs = arrayListOf<FileDoc>()
     private val adapter by lazy { ImportBookAdapter(this, this) }
+    private var scanDocJob: Job? = null
 
     private val selectFolder = registerForActivityResult(HandleFileContract()) {
         it.uri?.let { uri ->
@@ -62,7 +61,6 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
         initView()
         initEvent()
         initData()
-        initRootDoc()
     }
 
     override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
@@ -84,15 +82,15 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
             R.id.menu_import_file_name -> alertImportFileName()
             R.id.menu_sort_name -> {
                 viewModel.sort = 0
-                upPath()
+                upSort()
             }
             R.id.menu_sort_size -> {
                 viewModel.sort = 1
-                upPath()
+                upSort()
             }
             R.id.menu_sort_time -> {
                 viewModel.sort = 2
-                upPath()
+                upSort()
             }
         }
         return super.onCompatOptionsItemSelected(item)
@@ -140,11 +138,19 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
     }
 
     private fun initData() {
+        viewModel.dataFlowStart = {
+            initRootDoc()
+        }
         launch {
             appDb.bookDao.flowLocalUri().conflate().collect {
                 adapter.upBookHas(it)
             }
         }
+        launch {
+            viewModel.dataFlow.conflate().collect { docs ->
+                adapter.setItems(docs)
+            }
+        }
     }
 
     private fun initRootDoc() {
@@ -197,9 +203,16 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
             .request()
     }
 
+    private fun upSort() {
+        if (scanDocJob?.isCancelled == true || scanDocJob?.isCompleted == true) {
+            viewModel.dataCallback?.setItems(adapter.getItems())
+        }
+    }
+
     @Synchronized
     private fun upPath() {
         rootDoc?.let {
+            scanDocJob?.cancel()
             upDocs(it)
         }
     }
@@ -215,44 +228,7 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
         binding.tvPath.text = path
         adapter.selectedUris.clear()
         adapter.clearItems()
-        launch(IO) {
-            runCatching {
-                val docList = DocumentUtils.listFiles(lastDoc.uri) { item ->
-                    when {
-                        item.name.startsWith(".") -> false
-                        item.isDir -> true
-                        else -> item.name.matches(bookFileRegex)
-                    }
-                }
-                when (viewModel.sort) {
-                    2 -> docList.sortWith(
-                        compareBy(
-                            { !it.isDir },
-                            { it.date },
-                            { it.name }
-                        )
-                    )
-                    1 -> docList.sortWith(
-                        compareBy(
-                            { !it.isDir },
-                            { it.size },
-                            { it.name }
-                        )
-                    )
-                    else -> docList.sortWith(
-                        compareBy(
-                            { !it.isDir },
-                            { it.name }
-                        )
-                    )
-                }
-                withContext(Main) {
-                    adapter.setItems(docList)
-                }
-            }.onFailure {
-                toastOnUi("获取文件列表出错\n${it.localizedMessage}")
-            }
-        }
+        viewModel.loadDoc(lastDoc.uri)
     }
 
     /**
@@ -263,8 +239,9 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
             adapter.clearItems()
             val lastDoc = subDocs.lastOrNull() ?: doc
             binding.refreshProgressBar.isAutoLoading = true
-            launch(IO) {
-                viewModel.scanDoc(lastDoc, true, find) {
+            scanDocJob?.cancel()
+            scanDocJob = launch(IO) {
+                viewModel.scanDoc(lastDoc, true, this) {
                     launch {
                         binding.refreshProgressBar.isAutoLoading = false
                     }
@@ -288,12 +265,6 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
         }
     }
 
-    private val find: (docItem: FileDoc) -> Unit = {
-        launch {
-            adapter.addItem(it)
-        }
-    }
-
     @Synchronized
     override fun nextDoc(fileDoc: FileDoc) {
         subDocs.add(fileDoc)

+ 115 - 4
app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt

@@ -9,12 +9,79 @@ import io.legado.app.utils.DocumentUtils
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.isContentScheme
 import io.legado.app.utils.toastOnUi
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.Dispatchers.Main
+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.*
 
 
 class ImportBookViewModel(application: Application) : BaseViewModel(application) {
-
+    private val bookFileRegex = Regex("(?i).*\\.(txt|epub|umd)")
     var sort = 0
+    var dataCallback: DataCallback? = null
+    var dataFlowStart: (() -> Unit)? = null
+    val dataFlow = callbackFlow<List<FileDoc>> {
+
+        val list = Collections.synchronizedList(ArrayList<FileDoc>())
+
+        dataCallback = object : DataCallback {
+
+            override fun setItems(fileDocs: List<FileDoc>) {
+                list.clear()
+                list.addAll(fileDocs)
+                trySend(list)
+            }
+
+            override fun addItems(fileDocs: List<FileDoc>) {
+                list.addAll(fileDocs)
+                trySend(list)
+            }
+
+            override fun clear() {
+                list.clear()
+                trySend(emptyList())
+            }
+        }
+
+        withContext(Main) {
+            dataFlowStart?.invoke()
+        }
+
+        awaitClose {
+            dataCallback = null
+        }
+
+    }.map { docList ->
+        when (sort) {
+            2 -> docList.sortedWith(
+                compareBy(
+                    { !it.isDir },
+                    { it.date },
+                    { it.name }
+                )
+            )
+            1 -> docList.sortedWith(
+                compareBy(
+                    { !it.isDir },
+                    { it.size },
+                    { it.name }
+                )
+            )
+            else -> docList.sortedWith(
+                compareBy(
+                    { !it.isDir },
+                    { it.name }
+                )
+            )
+        }
+    }.flowOn(IO)
 
     fun addToBookshelf(uriList: HashSet<String>, finally: () -> Unit) {
         execute {
@@ -43,22 +110,56 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
         }
     }
 
+    fun loadDoc(uri: Uri) {
+        execute {
+            val docList = DocumentUtils.listFiles(uri) { item ->
+                when {
+                    item.name.startsWith(".") -> false
+                    item.isDir -> true
+                    else -> item.name.matches(bookFileRegex)
+                }
+            }
+            dataCallback?.setItems(docList)
+        }.onError {
+            context.toastOnUi("获取文件列表出错\n${it.localizedMessage}")
+        }
+    }
+
     fun scanDoc(
         fileDoc: FileDoc,
         isRoot: Boolean,
-        find: (docItem: FileDoc) -> Unit,
+        scope: CoroutineScope,
         finally: (() -> Unit)? = null
     ) {
+        if (isRoot) {
+            dataCallback?.clear()
+        }
+        if (!scope.isActive) {
+            finally?.invoke()
+            return
+        }
         kotlin.runCatching {
+            val list = ArrayList<FileDoc>()
             DocumentUtils.listFiles(fileDoc.uri).forEach { docItem ->
+                if (!scope.isActive) {
+                    finally?.invoke()
+                    return
+                }
                 if (docItem.isDir) {
-                    scanDoc(docItem, false, find)
+                    scanDoc(docItem, false, scope)
                 } else if (docItem.name.endsWith(".txt", true)
                     || docItem.name.endsWith(".epub", true)
                 ) {
-                    find(docItem)
+                    list.add(docItem)
                 }
             }
+            if (!scope.isActive) {
+                finally?.invoke()
+                return
+            }
+            if (list.isNotEmpty()) {
+                dataCallback?.addItems(list)
+            }
         }.onFailure {
             context.toastOnUi("扫描文件夹出错\n${it.localizedMessage}")
         }
@@ -67,4 +168,14 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
         }
     }
 
+    interface DataCallback {
+
+        fun setItems(fileDocs: List<FileDoc>)
+
+        fun addItems(fileDocs: List<FileDoc>)
+
+        fun clear()
+
+    }
+
 }