pull/1403/head
gedoor 3 years ago
parent 548df96f8a
commit b05b9a9973
  1. 105
      app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt
  2. 4
      app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt
  3. 36
      app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt
  4. 24
      app/src/main/java/io/legado/app/utils/DocumentExtensions.kt

@ -42,24 +42,21 @@ 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 val bookFileRegex = Regex("(?i).*\\.(txt|epub|umd)")
private var rootDoc: DocumentFile? = null private var rootDoc: FileDoc? = null
private val subDocs = arrayListOf<DocumentFile>() private val subDocs = arrayListOf<FileDoc>()
private val adapter by lazy { ImportBookAdapter(this, this) } private val adapter by lazy { ImportBookAdapter(this, this) }
private val sdPath = FileUtils.getSdCardPath()
private var path = sdPath
private val selectFolder = registerForActivityResult(HandleFileContract()) { private val selectFolder = registerForActivityResult(HandleFileContract()) {
it.uri?.let { uri -> it.uri?.let { uri ->
if (uri.isContentScheme()) { if (uri.isContentScheme()) {
AppConfig.importBookPath = uri.toString() AppConfig.importBookPath = uri.toString()
initRootDoc() initRootDoc()
} else { } else {
uri.path?.let { path -> AppConfig.importBookPath = uri.path
AppConfig.importBookPath = path
initRootDoc() initRootDoc()
} }
} }
} }
}
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
initView() initView()
@ -140,45 +137,56 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
} }
lastPath.isContentScheme() -> { lastPath.isContentScheme() -> {
val rootUri = Uri.parse(lastPath) val rootUri = Uri.parse(lastPath)
rootDoc = DocumentFile.fromTreeUri(this, rootUri) kotlin.runCatching {
if (rootDoc == null) { DocumentFile.fromTreeUri(this, rootUri)?.let {
subDocs.clear()
rootDoc = FileDoc.fromDocumentFile(it)
upDocs(rootDoc!!)
} ?: let {
binding.tvEmptyMsg.visible()
selectFolder.launch(null)
}
}.onFailure {
binding.tvEmptyMsg.visible() binding.tvEmptyMsg.visible()
selectFolder.launch(null) selectFolder.launch(null)
} else {
subDocs.clear()
upPath()
} }
} }
Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> { Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> {
binding.tvEmptyMsg.visible() binding.tvEmptyMsg.visible()
selectFolder.launch(null) selectFolder.launch(null)
} }
else -> { else -> initRootPath(lastPath)
}
}
private fun initRootPath(path: String) {
binding.tvEmptyMsg.visible() binding.tvEmptyMsg.visible()
PermissionsCompat.Builder(this) PermissionsCompat.Builder(this)
.addPermissions(*Permissions.Group.STORAGE) .addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage) .rationale(R.string.tip_perm_request_storage)
.onGranted { .onGranted {
rootDoc = null kotlin.runCatching {
rootDoc = FileDoc.fromFile(File(path))
subDocs.clear() subDocs.clear()
path = lastPath
upPath() upPath()
} }.onFailure {
.request() binding.tvEmptyMsg.visible()
selectFolder.launch(null)
} }
} }
.request()
} }
@Synchronized @Synchronized
private fun upPath() { private fun upPath() {
rootDoc?.let { rootDoc?.let {
upDocs(it) upDocs(it)
} ?: upFiles() }
} }
private fun upDocs(rootDoc: DocumentFile) { private fun upDocs(rootDoc: FileDoc) {
binding.tvEmptyMsg.gone() binding.tvEmptyMsg.gone()
var path = rootDoc.name.toString() + File.separator var path = rootDoc.name + File.separator
var lastDoc = rootDoc var lastDoc = rootDoc
for (doc in subDocs) { for (doc in subDocs) {
lastDoc = doc lastDoc = doc
@ -206,29 +214,6 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
} }
} }
private fun upFiles() {
binding.tvEmptyMsg.gone()
binding.tvPath.text = path.replace(sdPath, "SD")
adapter.clearItems()
launch(IO) {
kotlin.runCatching {
val docList = DocumentUtils.listFiles(path) {
when {
it.name.startsWith(".") -> false
it.isDir -> true
else -> it.name.matches(bookFileRegex)
}
}
docList.sortWith(compareBy({ !it.isDir }, { it.name }))
withContext(Main) {
adapter.setItems(docList)
}
}.onFailure {
toastOnUi("获取文件列表出错\n${it.localizedMessage}")
}
}
}
/** /**
* 扫描当前文件夹 * 扫描当前文件夹
*/ */
@ -244,22 +229,6 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
} }
} }
} }
} ?: let {
val lastPath = AppConfig.importBookPath
if (lastPath.isNullOrEmpty()) {
toastOnUi(R.string.empty_msg_import_book)
} else {
adapter.clearItems()
val file = File(path)
binding.refreshProgressBar.isAutoLoading = true
launch(IO) {
viewModel.scanFile(file, true, find) {
launch {
binding.refreshProgressBar.isAutoLoading = false
}
}
}
}
} }
} }
@ -285,27 +254,13 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
} }
@Synchronized @Synchronized
override fun nextDoc(uri: Uri) { override fun nextDoc(fileDoc: FileDoc) {
if (uri.toString().isContentScheme()) { subDocs.add(fileDoc)
subDocs.add(DocumentFile.fromSingleUri(this, uri)!!)
} else {
path = uri.path.toString()
}
upPath() upPath()
} }
@Synchronized @Synchronized
private fun goBackDir(): Boolean { private fun goBackDir(): Boolean {
if (rootDoc == null) {
if (path != sdPath) {
File(path).parent?.let {
path = it
upPath()
return true
}
}
return false
}
return if (subDocs.isNotEmpty()) { return if (subDocs.isNotEmpty()) {
subDocs.removeAt(subDocs.lastIndex) subDocs.removeAt(subDocs.lastIndex)
upPath() upPath()

@ -66,7 +66,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
holder.itemView.setOnClickListener { holder.itemView.setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
if (it.isDir) { if (it.isDir) {
callBack.nextDoc(it.uri) callBack.nextDoc(it)
} else if (!bookFileNames.contains(it.name)) { } else if (!bookFileNames.contains(it.name)) {
if (!selectedUris.contains(it.toString())) { if (!selectedUris.contains(it.toString())) {
selectedUris.add(it.toString()) selectedUris.add(it.toString())
@ -138,7 +138,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
} }
interface CallBack { interface CallBack {
fun nextDoc(uri: Uri) fun nextDoc(fileDoc: FileDoc)
fun upCountView() fun upCountView()
} }

@ -43,17 +43,15 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
} }
fun scanDoc( fun scanDoc(
documentFile: DocumentFile, fileDoc: FileDoc,
isRoot: Boolean, isRoot: Boolean,
find: (docItem: FileDoc) -> Unit, find: (docItem: FileDoc) -> Unit,
finally: (() -> Unit)? = null finally: (() -> Unit)? = null
) { ) {
kotlin.runCatching { kotlin.runCatching {
DocumentUtils.listFiles(documentFile.uri).forEach { docItem -> DocumentUtils.listFiles(fileDoc.uri).forEach { docItem ->
if (docItem.isDir) { if (docItem.isDir) {
DocumentFile.fromSingleUri(context, docItem.uri)?.let { scanDoc(docItem, false, find)
scanDoc(it, false, find)
}
} else if (docItem.name.endsWith(".txt", true) } else if (docItem.name.endsWith(".txt", true)
|| docItem.name.endsWith(".epub", true) || docItem.name.endsWith(".epub", true)
) { ) {
@ -68,32 +66,4 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
} }
} }
fun scanFile(
file: File,
isRoot: Boolean,
find: (docItem: FileDoc) -> Unit,
finally: (() -> Unit)? = null
) {
file.listFiles()?.forEach {
if (it.isDirectory) {
scanFile(it, false, find)
} else if (it.name.endsWith(".txt", true)
|| it.name.endsWith(".epub", true)
) {
find(
FileDoc(
it.name,
it.isDirectory,
it.length(),
Date(it.lastModified()),
Uri.parse(it.absolutePath)
)
)
}
}
if (isRoot) {
finally?.invoke()
}
}
} }

@ -179,6 +179,30 @@ data class FileDoc(
fun readBytes(): ByteArray { fun readBytes(): ByteArray {
return uri.readBytes(appCtx) return uri.readBytes(appCtx)
} }
companion object {
fun fromDocumentFile(doc: DocumentFile): FileDoc {
return FileDoc(
name = doc.name ?: "",
isDir = doc.isDirectory,
size = doc.length(),
date = Date(doc.lastModified()),
uri = doc.uri
)
}
fun fromFile(file: File): FileDoc {
return FileDoc(
name = file.name,
isDir = file.isDirectory,
size = file.length(),
date = Date(file.lastModified()),
uri = Uri.fromFile(file)
)
}
}
} }
@Throws(Exception::class) @Throws(Exception::class)

Loading…
Cancel
Save