diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt index 9616ee388..72f111296 100644 --- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt +++ b/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() - private val bookFileRegex = Regex("(?i).*\\.(txt|epub|umd)") private var rootDoc: FileDoc? = null private val subDocs = arrayListOf() 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 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 + adapter.setItems(docs) + } + } } private fun initRootDoc() { @@ -197,9 +203,16 @@ class ImportBookActivity : VMBaseActivity - 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 Unit = { - launch { - adapter.addItem(it) - } - } - @Synchronized override fun nextDoc(fileDoc: FileDoc) { subDocs.add(fileDoc) diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt index d8e193112..dae6bc710 100644 --- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt +++ b/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> { + + val list = Collections.synchronizedList(ArrayList()) + + dataCallback = object : DataCallback { + + override fun setItems(fileDocs: List) { + list.clear() + list.addAll(fileDocs) + trySend(list) + } + + override fun addItems(fileDocs: List) { + 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, 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() 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) + + fun addItems(fileDocs: List) + + fun clear() + + } + } \ No newline at end of file