diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt index 8acc883ee..4b2aa6d9c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt @@ -193,7 +193,7 @@ class BgTextConfigDialog : DialogFragment() { ) .rationale(R.string.bg_image_per) .onGranted { - FileUtils.getRealPath(requireContext(), uri)?.let { path -> + RealPathUtil.getPath(requireContext(), uri)?.let { path -> ReadBookConfig.getConfig().setBg(2, path) ReadBookConfig.upBg() postEvent(EventBus.UP_CONFIG, false) diff --git a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt index c9e4d4bd2..bfd4d23a1 100644 --- a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt @@ -11,7 +11,7 @@ import io.legado.app.R import io.legado.app.base.BaseActivity import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.PermissionsCompat -import io.legado.app.utils.FileUtils +import io.legado.app.utils.RealPathUtil import kotlinx.android.synthetic.main.activity_qrcode_capture.* import kotlinx.android.synthetic.main.view_title_bar.* import org.jetbrains.anko.toast @@ -98,7 +98,7 @@ class QrCodeActivity : BaseActivity(R.layout.activity_qrcode_capture), QRCodeVie zxingview.startSpotAndShowRect() // 显示扫描框,并开始识别 if (resultCode == Activity.RESULT_OK && requestCode == requestQrImage) { - val picturePath = FileUtils.getRealPath(this, it) + val picturePath = RealPathUtil.getPath(this, it) // 本来就用到 QRCodeView 时可直接调 QRCodeView 的方法,走通用的回调 zxingview.decodeQRCode(picturePath) } diff --git a/app/src/main/java/io/legado/app/utils/FileUtils.kt b/app/src/main/java/io/legado/app/utils/FileUtils.kt index c01eff00d..a9408dd10 100644 --- a/app/src/main/java/io/legado/app/utils/FileUtils.kt +++ b/app/src/main/java/io/legado/app/utils/FileUtils.kt @@ -1,11 +1,6 @@ package io.legado.app.utils -import android.content.ContentUris -import android.content.Context -import android.net.Uri import android.os.Environment -import android.provider.DocumentsContract -import android.provider.MediaStore import io.legado.app.App import java.io.File import java.io.IOException @@ -105,122 +100,4 @@ object FileUtils { return sdCardDirectory } - fun getRealPath(context: Context, uri: Uri): String? { - // DocumentProvider - if (DocumentsContract.isDocumentUri(context, uri)) { - // ExternalStorageProvider - if (isExternalStorageDocument(uri)) { - val docId = DocumentsContract.getDocumentId(uri) - val split = docId.split(":") - val type = split[0] - - if ("primary".equals(type, ignoreCase = true)) { - @Suppress("DEPRECATION") - return Environment.getExternalStorageDirectory().toString() + "/" + split[1] - } - - } else if (isDownloadsDocument(uri)) { - val id = DocumentsContract.getDocumentId(uri) - val split = id.split(":") - val type = split[0] - if ("raw".equals(type, ignoreCase = true)) { - //处理某些机型(比如Google Pixel )ID是raw:/storage/emulated/0/Download/c20f8664da05ab6b4644913048ea8c83.mp4 - return split[1] - } - - val contentUri = ContentUris.withAppendedId( - Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id) - ) - - return getDataColumn(context, contentUri, null, null) - } else if (isMediaDocument(uri)) { - val docId = DocumentsContract.getDocumentId(uri) - val split = docId.split(":".toRegex()) - - val contentUri: Uri = when (split[0]) { - "image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI - "video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI - "audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI - else -> uri - } - - val selection = "_id=?" - val selectionArgs = arrayOf(split[1]) - - return getDataColumn(context, contentUri, selection, selectionArgs) - }// MediaProvider - // DownloadsProvider - } else if ("content".equals(uri.scheme, ignoreCase = true)) { - // Return the remote address - return if (isGooglePhotosUri(uri)) - uri.lastPathSegment - else - getDataColumn(context, uri, null, null) - } else if ("file".equals(uri.scheme, ignoreCase = true)) { - return uri.path - }// File - // MediaStore (and general) - return null - } - - private fun getDataColumn( - context: Context, uri: Uri, selection: String?, - selectionArgs: Array? - ): String? { - - val column = "_data" - val projection = arrayOf(column) - - try { - context.contentResolver.query( - uri, - projection, - selection, - selectionArgs, - null - )?.use { cursor -> - if (cursor.moveToFirst()) { - val index = cursor.getColumnIndexOrThrow(column) - return cursor.getString(index) - } - } - } catch (e: Exception) { - e.printStackTrace() - } - - return null - } - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is ExternalStorageProvider. - */ - private fun isExternalStorageDocument(uri: Uri): Boolean { - return "com.android.externalstorage.documents" == uri.authority - } - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is DownloadsProvider. - */ - private fun isDownloadsDocument(uri: Uri): Boolean { - return "com.android.providers.downloads.documents" == uri.authority - } - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is MediaProvider. - */ - private fun isMediaDocument(uri: Uri): Boolean { - return "com.android.providers.media.documents" == uri.authority - } - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is Google Photos. - */ - private fun isGooglePhotosUri(uri: Uri): Boolean { - return "com.google.android.apps.photos.content" == uri.authority - } - } diff --git a/app/src/main/java/io/legado/app/utils/RealPathUtil.kt b/app/src/main/java/io/legado/app/utils/RealPathUtil.kt new file mode 100644 index 000000000..5ded2f823 --- /dev/null +++ b/app/src/main/java/io/legado/app/utils/RealPathUtil.kt @@ -0,0 +1,173 @@ +package io.legado.app.utils + +import android.annotation.SuppressLint +import android.content.ContentUris +import android.content.Context +import android.database.Cursor +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.DocumentsContract +import android.provider.MediaStore +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException + +@Suppress("unused") +object RealPathUtil { + /** + * Method for return file path of Gallery image + * @return path of the selected image file from gallery + */ + private var filePathUri: Uri? = null + + @Suppress("DEPRECATION") + fun getPath( + context: Context, + uri: Uri + ): String? { + //check here to KITKAT or new version + @SuppressLint("ObsoleteSdkInt") + val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT + filePathUri = uri + // DocumentProvider + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider + if (isExternalStorageDocument(uri)) { + val docId = DocumentsContract.getDocumentId(uri) + val split = docId.split(":").toTypedArray() + val type = split[0] + if ("primary".equals(type, ignoreCase = true)) { + return Environment.getExternalStorageDirectory().toString() + "/" + split[1] + } + } else if (isDownloadsDocument(uri)) { + val id = DocumentsContract.getDocumentId(uri) + val contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), + java.lang.Long.valueOf(id) + ) + //return getDataColumn(context, uri, null, null); + return getDataColumn(context, contentUri, null, null) + } else if (isMediaDocument(uri)) { + val docId = DocumentsContract.getDocumentId(uri) + val split = docId.split(":").toTypedArray() + val type = split[0] + var contentUri: Uri? = null + if ("image" == type) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI + } else if ("video" == type) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI + } else if ("audio" == type) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + } + val selection = "_id=?" + val selectionArgs = arrayOf( + split[1] + ) + return getDataColumn(context, contentUri, selection, selectionArgs) + } + } else if ("content".equals( + uri.scheme, + ignoreCase = true + ) + ) { // Return the remote address + return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn( + context, + uri, + null, + null + ) + } else if ("file".equals(uri.scheme, ignoreCase = true)) { + return uri.path + } + return null + } + + /** + * Get the value of the data column for this Uri. This is useful for + * MediaStore Uris, and other file-based ContentProviders. + * + * @param context The context. + * @param uri The Uri to query. + * @param selection (Optional) Filter used in the query. + * @param selectionArgs (Optional) Selection arguments used in the query. + * @return The value of the _data column, which is typically a file path. + */ + private fun getDataColumn( + context: Context, uri: Uri?, selection: String?, + selectionArgs: Array? + ): String? { + var cursor: Cursor? = null + val column = "_data" + val projection = arrayOf( + column + ) + var input: FileInputStream? = null + var output: FileOutputStream? = null + try { + cursor = + context.contentResolver.query(uri!!, projection, selection, selectionArgs, null) + if (cursor != null && cursor.moveToFirst()) { + val index = cursor.getColumnIndexOrThrow(column) + return cursor.getString(index) + } + } catch (e: IllegalArgumentException) { + e.printStackTrace() + val file = File(context.cacheDir, "tmp") + val filePath = file.absolutePath + try { + val pfd = + context.contentResolver.openFileDescriptor(filePathUri!!, "r") + ?: return null + val fd = pfd.fileDescriptor + input = FileInputStream(fd) + output = FileOutputStream(filePath) + var read: Int + val bytes = ByteArray(4096) + while (input.read(bytes).also { read = it } != -1) { + output.write(bytes, 0, read) + } + input.close() + output.close() + return File(filePath).absolutePath + } catch (ignored: IOException) { + ignored.printStackTrace() + } + } finally { + cursor?.close() + } + return null + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is ExternalStorageProvider. + */ + private fun isExternalStorageDocument(uri: Uri): Boolean { + return "com.android.externalstorage.documents" == uri.authority + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is DownloadsProvider. + */ + private fun isDownloadsDocument(uri: Uri): Boolean { + return "com.android.providers.downloads.documents" == uri.authority + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is MediaProvider. + */ + private fun isMediaDocument(uri: Uri): Boolean { + return "com.android.providers.media.documents" == uri.authority + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is Google Photos. + */ + private fun isGooglePhotosUri(uri: Uri): Boolean { + return "com.google.android.apps.photos.content" == uri.authority + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/utils/UriExtensions.kt b/app/src/main/java/io/legado/app/utils/UriExtensions.kt index cd3056941..672259dc8 100644 --- a/app/src/main/java/io/legado/app/utils/UriExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/UriExtensions.kt @@ -2,15 +2,14 @@ package io.legado.app.utils import android.content.Context import android.net.Uri -import androidx.documentfile.provider.DocumentFile import java.io.File @Throws(Exception::class) fun Uri.readBytes(context: Context): ByteArray? { - if (DocumentFile.isDocumentUri(context, this)) { + if (this.toString().isContentPath()) { return DocumentUtils.readBytes(context, this) } else { - val path = FileUtils.getRealPath(context, this) + val path = RealPathUtil.getPath(context, this) if (path?.isNotEmpty() == true) { return File(path).readBytes() } @@ -20,10 +19,10 @@ fun Uri.readBytes(context: Context): ByteArray? { @Throws(Exception::class) fun Uri.readText(context: Context): String? { - if (DocumentFile.isDocumentUri(context, this)) { + if (this.toString().isContentPath()) { return DocumentUtils.readText(context, this) } else { - val path = FileUtils.getRealPath(context, this) + val path = RealPathUtil.getPath(context, this) if (path?.isNotEmpty() == true) { return File(path).readText() } @@ -33,10 +32,10 @@ fun Uri.readText(context: Context): String? { @Throws(Exception::class) fun Uri.writeBytes(context: Context, byteArray: ByteArray): Boolean { - if (DocumentFile.isDocumentUri(context, this)) { + if (this.toString().isContentPath()) { return DocumentUtils.writeBytes(context, byteArray, this) } else { - val path = FileUtils.getRealPath(context, this) + val path = RealPathUtil.getPath(context, this) if (path?.isNotEmpty() == true) { File(path).writeBytes(byteArray) return true @@ -47,10 +46,10 @@ fun Uri.writeBytes(context: Context, byteArray: ByteArray): Boolean { @Throws(Exception::class) fun Uri.writeText(context: Context, text: String): Boolean { - if (DocumentFile.isDocumentUri(context, this)) { + if (this.toString().isContentPath()) { return DocumentUtils.writeText(context, text, this) } else { - val path = FileUtils.getRealPath(context, this) + val path = RealPathUtil.getPath(context, this) if (path?.isNotEmpty() == true) { File(path).writeText(text) return true