为 ACTION_SEND 意图过滤 uri 获取持久 URI 权限会导致 SecurityException

问题描述 投票:0回答:2

我的用户可以通过

ACTION_OPEN_DOCUMENT

选择文件
val launcher = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.OpenDocument(),
    onResult = onResult
)

之后我使用

takePersistableUriPermission

contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)

这个效果太棒了!现在,选择器 UI 并不是我所说的最佳界面,因此我还想允许通过与我的应用程序共享文件来反转控制流。

<intent-filter>
   <action android:name="android.intent.action.SEND" />
   <category android:name="android.intent.category.DEFAULT" />
   <data android:mimeType="image/*" />
</intent-filter>

onCreate
或我的
onNewIntent
中的
Activity
,我还想获取持久 URI 权限。

override fun onNewIntent(intent: Intent?) {
    // null checks etc
    val uri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri ?: return
    contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
    // ...
}

然而,这会导致 SecurityException:

java.lang.SecurityException:未找到 UID 10146 和 Uri content://com.google.android.apps.photos.contentprovider/-1/1/content://media/external/images/media/ 的持久权限授予61/REQUIRE_ORIGINAL/无/图像/jpeg/702648108

有没有办法实现这个目标?

android android-contentprovider android-jetpack-compose android-context
2个回答
9
投票

有没有办法实现这个目标?

一般来说,不会。

takePersistableUriPermission()
适用于存储访问框架
Uri
值,如您的
ACTION_OPEN_DOCUMENT
场景中一样。它不适用于任意
Uri
值,就像您可能通过
ACTION_PICK
获得的那样,或在接收
EXTRA_STREAM
时获得
ACTION_SEND
,或者将
Uri
传递给
ACTION_VIEW
等。

对于

ACTION_SEND
ACTION_VIEW
等,您需要假设您的应用程序只能暂时访问内容。如果您需要长期访问,您将需要进行某种“导入”操作并制作您自己的内容副本。


0
投票

我使用

UriToFile
实用程序来缓存和使用来自内容提供商的文件。

val file = UriToFile(context).getImageBody(conetntUri)

实用性:

import android.content.Context
import android.net.Uri
import android.provider.OpenableColumns
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream

class UriToFile(context: Context) {
    private val applicationContext = context.applicationContext
    fun getImageBody(imageUri: Uri): File {
        val parcelFileDescriptor = applicationContext.contentResolver.openFileDescriptor(
            imageUri,
            "r",// r stands for read only for the file
            null
        )
        val file = File(
            applicationContext.cacheDir,
            applicationContext.contentResolver.getFileName(imageUri)
        )
        val inputStream = FileInputStream(parcelFileDescriptor?.fileDescriptor)
        val outputStream = FileOutputStream(file)
        inputStream.copyTo(outputStream)
        parcelFileDescriptor?.close()
        return file
    }
}

private fun ContentResolver.getFileName(uri: Uri): String {
    var name = ""
    val cursor = this.query(
        uri, null, null,
        null, null
    )
    cursor?.use {
        it.moveToFirst()
        name = it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
    }
    return name
}
© www.soinside.com 2019 - 2024. All rights reserved.