kunfei 4 anni fa
parent
commit
25d5935123

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/import/local/ImportBookActivity.kt

@@ -233,7 +233,7 @@ class ImportBookActivity : BaseImportBookActivity<ActivityImportBookBinding, Imp
         binding.tvPath.text = path
         adapter.selectedUris.clear()
         adapter.clearItems()
-        viewModel.loadDoc(lastDoc.uri)
+        viewModel.loadDoc(lastDoc)
     }
 
     /**

+ 4 - 4
app/src/main/java/io/legado/app/ui/book/import/local/ImportBookViewModel.kt

@@ -102,16 +102,16 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
         }
     }
 
-    fun loadDoc(uri: Uri) {
+    fun loadDoc(fileDoc: FileDoc) {
         execute {
-            val docList = DocumentUtils.listFiles(uri) { item ->
+            val docList = fileDoc.list { item ->
                 when {
                     item.name.startsWith(".") -> false
                     item.isDir -> true
                     else -> item.name.matches(bookFileRegex)
                 }
             }
-            dataCallback?.setItems(docList)
+            dataCallback?.setItems(docList!!)
         }.onError {
             context.toastOnUi("获取文件列表出错\n${it.localizedMessage}")
         }
@@ -132,7 +132,7 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
         }
         kotlin.runCatching {
             val list = ArrayList<FileDoc>()
-            DocumentUtils.listFiles(fileDoc.uri).forEach { docItem ->
+            fileDoc.list()!!.forEach { docItem ->
                 if (!scope.isActive) {
                     finally?.invoke()
                     return

+ 4 - 4
app/src/main/java/io/legado/app/ui/font/FontSelectDialog.kt

@@ -117,16 +117,16 @@ class FontSelectDialog : BaseDialogFragment(R.layout.dialog_font_select),
 
     private fun getLocalFonts(): ArrayList<FileDoc> {
         val path = FileUtils.getPath(requireContext().externalFiles, "font")
-        return DocumentUtils.listFiles(path) {
+        return File(path).listFileDocs {
             it.name.matches(fontRegex)
         }
     }
 
     private fun loadFontFiles(doc: DocumentFile) {
         execute {
-            val fontItems = DocumentUtils.listFiles(doc.uri) {
+            val fontItems = doc.listFileDocs {
                 it.name.matches(fontRegex)
-            }
+            }!!
             mergeFontItems(fontItems, getLocalFonts())
         }.onSuccess {
             adapter.setItems(it)
@@ -147,7 +147,7 @@ class FontSelectDialog : BaseDialogFragment(R.layout.dialog_font_select),
 
     private fun loadFontFiles(path: String) {
         execute {
-            val fontItems = DocumentUtils.listFiles(path) {
+            val fontItems = File(path).listFileDocs {
                 it.name.matches(fontRegex)
             }
             mergeFontItems(fontItems, getLocalFonts())

+ 0 - 238
app/src/main/java/io/legado/app/utils/DocumentExtensions.kt

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

+ 58 - 0
app/src/main/java/io/legado/app/utils/DocumentUtils.kt

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

+ 182 - 0
app/src/main/java/io/legado/app/utils/FileDocExtensions.kt

@@ -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}")
+}

+ 21 - 0
app/src/main/java/io/legado/app/utils/FileExtensions.kt

@@ -1,5 +1,6 @@
 package io.legado.app.utils
 
+import android.net.Uri
 import java.io.File
 
 fun File.getFile(vararg subDirFiles: String): File {
@@ -11,3 +12,23 @@ fun File.exists(vararg subDirFiles: String): Boolean {
     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
+}
+
+

+ 1 - 2
app/src/main/java/io/legado/app/utils/FileUtils.kt

@@ -4,7 +4,6 @@ import android.os.Environment
 import android.webkit.MimeTypeMap
 import androidx.annotation.IntDef
 import splitties.init.appCtx
-
 import java.io.*
 import java.nio.charset.Charset
 import java.text.SimpleDateFormat
@@ -115,7 +114,7 @@ object FileUtils {
     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])
-    @kotlin.annotation.Retention(AnnotationRetention.SOURCE)
+    @Retention(AnnotationRetention.SOURCE)
     annotation class SortType
 
     /**