pull/1693/head
kunfei 3 years ago
parent bb3c475d19
commit 536577b869
  1. 77
      app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt
  2. 119
      app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt

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

@ -9,12 +9,79 @@ import io.legado.app.utils.DocumentUtils
import io.legado.app.utils.FileDoc import io.legado.app.utils.FileDoc
import io.legado.app.utils.isContentScheme import io.legado.app.utils.isContentScheme
import io.legado.app.utils.toastOnUi 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.io.File
import java.util.*
class ImportBookViewModel(application: Application) : BaseViewModel(application) { class ImportBookViewModel(application: Application) : BaseViewModel(application) {
private val bookFileRegex = Regex("(?i).*\\.(txt|epub|umd)")
var sort = 0 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) { fun addToBookshelf(uriList: HashSet<String>, finally: () -> Unit) {
execute { 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( fun scanDoc(
fileDoc: FileDoc, fileDoc: FileDoc,
isRoot: Boolean, isRoot: Boolean,
find: (docItem: FileDoc) -> Unit, scope: CoroutineScope,
finally: (() -> Unit)? = null finally: (() -> Unit)? = null
) { ) {
if (isRoot) {
dataCallback?.clear()
}
if (!scope.isActive) {
finally?.invoke()
return
}
kotlin.runCatching { kotlin.runCatching {
val list = ArrayList<FileDoc>()
DocumentUtils.listFiles(fileDoc.uri).forEach { docItem -> DocumentUtils.listFiles(fileDoc.uri).forEach { docItem ->
if (!scope.isActive) {
finally?.invoke()
return
}
if (docItem.isDir) { if (docItem.isDir) {
scanDoc(docItem, false, find) scanDoc(docItem, false, scope)
} else if (docItem.name.endsWith(".txt", true) } else if (docItem.name.endsWith(".txt", true)
|| docItem.name.endsWith(".epub", true) || docItem.name.endsWith(".epub", true)
) { ) {
find(docItem) list.add(docItem)
} }
} }
if (!scope.isActive) {
finally?.invoke()
return
}
if (list.isNotEmpty()) {
dataCallback?.addItems(list)
}
}.onFailure { }.onFailure {
context.toastOnUi("扫描文件夹出错\n${it.localizedMessage}") 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()
}
} }
Loading…
Cancel
Save