使用 AndroidX ExifInterface 从图像中检索 GPS EXIF 数据?

Nie*_*orp 6 android runtime-permissions android-jetpack-compose android-exifinterface photo-picker

我的目标是 Android 13 并使用新的照片选择器来检索图像。例如

val photoPicker = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.PickMultipleVisualMedia()
    ) { uris: List<Uri> -> handleUris(context, uris) ) }) }

photoPicker.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))

fun handleUris(context: Context, uris: List<Uri>) {
    for (uri in uris) {
        context.contentResolver.openInputStream(uri)!!.use { stream ->
            ExifInterface(stream).let { exif ->
                val dateTime = exif.dateTime // present
                val latLng = exif.latLong // always null
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我发现了什么:

自 Android 10 起,当您检索媒体时,GPS 信息不再包含在 EXIF 数据中。根据 2019 年的这段视频,您需要请求运行时权限ACCESS_MEDIA_LOCATION

他们解释说这是一个运行时权限,但用户无法在系统设置中看到该权限(这本身就很奇怪)。他们还声称您需要READ_EXTERNAL_STORAGE媒体许可。

这篇文档还讨论了媒体位置权限。

使用以下方式请求许可ACCESS_MEDIA_LOCATION

val locationPermissionState = rememberPermissionState(
        permission = ACCESS_MEDIA_LOCATION
)
locationPermissionState.launchPermissionRequest()
Run Code Online (Sandbox Code Playgroud)

它实际上确实会导致一个权限对话框,要求“应用程序访问此设备上的照片和视频”。当授予 EXIF 位置数据时,但仍然不存在。

啊,对了。根据视频我们READ_EXTERNAL_STORAGE也需要。

val locationPermissionState = rememberMultiplePermissionsState(
        permissions = listOf(ACCESS_MEDIA_LOCATION, READ_EXTERNAL_STORAGE)
)
Run Code Online (Sandbox Code Playgroud)

没有显示对话框。检查ContextCompat.checkSelfPermission()它似乎我们有ACCESS_MEDIA_LOCATION权限,但没有READ_EXTERNAL_STORAGE。为什么它不显示一个对话框?

查看Android 13 的文档,似乎已READ_EXTERNAL_STORAGE被更细化的权限所取代。

当针对 Android 13 时,您似乎不应READ_EXTERNAL_STORAGE 再使用该权限。对于我们需要的图像READ_MEDIA_IMAGES。但请求该权限也不会显示对话框。

我还尝试了MediaStore.setRequireOriginal(Uri)API,它应该可以防止 EXIF 元数据被编辑,根据文档,UnsupportedOperationException您没有权限时,这会抛出一个错误ACCESS_MEDIA_LOCATION 。但对我来说,它会抛出一个SecurityException带有消息的Calling uid does not have permission to access picker uri: content://etc

那么,是的,我如何获取 EXIF 数据中的 GPS 位置?我实际需要哪些权限以及如何请求它们?

编辑:

好的,所以在 Android 11 上,当照片选择器不可用时,它会Intent(Intent.ACTION_OPEN_DOCUMENT)在内部使用。返回的 Uri 如下所示:content://com.android.providers.media.documents/document/image%3A283

我可以从该 uri 检索 GPS 位置,而无需授予ACCESS_MEDIA_LOCATION,READ_MEDIA_IMAGEREAD_EXTERNAL_STORAGE权限。与我能找到的大多数文档相反。也不MediaStore.setRequireOriginal(Uri)需要。

这让我认为这是照片选择器问题而不是权限问题。所以我想我在 Android 13 上进行测试时不使用照片选择器。

我在用着

val photoPicker = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.GetContent()
) { uri: Uri? ->  }
Run Code Online (Sandbox Code Playgroud)

相反,现在Intent(Intent.ACTION_GET_CONTENT)在内部使用。uri 看起来像这样content://com.android.providers.media.documents/document/image%3A1000000049,我再次能够在没有授予任何上述权限的情况下提取 GPS 数据,并且 no setRequireOriginal

这让我得出结论,有关媒体位置的文档是错误的......不需要许可(除非他们谈论的是纬度/经度以外的其他元数据)。SecurityException当使用setRequireOriginal和/或 exif 数据编辑时,新的照片选择器 uri 肯定会发生一些事情。我还不知道这是否是预期的行为。

Nie*_*orp 7

截至目前,位置元数据已从通过新照片选择器检索的图像中进行编辑。将来可能会添加它,但目前我们仍坚持使用“旧” Intent.ACTION_GET_CONTENT

为此问题加注星标。