修复:解决google play的图片和视频权限要求
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
package uts.sdk.modules.uniChooseSystemImage
|
||||
|
||||
import android.content.ContentResolver
|
||||
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 android.webkit.MimeTypeMap
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URLConnection
|
||||
import java.security.MessageDigest
|
||||
|
||||
|
||||
object FileUtils {
|
||||
fun getFilePathByUri(context: Context, uri: Uri): String? {
|
||||
var path: String? = null
|
||||
// 以 file:// 开头的
|
||||
if (ContentResolver.SCHEME_FILE == uri.scheme) {
|
||||
path = uri.path
|
||||
return path
|
||||
}
|
||||
// 以 content:// 开头的,比如 content://media/extenral/images/media/17766
|
||||
if (ContentResolver.SCHEME_CONTENT == uri.scheme && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
val cursor = context.contentResolver.query(
|
||||
uri,
|
||||
arrayOf(MediaStore.Images.Media.DATA),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||
if (columnIndex > -1) {
|
||||
path = cursor.getString(columnIndex)
|
||||
}
|
||||
}
|
||||
cursor.close()
|
||||
}
|
||||
return path
|
||||
}
|
||||
// 4.4及之后的 是以 content:// 开头的,比如 content://com.android.providers.media.documents/document/image%3A235700
|
||||
if (ContentResolver.SCHEME_CONTENT == uri.scheme && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
// ExternalStorageProvider
|
||||
val docId = DocumentsContract.getDocumentId(uri)
|
||||
val split =
|
||||
docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val type = split[0]
|
||||
if ("primary".equals(type, ignoreCase = true)) {
|
||||
path = Environment.getExternalStorageDirectory().toString() + "/" + split[1]
|
||||
return path
|
||||
}
|
||||
} else if (isDownloadsDocument(uri)) {
|
||||
// DownloadsProvider
|
||||
val id = DocumentsContract.getDocumentId(uri)
|
||||
val contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"),
|
||||
id.toLong()
|
||||
)
|
||||
path = getDataColumn(context, contentUri, null, null)
|
||||
return path
|
||||
} else if (isMediaDocument(uri)) {
|
||||
// MediaProvider
|
||||
val docId = DocumentsContract.getDocumentId(uri)
|
||||
val split =
|
||||
docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.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])
|
||||
path = getDataColumn(context, contentUri, selection, selectionArgs)
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 新增:将 uri 拷贝到传入的父文件夹,文件名为 uri 的 MD5,后缀从头信息或原文件名推断。
|
||||
// 若目标文件已存在则直接返回已存在路径,不重复拷贝。
|
||||
// 返回目标文件的绝对路径,失败返回 null。
|
||||
fun copyUriToDir(context: Context, parentDirStr: String, uriString: String): String? {
|
||||
try {
|
||||
var uri = Uri.parse(uriString)
|
||||
var parentDir = File(parentDirStr)
|
||||
val resolver = context.contentResolver
|
||||
// 读取全部数据到内存(用于判断 MIME 并写入目标文件)
|
||||
val inputStream = resolver.openInputStream(uri) ?: return null
|
||||
val baos = ByteArrayOutputStream()
|
||||
inputStream.use { ins ->
|
||||
val buf = ByteArray(8 * 1024)
|
||||
var len: Int
|
||||
while (ins.read(buf).also { len = it } != -1) {
|
||||
baos.write(buf, 0, len)
|
||||
}
|
||||
}
|
||||
val data = baos.toByteArray()
|
||||
// 通过头信息猜 MIME
|
||||
var mime: String? = null
|
||||
try {
|
||||
mime = URLConnection.guessContentTypeFromStream(ByteArrayInputStream(data))
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
if (mime == null) {
|
||||
try {
|
||||
mime = resolver.getType(uri)
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
// 若仍为空,尝试从原始路径推断
|
||||
var originalPath: String? = null
|
||||
try {
|
||||
originalPath = getFilePathByUri(context, uri)
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
// 根据 mime 获取扩展名
|
||||
var ext: String? = null
|
||||
if (mime != null) {
|
||||
ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime)
|
||||
}
|
||||
// 如果通过 mime 无法得到扩展名,尝试从原始路径取后缀
|
||||
if (ext.isNullOrEmpty() && !originalPath.isNullOrEmpty()) {
|
||||
val idx = originalPath.lastIndexOf('.')
|
||||
if (idx != -1 && idx + 1 < originalPath.length) {
|
||||
ext = originalPath.substring(idx + 1).lowercase()
|
||||
}
|
||||
}
|
||||
val extSuffix = if (!ext.isNullOrEmpty()) ".${ext}" else ""
|
||||
// 计算 MD5 作为文件名(基于 uri.toString())
|
||||
val name = md5(uri.toString()) + extSuffix
|
||||
// 确保父目录存在
|
||||
if (!parentDir.exists()) {
|
||||
parentDir.mkdirs()
|
||||
}
|
||||
val destFile = File(parentDir, name)
|
||||
// 若已存在,直接返回
|
||||
if (destFile.exists()) {
|
||||
return destFile.absolutePath
|
||||
}
|
||||
// 写入文件
|
||||
FileOutputStream(destFile).use { fos ->
|
||||
fos.write(data)
|
||||
fos.flush()
|
||||
}
|
||||
return destFile.absolutePath
|
||||
} catch (e: Exception) {
|
||||
// 出错返回 null
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助:计算字符串的 MD5(小写 hex)
|
||||
private fun md5(input: String): String {
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
val bytes = md.digest(input.toByteArray(Charsets.UTF_8))
|
||||
return bytes.joinToString("") { "%02x".format(it) }
|
||||
}
|
||||
|
||||
private fun getDataColumn(
|
||||
context: Context,
|
||||
uri: Uri?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?,
|
||||
): String? {
|
||||
var cursor: Cursor? = null
|
||||
val column = "_data"
|
||||
val projection = arrayOf(column)
|
||||
try {
|
||||
cursor =
|
||||
context.contentResolver.query(uri!!, projection, selection, selectionArgs, null)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
val column_index = cursor.getColumnIndexOrThrow(column)
|
||||
return cursor.getString(column_index)
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun isExternalStorageDocument(uri: Uri): Boolean {
|
||||
return "com.android.externalstorage.documents" == uri.authority
|
||||
}
|
||||
|
||||
private fun isDownloadsDocument(uri: Uri): Boolean {
|
||||
return "com.android.providers.downloads.documents" == uri.authority
|
||||
}
|
||||
|
||||
private fun isMediaDocument(uri: Uri): Boolean {
|
||||
return "com.android.providers.media.documents" == uri.authority
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user