Files
QieKan-3.0/app/src/main/java/io/legado/app/utils/UriExtensions.kt
T
2023-02-15 22:38:43 +08:00

223 lines
7.0 KiB
Kotlin

package io.legado.app.utils
import android.content.Context
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import io.legado.app.R
import io.legado.app.constant.AppLog
import io.legado.app.exception.NoStackTraceException
import io.legado.app.lib.permission.Permissions
import io.legado.app.lib.permission.PermissionsCompat
import okhttp3.MediaType
import okhttp3.RequestBody
import okio.BufferedSink
import okio.source
import splitties.init.appCtx
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.nio.charset.Charset
fun Uri.isContentScheme() = this.scheme == "content"
fun Uri.isFileScheme() = this.scheme == "file"
/**
* 读取URI
*/
fun AppCompatActivity.readUri(
uri: Uri?,
success: (fileDoc: FileDoc, inputStream: InputStream) -> Unit
) {
uri ?: return
try {
if (uri.isContentScheme()) {
val doc = DocumentFile.fromSingleUri(this, uri)
doc ?: throw NoStackTraceException("未获取到文件")
val fileDoc = FileDoc.fromDocumentFile(doc)
contentResolver.openInputStream(uri)!!.use { inputStream ->
success.invoke(fileDoc, inputStream)
}
} else {
PermissionsCompat.Builder()
.addPermissions(
Permissions.READ_EXTERNAL_STORAGE,
Permissions.WRITE_EXTERNAL_STORAGE
)
.rationale(R.string.get_storage_per)
.onGranted {
RealPathUtil.getPath(this, uri)?.let { path ->
val file = File(path)
val fileDoc = FileDoc.fromFile(file)
FileInputStream(file).use { inputStream ->
success.invoke(fileDoc, inputStream)
}
}
}
.request()
}
} catch (e: Exception) {
e.printOnDebug()
toastOnUi(e.localizedMessage ?: "read uri error")
if (e is SecurityException) {
throw e
}
}
}
/**
* 读取URI
*/
fun Fragment.readUri(uri: Uri?, success: (fileDoc: FileDoc, inputStream: InputStream) -> Unit) {
uri ?: return
try {
if (uri.isContentScheme()) {
val doc = DocumentFile.fromSingleUri(requireContext(), uri)
doc ?: throw NoStackTraceException("未获取到文件")
val fileDoc = FileDoc.fromDocumentFile(doc)
requireContext().contentResolver.openInputStream(uri)!!.use { inputStream ->
success.invoke(fileDoc, inputStream)
}
} else {
PermissionsCompat.Builder()
.addPermissions(
Permissions.READ_EXTERNAL_STORAGE,
Permissions.WRITE_EXTERNAL_STORAGE
)
.rationale(R.string.get_storage_per)
.onGranted {
RealPathUtil.getPath(requireContext(), uri)?.let { path ->
val file = File(path)
val fileDoc = FileDoc.fromFile(file)
FileInputStream(file).use { inputStream ->
success.invoke(fileDoc, inputStream)
}
}
}
.request()
}
} catch (e: Exception) {
e.printOnDebug()
toastOnUi(e.localizedMessage ?: "read uri error")
}
}
@Throws(Exception::class)
fun Uri.readBytes(context: Context): ByteArray {
return if (this.isContentScheme()) {
context.contentResolver.openInputStream(this)?.let {
val len: Int = it.available()
val buffer = ByteArray(len)
it.read(buffer)
it.close()
return buffer
} ?: throw NoStackTraceException("打开文件失败\n${this}")
} else {
val path = RealPathUtil.getPath(context, this)
if (path?.isNotEmpty() == true) {
File(path).readBytes()
} else {
throw NoStackTraceException("获取文件真实地址失败\n${this.path}")
}
}
}
@Throws(Exception::class)
fun Uri.readText(context: Context): String {
readBytes(context).let {
return String(it)
}
}
@Throws(Exception::class)
fun Uri.writeBytes(
context: Context,
byteArray: ByteArray
): Boolean {
if (this.isContentScheme()) {
context.contentResolver.openOutputStream(this)?.let {
it.write(byteArray)
it.close()
return true
}
return false
} else {
val path = RealPathUtil.getPath(context, this)
if (path?.isNotEmpty() == true) {
File(path).writeBytes(byteArray)
return true
}
}
return false
}
@Throws(Exception::class)
fun Uri.writeText(context: Context, text: String, charset: Charset = Charsets.UTF_8): Boolean {
return writeBytes(context, text.toByteArray(charset))
}
fun Uri.writeBytes(
context: Context,
fileName: String,
byteArray: ByteArray
): Boolean {
if (this.isContentScheme()) {
DocumentFile.fromTreeUri(context, this)?.let { pDoc ->
DocumentUtils.createFileIfNotExist(pDoc, fileName)?.let {
return it.uri.writeBytes(context, byteArray)
}
}
} else {
FileUtils.createFileWithReplace(path + File.separatorChar + fileName)
.writeBytes(byteArray)
return true
}
return false
}
fun Uri.inputStream(context: Context): Result<InputStream> {
val uri = this
return kotlin.runCatching {
try {
if (isContentScheme()) {
DocumentFile.fromSingleUri(context, uri)
?: throw NoStackTraceException("未获取到文件")
return@runCatching context.contentResolver.openInputStream(uri)!!
} else {
val path = RealPathUtil.getPath(context, uri)
?: throw NoStackTraceException("未获取到文件")
val file = File(path)
if (file.exists()) {
return@runCatching FileInputStream(file)
} else {
throw NoStackTraceException("文件不存在")
}
}
} catch (e: Exception) {
e.printOnDebug()
AppLog.put("读取inputStream失败:${e.localizedMessage}", e)
throw e
}
}
}
fun Uri.toRequestBody(contentType: MediaType? = null): RequestBody {
val uri = this
return object : RequestBody() {
override fun contentType() = contentType
override fun contentLength(): Long {
val length = uri.inputStream(appCtx).getOrThrow().available().toLong()
return if (length > 0) length else -1
}
override fun writeTo(sink: BufferedSink) {
uri.inputStream(appCtx).getOrThrow().source().use { source ->
sink.writeAll(source)
}
}
}
}