pull/1415/head
gedoor 3 years ago
parent 92ec2d1057
commit d13a405d6c
  1. 66
      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. 13
      app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt
  4. 8
      app/src/main/java/io/legado/app/ui/widget/font/FontAdapter.kt
  5. 37
      app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt
  6. 45
      app/src/main/java/io/legado/app/utils/DocumentExtensions.kt

@ -4,7 +4,6 @@ import android.annotation.SuppressLint
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.DocumentsContract
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.viewModels import androidx.activity.viewModels
@ -31,7 +30,6 @@ import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.util.*
/** /**
* 导入本地书籍界面 * 导入本地书籍界面
@ -43,7 +41,7 @@ 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: DocumentFile? = null private var rootDoc: DocumentFile? = null
private val subDocs = arrayListOf<DocumentFile>() private val subDocs = arrayListOf<DocumentFile>()
private val adapter by lazy { ImportBookAdapter(this, this) } private val adapter by lazy { ImportBookAdapter(this, this) }
@ -191,17 +189,11 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
adapter.clearItems() adapter.clearItems()
launch(IO) { launch(IO) {
runCatching { runCatching {
val docList = DocumentUtils.listFiles(lastDoc.uri) val docList = DocumentUtils.listFiles(lastDoc.uri) { item ->
for (i in docList.lastIndex downTo 0) { when {
val item = docList[i] item.name.startsWith(".") -> false
if (item.name.startsWith(".")) { item.isDir -> true
docList.removeAt(i) else -> item.name.matches(bookFileRegex)
} else if (!item.isDir
&& !item.name.endsWith(".txt", true)
&& !item.name.endsWith(".epub", true)
&& !item.name.endsWith(".umd", true)
) {
docList.removeAt(i)
} }
} }
docList.sortWith(compareBy({ !it.isDir }, { it.name })) docList.sortWith(compareBy({ !it.isDir }, { it.name }))
@ -217,36 +209,24 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
private fun upFiles() { private fun upFiles() {
binding.tvEmptyMsg.gone() binding.tvEmptyMsg.gone()
binding.tvPath.text = path.replace(sdPath, "SD") binding.tvPath.text = path.replace(sdPath, "SD")
val docList = arrayListOf<DocItem>() adapter.clearItems()
File(path).listFiles()?.forEach { launch(IO) {
if (it.isDirectory) { kotlin.runCatching {
if (!it.name.startsWith(".")) val docList = DocumentUtils.listFiles(path) {
docList.add( when {
DocItem( it.name.startsWith(".") -> false
it.name, it.isDirectory -> true
DocumentsContract.Document.MIME_TYPE_DIR, else -> it.name.matches(bookFileRegex)
it.length(), }
Date(it.lastModified()), }
Uri.fromFile(it) docList.sortWith(compareBy({ !it.isDir }, { it.name }))
) withContext(Main) {
) adapter.setItems(docList)
} else if (it.name.endsWith(".txt", true) }
|| it.name.endsWith(".epub", true) }.onFailure {
|| it.name.endsWith(".umd", true) toastOnUi("获取文件列表出错\n${it.localizedMessage}")
) {
docList.add(
DocItem(
it.name,
it.extension,
it.length(),
Date(it.lastModified()),
Uri.fromFile(it)
)
)
} }
} }
docList.sortWith(compareBy({ !it.isDir }, { it.name }))
adapter.setItems(docList)
} }
/** /**
@ -298,7 +278,7 @@ class ImportBookActivity : VMBaseActivity<ActivityImportBookBinding, ImportBookV
} }
} }
private val find: (docItem: DocItem) -> Unit = { private val find: (docItem: FileDoc) -> Unit = {
launch { launch {
adapter.addItem(it) adapter.addItem(it)
} }

@ -13,7 +13,7 @@ import io.legado.app.utils.*
class ImportBookAdapter(context: Context, val callBack: CallBack) : class ImportBookAdapter(context: Context, val callBack: CallBack) :
RecyclerAdapter<DocItem, ItemImportBookBinding>(context) { RecyclerAdapter<FileDoc, ItemImportBookBinding>(context) {
var selectedUris = hashSetOf<String>() var selectedUris = hashSetOf<String>()
var checkableCount = 0 var checkableCount = 0
private var bookFileNames = arrayListOf<String>() private var bookFileNames = arrayListOf<String>()
@ -29,7 +29,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
override fun convert( override fun convert(
holder: ItemViewHolder, holder: ItemViewHolder,
binding: ItemImportBookBinding, binding: ItemImportBookBinding,
item: DocItem, item: FileDoc,
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
binding.run { binding.run {

@ -5,8 +5,8 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.model.localBook.LocalBook import io.legado.app.model.localBook.LocalBook
import io.legado.app.utils.DocItem
import io.legado.app.utils.DocumentUtils import io.legado.app.utils.DocumentUtils
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 java.io.File import java.io.File
@ -45,12 +45,11 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
fun scanDoc( fun scanDoc(
documentFile: DocumentFile, documentFile: DocumentFile,
isRoot: Boolean, isRoot: Boolean,
find: (docItem: DocItem) -> Unit, find: (docItem: FileDoc) -> Unit,
finally: (() -> Unit)? = null finally: (() -> Unit)? = null
) { ) {
kotlin.runCatching { kotlin.runCatching {
val docList = DocumentUtils.listFiles(documentFile.uri) DocumentUtils.listFiles(documentFile.uri).forEach { docItem ->
docList.forEach { docItem ->
if (docItem.isDir) { if (docItem.isDir) {
DocumentFile.fromSingleUri(context, docItem.uri)?.let { DocumentFile.fromSingleUri(context, docItem.uri)?.let {
scanDoc(it, false, find) scanDoc(it, false, find)
@ -72,7 +71,7 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
fun scanFile( fun scanFile(
file: File, file: File,
isRoot: Boolean, isRoot: Boolean,
find: (docItem: DocItem) -> Unit, find: (docItem: FileDoc) -> Unit,
finally: (() -> Unit)? = null finally: (() -> Unit)? = null
) { ) {
file.listFiles()?.forEach { file.listFiles()?.forEach {
@ -82,9 +81,9 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
|| it.name.endsWith(".epub", true) || it.name.endsWith(".epub", true)
) { ) {
find( find(
DocItem( FileDoc(
it.name, it.name,
it.extension, it.isDirectory,
it.length(), it.length(),
Date(it.lastModified()), Date(it.lastModified()),
Uri.parse(it.absolutePath) Uri.parse(it.absolutePath)

@ -13,7 +13,7 @@ import java.io.File
import java.net.URLDecoder import java.net.URLDecoder
class FontAdapter(context: Context, curFilePath: String, val callBack: CallBack) : class FontAdapter(context: Context, curFilePath: String, val callBack: CallBack) :
RecyclerAdapter<DocItem, ItemFontBinding>(context) { RecyclerAdapter<FileDoc, ItemFontBinding>(context) {
private val curName = URLDecoder.decode(curFilePath, "utf-8") private val curName = URLDecoder.decode(curFilePath, "utf-8")
.substringAfterLast(File.separator) .substringAfterLast(File.separator)
@ -25,12 +25,12 @@ class FontAdapter(context: Context, curFilePath: String, val callBack: CallBack)
override fun convert( override fun convert(
holder: ItemViewHolder, holder: ItemViewHolder,
binding: ItemFontBinding, binding: ItemFontBinding,
item: DocItem, item: FileDoc,
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
binding.run { binding.run {
kotlin.runCatching { kotlin.runCatching {
val typeface: Typeface? = if (item.isContentPath) { val typeface: Typeface? = if (item.isContentScheme) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.contentResolver context.contentResolver
.openFileDescriptor(item.uri, "r") .openFileDescriptor(item.uri, "r")
@ -67,6 +67,6 @@ class FontAdapter(context: Context, curFilePath: String, val callBack: CallBack)
} }
interface CallBack { interface CallBack {
fun onClick(docItem: DocItem) fun onClick(docItem: FileDoc)
} }
} }

@ -126,29 +126,18 @@ class FontSelectDialog : BaseDialogFragment(R.layout.dialog_font_select),
} }
} }
private fun getLocalFonts(): ArrayList<DocItem> { private fun getLocalFonts(): ArrayList<FileDoc> {
val fontItems = arrayListOf<DocItem>() val path = FileUtils.getPath(requireContext().externalFiles, "font")
val fontDir = return DocumentUtils.listFiles(path) {
FileUtils.createFolderIfNotExist(requireContext().externalFiles, "font") it.name.matches(fontRegex)
fontDir.listFiles { pathName ->
pathName.name.lowercase(Locale.getDefault()).matches(fontRegex)
}?.forEach {
fontItems.add(
DocItem(
it.name,
it.extension,
it.length(),
Date(it.lastModified()),
Uri.parse(it.absolutePath)
)
)
} }
return fontItems
} }
private fun loadFontFiles(doc: DocumentFile) { private fun loadFontFiles(doc: DocumentFile) {
execute { execute {
val fontItems = DocumentUtils.listFiles(doc.uri, fontRegex) val fontItems = DocumentUtils.listFiles(doc.uri) {
it.name.matches(fontRegex)
}
mergeFontItems(fontItems, getLocalFonts()) mergeFontItems(fontItems, getLocalFonts())
}.onSuccess { }.onSuccess {
adapter.setItems(it) adapter.setItems(it)
@ -169,7 +158,9 @@ class FontSelectDialog : BaseDialogFragment(R.layout.dialog_font_select),
private fun loadFontFiles(path: String) { private fun loadFontFiles(path: String) {
execute { execute {
val fontItems = DocumentUtils.listFiles(path, fontRegex) val fontItems = DocumentUtils.listFiles(path) {
it.name.matches(fontRegex)
}
mergeFontItems(fontItems, getLocalFonts()) mergeFontItems(fontItems, getLocalFonts())
}.onSuccess { }.onSuccess {
adapter.setItems(it) adapter.setItems(it)
@ -179,9 +170,9 @@ class FontSelectDialog : BaseDialogFragment(R.layout.dialog_font_select),
} }
private fun mergeFontItems( private fun mergeFontItems(
items1: ArrayList<DocItem>, items1: ArrayList<FileDoc>,
items2: ArrayList<DocItem> items2: ArrayList<FileDoc>
): List<DocItem> { ): List<FileDoc> {
val items = ArrayList(items1) val items = ArrayList(items1)
items2.forEach { item2 -> items2.forEach { item2 ->
var isInFirst = false var isInFirst = false
@ -200,7 +191,7 @@ class FontSelectDialog : BaseDialogFragment(R.layout.dialog_font_select),
} }
} }
override fun onClick(docItem: DocItem) { override fun onClick(docItem: FileDoc) {
execute { execute {
FileUtils.deleteFile(fontFolder.absolutePath) FileUtils.deleteFile(fontFolder.absolutePath)
callBack?.selectFont(docItem.uri.toString()) callBack?.selectFont(docItem.uri.toString())

@ -95,8 +95,8 @@ object DocumentUtils {
} }
@Throws(Exception::class) @Throws(Exception::class)
fun listFiles(uri: Uri, regex: Regex? = null): ArrayList<DocItem> { fun listFiles(uri: Uri, filter: ((file: FileDoc) -> Boolean)? = null): ArrayList<FileDoc> {
val docList = arrayListOf<DocItem>() val docList = arrayListOf<FileDoc>()
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
val childrenUri = DocumentsContract val childrenUri = DocumentsContract
@ -118,16 +118,15 @@ object DocumentUtils {
val dci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED) val dci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED)
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
val name = cursor.getString(nci) val item = FileDoc(
if (regex == null || regex.matches(name)) { name = cursor.getString(nci),
val item = DocItem( isDir = cursor.getString(mci) == DocumentsContract.Document.MIME_TYPE_DIR,
name = name, size = cursor.getLong(sci),
attr = cursor.getString(mci), date = Date(cursor.getLong(dci)),
size = cursor.getLong(sci), uri = DocumentsContract
date = Date(cursor.getLong(dci)), .buildDocumentUriUsingTree(uri, cursor.getString(ici))
uri = DocumentsContract )
.buildDocumentUriUsingTree(uri, cursor.getString(ici)) if (filter == null || filter.invoke(item)) {
)
docList.add(item) docList.add(item)
} }
} while (cursor.moveToNext()) } while (cursor.moveToNext())
@ -140,18 +139,16 @@ object DocumentUtils {
} }
@Throws(Exception::class) @Throws(Exception::class)
fun listFiles(path: String, regex: Regex? = null): ArrayList<DocItem> { fun listFiles(path: String, filter: ((file: File) -> Boolean)? = null): ArrayList<FileDoc> {
val docItems = arrayListOf<DocItem>() val docItems = arrayListOf<FileDoc>()
val file = File(path) val file = File(path)
file.listFiles { pathName -> file.listFiles { pathName ->
regex?.let { filter?.invoke(pathName) ?: true
pathName.name.matches(it)
} ?: true
}?.forEach { }?.forEach {
docItems.add( docItems.add(
DocItem( FileDoc(
it.name, it.name,
it.extension, it.isDirectory,
it.length(), it.length(),
Date(it.lastModified()), Date(it.lastModified()),
Uri.parse(it.absolutePath) Uri.parse(it.absolutePath)
@ -163,18 +160,14 @@ object DocumentUtils {
} }
data class DocItem( data class FileDoc(
val name: String, val name: String,
val attr: String, val isDir: Boolean,
val size: Long, val size: Long,
val date: Date, val date: Date,
val uri: Uri val uri: Uri
) { ) {
val isDir: Boolean by lazy { val isContentScheme get() = uri.isContentScheme()
DocumentsContract.Document.MIME_TYPE_DIR == attr
}
val isContentPath get() = uri.isContentScheme()
} }
@Throws(Exception::class) @Throws(Exception::class)

Loading…
Cancel
Save