优化
This commit is contained in:
@@ -233,7 +233,7 @@ class ImportBookActivity : BaseImportBookActivity<ActivityImportBookBinding, Imp
|
|||||||
binding.tvPath.text = path
|
binding.tvPath.text = path
|
||||||
adapter.selectedUris.clear()
|
adapter.selectedUris.clear()
|
||||||
adapter.clearItems()
|
adapter.clearItems()
|
||||||
viewModel.loadDoc(lastDoc.uri)
|
viewModel.loadDoc(lastDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -102,16 +102,16 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadDoc(uri: Uri) {
|
fun loadDoc(fileDoc: FileDoc) {
|
||||||
execute {
|
execute {
|
||||||
val docList = DocumentUtils.listFiles(uri) { item ->
|
val docList = fileDoc.list { item ->
|
||||||
when {
|
when {
|
||||||
item.name.startsWith(".") -> false
|
item.name.startsWith(".") -> false
|
||||||
item.isDir -> true
|
item.isDir -> true
|
||||||
else -> item.name.matches(bookFileRegex)
|
else -> item.name.matches(bookFileRegex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataCallback?.setItems(docList)
|
dataCallback?.setItems(docList!!)
|
||||||
}.onError {
|
}.onError {
|
||||||
context.toastOnUi("获取文件列表出错\n${it.localizedMessage}")
|
context.toastOnUi("获取文件列表出错\n${it.localizedMessage}")
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
|
|||||||
}
|
}
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val list = ArrayList<FileDoc>()
|
val list = ArrayList<FileDoc>()
|
||||||
DocumentUtils.listFiles(fileDoc.uri).forEach { docItem ->
|
fileDoc.list()!!.forEach { docItem ->
|
||||||
if (!scope.isActive) {
|
if (!scope.isActive) {
|
||||||
finally?.invoke()
|
finally?.invoke()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -117,16 +117,16 @@ class FontSelectDialog : BaseDialogFragment(R.layout.dialog_font_select),
|
|||||||
|
|
||||||
private fun getLocalFonts(): ArrayList<FileDoc> {
|
private fun getLocalFonts(): ArrayList<FileDoc> {
|
||||||
val path = FileUtils.getPath(requireContext().externalFiles, "font")
|
val path = FileUtils.getPath(requireContext().externalFiles, "font")
|
||||||
return DocumentUtils.listFiles(path) {
|
return File(path).listFileDocs {
|
||||||
it.name.matches(fontRegex)
|
it.name.matches(fontRegex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFontFiles(doc: DocumentFile) {
|
private fun loadFontFiles(doc: DocumentFile) {
|
||||||
execute {
|
execute {
|
||||||
val fontItems = DocumentUtils.listFiles(doc.uri) {
|
val fontItems = doc.listFileDocs {
|
||||||
it.name.matches(fontRegex)
|
it.name.matches(fontRegex)
|
||||||
}
|
}!!
|
||||||
mergeFontItems(fontItems, getLocalFonts())
|
mergeFontItems(fontItems, getLocalFonts())
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
adapter.setItems(it)
|
adapter.setItems(it)
|
||||||
@@ -147,7 +147,7 @@ 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) {
|
val fontItems = File(path).listFileDocs {
|
||||||
it.name.matches(fontRegex)
|
it.name.matches(fontRegex)
|
||||||
}
|
}
|
||||||
mergeFontItems(fontItems, getLocalFonts())
|
mergeFontItems(fontItems, getLocalFonts())
|
||||||
|
|||||||
@@ -1,238 +0,0 @@
|
|||||||
package io.legado.app.utils
|
|
||||||
|
|
||||||
import android.app.DownloadManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.database.Cursor
|
|
||||||
import android.net.Uri
|
|
||||||
import android.provider.DocumentsContract
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import io.legado.app.exception.NoStackTraceException
|
|
||||||
import splitties.init.appCtx
|
|
||||||
import splitties.systemservices.downloadManager
|
|
||||||
import java.io.File
|
|
||||||
import java.nio.charset.Charset
|
|
||||||
|
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
object DocumentUtils {
|
|
||||||
|
|
||||||
fun exists(root: DocumentFile, fileName: String, vararg subDirs: String): Boolean {
|
|
||||||
val parent = getDirDocument(root, *subDirs) ?: return false
|
|
||||||
return parent.findFile(fileName)?.exists() ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun delete(root: DocumentFile, fileName: String, vararg subDirs: String) {
|
|
||||||
val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs)
|
|
||||||
parent?.findFile(fileName)?.delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createFileIfNotExist(
|
|
||||||
root: DocumentFile,
|
|
||||||
fileName: String,
|
|
||||||
mimeType: String = "",
|
|
||||||
vararg subDirs: String
|
|
||||||
): DocumentFile? {
|
|
||||||
val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs)
|
|
||||||
return parent?.findFile(fileName) ?: parent?.createFile(mimeType, fileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createFolderIfNotExist(root: DocumentFile, vararg subDirs: String): DocumentFile? {
|
|
||||||
var parent: DocumentFile? = root
|
|
||||||
for (subDirName in subDirs) {
|
|
||||||
val subDir = parent?.findFile(subDirName)
|
|
||||||
?: parent?.createDirectory(subDirName)
|
|
||||||
parent = subDir
|
|
||||||
}
|
|
||||||
return parent
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDirDocument(root: DocumentFile, vararg subDirs: String): DocumentFile? {
|
|
||||||
var parent = root
|
|
||||||
for (subDirName in subDirs) {
|
|
||||||
val subDir = parent.findFile(subDirName)
|
|
||||||
parent = subDir ?: return null
|
|
||||||
}
|
|
||||||
return parent
|
|
||||||
}
|
|
||||||
|
|
||||||
private val projection by lazy {
|
|
||||||
arrayOf(
|
|
||||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
|
||||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
|
|
||||||
DocumentsContract.Document.COLUMN_LAST_MODIFIED,
|
|
||||||
DocumentsContract.Document.COLUMN_SIZE,
|
|
||||||
DocumentsContract.Document.COLUMN_MIME_TYPE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DocumentFile 的 listFiles() 非常的慢,所以这里直接从数据库查询
|
|
||||||
*/
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun listFiles(uri: Uri, filter: ((file: FileDoc) -> Boolean)? = null): ArrayList<FileDoc> {
|
|
||||||
if (!uri.isContentScheme()) {
|
|
||||||
return listFiles(uri.path!!, filter)
|
|
||||||
}
|
|
||||||
val childrenUri = DocumentsContract
|
|
||||||
.buildChildDocumentsUriUsingTree(uri, DocumentsContract.getDocumentId(uri))
|
|
||||||
val docList = arrayListOf<FileDoc>()
|
|
||||||
var cursor: Cursor? = null
|
|
||||||
try {
|
|
||||||
cursor = appCtx.contentResolver.query(
|
|
||||||
childrenUri, projection, null, null, DocumentsContract.Document.COLUMN_DISPLAY_NAME
|
|
||||||
)
|
|
||||||
cursor?.let {
|
|
||||||
val ici = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
|
|
||||||
val nci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME)
|
|
||||||
val sci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_SIZE)
|
|
||||||
val mci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_MIME_TYPE)
|
|
||||||
val dci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED)
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
do {
|
|
||||||
val item = FileDoc(
|
|
||||||
name = cursor.getString(nci),
|
|
||||||
isDir = cursor.getString(mci) == DocumentsContract.Document.MIME_TYPE_DIR,
|
|
||||||
size = cursor.getLong(sci),
|
|
||||||
lastModified = cursor.getLong(dci),
|
|
||||||
uri = DocumentsContract
|
|
||||||
.buildDocumentUriUsingTree(uri, cursor.getString(ici))
|
|
||||||
)
|
|
||||||
if (filter == null || filter.invoke(item)) {
|
|
||||||
docList.add(item)
|
|
||||||
}
|
|
||||||
} while (cursor.moveToNext())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
cursor?.close()
|
|
||||||
}
|
|
||||||
return docList
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun listFiles(path: String, filter: ((file: FileDoc) -> Boolean)? = null): ArrayList<FileDoc> {
|
|
||||||
val docList = arrayListOf<FileDoc>()
|
|
||||||
val file = File(path)
|
|
||||||
file.listFiles()?.forEach {
|
|
||||||
val item = FileDoc(
|
|
||||||
it.name,
|
|
||||||
it.isDirectory,
|
|
||||||
it.length(),
|
|
||||||
it.lastModified(),
|
|
||||||
Uri.fromFile(it)
|
|
||||||
)
|
|
||||||
if (filter == null || filter.invoke(item)) {
|
|
||||||
docList.add(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return docList
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
data class FileDoc(
|
|
||||||
val name: String,
|
|
||||||
val isDir: Boolean,
|
|
||||||
val size: Long,
|
|
||||||
val lastModified: Long,
|
|
||||||
val uri: Uri
|
|
||||||
) {
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return if (uri.isContentScheme()) uri.toString() else uri.path!!
|
|
||||||
}
|
|
||||||
|
|
||||||
val isContentScheme get() = uri.isContentScheme()
|
|
||||||
|
|
||||||
fun readBytes(): ByteArray {
|
|
||||||
return uri.readBytes(appCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
fun fromUri(uri: Uri, isDir: Boolean): FileDoc {
|
|
||||||
if (uri.isContentScheme()) {
|
|
||||||
val doc = if (isDir) {
|
|
||||||
DocumentFile.fromTreeUri(appCtx, uri)!!
|
|
||||||
} else if (uri.host == "downloads") {
|
|
||||||
val query = DownloadManager.Query()
|
|
||||||
query.setFilterById(uri.lastPathSegment!!.toLong())
|
|
||||||
downloadManager.query(query).use {
|
|
||||||
if (it.moveToFirst()) {
|
|
||||||
val lUriColum = it.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
|
|
||||||
val lUri = it.getString(lUriColum)
|
|
||||||
DocumentFile.fromSingleUri(appCtx, Uri.parse(lUri))!!
|
|
||||||
} else {
|
|
||||||
DocumentFile.fromSingleUri(appCtx, uri)!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DocumentFile.fromSingleUri(appCtx, uri)!!
|
|
||||||
}
|
|
||||||
return FileDoc(doc.name ?: "", true, doc.length(), doc.lastModified(), doc.uri)
|
|
||||||
}
|
|
||||||
val file = File(uri.path!!)
|
|
||||||
return FileDoc(file.name, isDir, file.length(), file.lastModified(), uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fromDocumentFile(doc: DocumentFile): FileDoc {
|
|
||||||
return FileDoc(
|
|
||||||
name = doc.name ?: "",
|
|
||||||
isDir = doc.isDirectory,
|
|
||||||
size = doc.length(),
|
|
||||||
lastModified = doc.lastModified(),
|
|
||||||
uri = doc.uri
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fromFile(file: File): FileDoc {
|
|
||||||
return FileDoc(
|
|
||||||
name = file.name,
|
|
||||||
isDir = file.isDirectory,
|
|
||||||
size = file.length(),
|
|
||||||
lastModified = file.lastModified(),
|
|
||||||
uri = Uri.fromFile(file)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun FileDoc.list(filter: ((file: FileDoc) -> Boolean)? = null): ArrayList<FileDoc>? {
|
|
||||||
if (isDir) {
|
|
||||||
return DocumentUtils.listFiles(uri, filter)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun FileDoc.find(name: String): FileDoc? {
|
|
||||||
return list {
|
|
||||||
it.name == name
|
|
||||||
}?.firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun DocumentFile.writeText(context: Context, data: String, charset: Charset = Charsets.UTF_8) {
|
|
||||||
uri.writeText(context, data, charset)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun DocumentFile.writeBytes(context: Context, data: ByteArray) {
|
|
||||||
uri.writeBytes(context, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun DocumentFile.readText(context: Context): String {
|
|
||||||
return String(readBytes(context))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun DocumentFile.readBytes(context: Context): ByteArray {
|
|
||||||
return context.contentResolver.openInputStream(uri)?.let {
|
|
||||||
val len: Int = it.available()
|
|
||||||
val buffer = ByteArray(len)
|
|
||||||
it.read(buffer)
|
|
||||||
it.close()
|
|
||||||
return buffer
|
|
||||||
} ?: throw NoStackTraceException("打开文件失败\n${uri}")
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package io.legado.app.utils
|
||||||
|
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
object DocumentUtils {
|
||||||
|
|
||||||
|
fun exists(root: DocumentFile, fileName: String, vararg subDirs: String): Boolean {
|
||||||
|
val parent = getDirDocument(root, *subDirs) ?: return false
|
||||||
|
return parent.findFile(fileName)?.exists() ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun delete(root: DocumentFile, fileName: String, vararg subDirs: String) {
|
||||||
|
val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs)
|
||||||
|
parent?.findFile(fileName)?.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFileIfNotExist(
|
||||||
|
root: DocumentFile,
|
||||||
|
fileName: String,
|
||||||
|
mimeType: String = "",
|
||||||
|
vararg subDirs: String
|
||||||
|
): DocumentFile? {
|
||||||
|
val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs)
|
||||||
|
return parent?.findFile(fileName) ?: parent?.createFile(mimeType, fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFolderIfNotExist(root: DocumentFile, vararg subDirs: String): DocumentFile? {
|
||||||
|
var parent: DocumentFile? = root
|
||||||
|
for (subDirName in subDirs) {
|
||||||
|
val subDir = parent?.findFile(subDirName)
|
||||||
|
?: parent?.createDirectory(subDirName)
|
||||||
|
parent = subDir
|
||||||
|
}
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDirDocument(root: DocumentFile, vararg subDirs: String): DocumentFile? {
|
||||||
|
var parent = root
|
||||||
|
for (subDirName in subDirs) {
|
||||||
|
val subDir = parent.findFile(subDirName)
|
||||||
|
parent = subDir ?: return null
|
||||||
|
}
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
|
||||||
|
val projection by lazy {
|
||||||
|
arrayOf(
|
||||||
|
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
||||||
|
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
|
||||||
|
DocumentsContract.Document.COLUMN_LAST_MODIFIED,
|
||||||
|
DocumentsContract.Document.COLUMN_SIZE,
|
||||||
|
DocumentsContract.Document.COLUMN_MIME_TYPE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
package io.legado.app.utils
|
||||||
|
|
||||||
|
import android.app.DownloadManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import io.legado.app.exception.NoStackTraceException
|
||||||
|
import splitties.init.appCtx
|
||||||
|
import splitties.systemservices.downloadManager
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
|
||||||
|
data class FileDoc(
|
||||||
|
val name: String,
|
||||||
|
val isDir: Boolean,
|
||||||
|
val size: Long,
|
||||||
|
val lastModified: Long,
|
||||||
|
val uri: Uri
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return if (uri.isContentScheme()) uri.toString() else uri.path!!
|
||||||
|
}
|
||||||
|
|
||||||
|
val isContentScheme get() = uri.isContentScheme()
|
||||||
|
|
||||||
|
fun readBytes(): ByteArray {
|
||||||
|
return uri.readBytes(appCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun fromUri(uri: Uri, isDir: Boolean): FileDoc {
|
||||||
|
if (uri.isContentScheme()) {
|
||||||
|
val doc = if (isDir) {
|
||||||
|
DocumentFile.fromTreeUri(appCtx, uri)!!
|
||||||
|
} else if (uri.host == "downloads") {
|
||||||
|
val query = DownloadManager.Query()
|
||||||
|
query.setFilterById(uri.lastPathSegment!!.toLong())
|
||||||
|
downloadManager.query(query).use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
val lUriColum = it.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
|
||||||
|
val lUri = it.getString(lUriColum)
|
||||||
|
DocumentFile.fromSingleUri(appCtx, Uri.parse(lUri))!!
|
||||||
|
} else {
|
||||||
|
DocumentFile.fromSingleUri(appCtx, uri)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DocumentFile.fromSingleUri(appCtx, uri)!!
|
||||||
|
}
|
||||||
|
return FileDoc(doc.name ?: "", true, doc.length(), doc.lastModified(), doc.uri)
|
||||||
|
}
|
||||||
|
val file = File(uri.path!!)
|
||||||
|
return FileDoc(file.name, isDir, file.length(), file.lastModified(), uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromDocumentFile(doc: DocumentFile): FileDoc {
|
||||||
|
return FileDoc(
|
||||||
|
name = doc.name ?: "",
|
||||||
|
isDir = doc.isDirectory,
|
||||||
|
size = doc.length(),
|
||||||
|
lastModified = doc.lastModified(),
|
||||||
|
uri = doc.uri
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromFile(file: File): FileDoc {
|
||||||
|
return FileDoc(
|
||||||
|
name = file.name,
|
||||||
|
isDir = file.isDirectory,
|
||||||
|
size = file.length(),
|
||||||
|
lastModified = file.lastModified(),
|
||||||
|
uri = Uri.fromFile(file)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤器
|
||||||
|
*/
|
||||||
|
typealias FileDocFilter = (file: FileDoc) -> Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回子文件列表,如果不是文件夹则返回null
|
||||||
|
*/
|
||||||
|
fun FileDoc.list(filter: FileDocFilter? = null): ArrayList<FileDoc>? {
|
||||||
|
if (isDir) {
|
||||||
|
if (uri.isContentScheme()) {
|
||||||
|
/**
|
||||||
|
* DocumentFile 的 listFiles() 非常的慢,所以这里直接从数据库查询
|
||||||
|
*/
|
||||||
|
val childrenUri = DocumentsContract
|
||||||
|
.buildChildDocumentsUriUsingTree(uri, DocumentsContract.getDocumentId(uri))
|
||||||
|
val docList = arrayListOf<FileDoc>()
|
||||||
|
var cursor: Cursor? = null
|
||||||
|
try {
|
||||||
|
cursor = appCtx.contentResolver.query(
|
||||||
|
childrenUri,
|
||||||
|
DocumentUtils.projection,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
DocumentsContract.Document.COLUMN_DISPLAY_NAME
|
||||||
|
)
|
||||||
|
cursor?.let {
|
||||||
|
val ici = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
|
||||||
|
val nci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME)
|
||||||
|
val sci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_SIZE)
|
||||||
|
val mci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_MIME_TYPE)
|
||||||
|
val dci = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED)
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
val item = FileDoc(
|
||||||
|
name = cursor.getString(nci),
|
||||||
|
isDir = cursor.getString(mci) == DocumentsContract.Document.MIME_TYPE_DIR,
|
||||||
|
size = cursor.getLong(sci),
|
||||||
|
lastModified = cursor.getLong(dci),
|
||||||
|
uri = DocumentsContract.buildDocumentUriUsingTree(
|
||||||
|
uri,
|
||||||
|
cursor.getString(ici)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (filter == null || filter.invoke(item)) {
|
||||||
|
docList.add(item)
|
||||||
|
}
|
||||||
|
} while (cursor.moveToNext())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor?.close()
|
||||||
|
}
|
||||||
|
return docList
|
||||||
|
} else {
|
||||||
|
return File(uri.path!!).listFileDocs(filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FileDoc.find(name: String): FileDoc? {
|
||||||
|
return list {
|
||||||
|
it.name == name
|
||||||
|
}?.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DocumentFile 的 listFiles() 非常的慢,尽量不要使用
|
||||||
|
*/
|
||||||
|
fun DocumentFile.listFileDocs(filter: FileDocFilter? = null): ArrayList<FileDoc>? {
|
||||||
|
return FileDoc.fromDocumentFile(this).list(filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun DocumentFile.writeText(context: Context, data: String, charset: Charset = Charsets.UTF_8) {
|
||||||
|
uri.writeText(context, data, charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun DocumentFile.writeBytes(context: Context, data: ByteArray) {
|
||||||
|
uri.writeBytes(context, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun DocumentFile.readText(context: Context): String {
|
||||||
|
return String(readBytes(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun DocumentFile.readBytes(context: Context): ByteArray {
|
||||||
|
return context.contentResolver.openInputStream(uri)?.let {
|
||||||
|
val len: Int = it.available()
|
||||||
|
val buffer = ByteArray(len)
|
||||||
|
it.read(buffer)
|
||||||
|
it.close()
|
||||||
|
return buffer
|
||||||
|
} ?: throw NoStackTraceException("打开文件失败\n${uri}")
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.legado.app.utils
|
package io.legado.app.utils
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
fun File.getFile(vararg subDirFiles: String): File {
|
fun File.getFile(vararg subDirFiles: String): File {
|
||||||
@@ -11,3 +12,23 @@ fun File.exists(vararg subDirFiles: String): Boolean {
|
|||||||
return getFile(*subDirFiles).exists()
|
return getFile(*subDirFiles).exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun File.listFileDocs(filter: FileDocFilter? = null): ArrayList<FileDoc> {
|
||||||
|
val docList = arrayListOf<FileDoc>()
|
||||||
|
listFiles()
|
||||||
|
listFiles()?.forEach {
|
||||||
|
val item = FileDoc(
|
||||||
|
it.name,
|
||||||
|
it.isDirectory,
|
||||||
|
it.length(),
|
||||||
|
it.lastModified(),
|
||||||
|
Uri.fromFile(it)
|
||||||
|
)
|
||||||
|
if (filter == null || filter.invoke(item)) {
|
||||||
|
docList.add(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return docList
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.os.Environment
|
|||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import androidx.annotation.IntDef
|
import androidx.annotation.IntDef
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
|
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@@ -115,7 +114,7 @@ object FileUtils {
|
|||||||
const val BY_EXTENSION_DESC = 7
|
const val BY_EXTENSION_DESC = 7
|
||||||
|
|
||||||
@IntDef(value = [BY_NAME_ASC, BY_NAME_DESC, BY_TIME_ASC, BY_TIME_DESC, BY_SIZE_ASC, BY_SIZE_DESC, BY_EXTENSION_ASC, BY_EXTENSION_DESC])
|
@IntDef(value = [BY_NAME_ASC, BY_NAME_DESC, BY_TIME_ASC, BY_TIME_DESC, BY_SIZE_ASC, BY_SIZE_DESC, BY_EXTENSION_ASC, BY_EXTENSION_DESC])
|
||||||
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
annotation class SortType
|
annotation class SortType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user