This commit is contained in:
kunfei 2022-03-07 10:25:09 +08:00
parent bb3c475d19
commit 536577b869
2 changed files with 139 additions and 57 deletions

View File

@ -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)

View File

@ -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()
}
}