如何在Android 11(API 30)中删除文件而不显示系统确认对话框?

11

我有一个应用程序,可以将视频录制到共享的MOVIES文件夹中。

在Android 11(API 30)上,我可以使用contentResolver.delete(uri, null, null)方法在我的录制视频活动中删除这些文件。

但是,如果我重新安装该应用程序,则会失去对这些文件的权限...(非常糟糕),在这种情况下,我需要执行以下操作:

try {
    context.contentResolver.delete(uri, null, null)
} catch (exception: Exception) {
    if (exception is RecoverableSecurityException) {
        val intentSender = exception.userAction.actionIntent.intentSender
        intentSender?.let {
            callback?.startIntentSenderForResult(
                intentSender,
                requestCode
            )
        }
    }
}

所以它无法使用ContentResolver删除文件,因为应用程序已被重新安装,并且存在异常,我们可以捕获并打开下一个令人烦恼的对话框,要求用户确认删除(每个文件删除都应该是不同的对话框,多个删除-不可能)。

enter image description here

然后我在这个运行Android 11的设备(模拟器)上从Google Play安装了Explorer应用程序,当我打开它时,该应用程序仅请求存储写入权限(我的应用程序也这样做),并且这个Explorer应用程序可以轻松删除任何文件(包括我的录制视频文件),而无需任何确认对话框。
那么,他们是如何做到的?这是一个黑客行为还是其他原因?
应用链接https://play.google.com/store/apps/details?id=com.speedsoftware.explorer 更新:
VLC for Android也可以删除任何媒体文件https://play.google.com/store/apps/details?id=org.videolan.vlc 他们也使用内容提供程序,所以这是相同的,但它返回true,不像我的应用程序,为什么?
fun deleteFile(file: File): Boolean {
    var deleted: Boolean
    //Delete from Android Medialib, for consistency with device MTP storing and other apps listing content:// media
    if (file.isDirectory) {
        deleted = true
        for (child in file.listFiles()) deleted = deleted and deleteFile(child)
        if (deleted) deleted = deleted and file.delete()
    } else {
        val cr = AppContextProvider.appContext.contentResolver
        try {
            deleted = cr.delete(MediaStore.Files.getContentUri("external"),
                    MediaStore.Files.FileColumns.DATA + "=?", arrayOf(file.path)) > 0
        } catch (ignored: IllegalArgumentException) {
            deleted = false
        } catch (ignored: SecurityException) {
            deleted = false
        }
        // Can happen on some devices...
        if (file.exists()) deleted = deleted or file.delete()
    }
    return deleted
}

https://github.com/videolan/vlc-android/blob/master/application/vlc-android/src/org/videolan/vlc/util/FileUtils.kt#L240


1
请求所有文件访问权限的应用程序(例如文件管理器)可以使用标准文件类删除所有文件。无需使用内容解析器或媒体存储。也不需要询问用户。 - blackapps
1
Android 11 将会推出批量删除功能。这个功能已经存在了吗? - blackapps
2
“在Google Play上发布更新,使用MANAGE_EXTERNAL_STORAGE权限会有问题吗?”-- 我无法回答这个问题。“但我仍然希望删除视频文件而不弹出对话框” -- 你已经在做到了。你的问题在于,在用户卸载应用程序,然后重新安装应用程序的情况下,你不能删除任何旧的录音。在我看来,你正在过度思考一个不常见的情况。只需告诉用户“嘿,您已卸载并重新安装,所以我们需要您的帮助清理旧的录音”,然后使用存储访问框架即可。 - CommonsWare
1
@CommonsWare 有人说 ContentResolver#checkUriPermission(Uri, int, int) 存在,但我在 ContentResolver 中找不到这个方法... https://developer.android.com/reference/android/provider/MediaStore#createDeleteRequest(android.content.ContentResolver,%20java.util.Collection%3Candroid.net.Uri%3E) - user25
@user25:看起来这是文档中的一个错误。在Context上有一个checkUriPermission()方法。 - CommonsWare
显示剩余6条评论
4个回答

4

在Android 11 (API 30)中,您可以不需要系统确认对话框,但是需要manage_external_storage权限。该权限仅允许某些特定类别的应用程序拥有。

  • 文件管理器
  • 备份和还原应用程序
  • 防病毒应用程序
  • 文档管理应用程序
  • 设备内文件搜索
  • 磁盘和文件加密
  • 设备间数据迁移

管理存储设备上的所有文件

如果您的应用程序不属于以上任一种类,则不允许使用manage_external_storage权限进行发布。

如果您的应用程序是图库、视频和音频播放器,则不需要manage_external_storage权限,并且您可以通过系统确认对话框直接删除它。
这里可以获取删除媒体文件的示例

在Android 11之前,您可以直接使用file.delete()方法删除文件。

在Android 11中,file.delete()方法只适用于您自己创建的内容。例如,您的应用程序下载了一张图片,位置在外部存储中,在这种情况下使用file.delete()方法。

如果您要删除相机或屏幕截图等媒体文件,则file.delete()方法在Android 11中不起作用,因为媒体内容不是由您创建的。在这种情况下需要使用系统确认对话框。


1
以下代码片段适用于所有 Android 版本,包括 Android 11。
fun deleteFile(path_of_file :String){

        val uri = Uri.parse(path_of_file)

        try{
            // android 28 and below
            contentResolver.delete(uri, null, null)
        }catch (e : SecurityException){
            // android 29 (Andriod 10) 
            val intentSender = when {
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
                    MediaStore.createDeleteRequest(contentResolver, listOf(uri)).intentSender
                }
                // android 30 (Andriod 11) 
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
                    val recoverableSecurityException = e as? RecoverableSecurityException
                    recoverableSecurityException?.userAction?.actionIntent?.intentSender
                }
                else -> null
            }
            intentSender?.let { sender ->
                intentSenderLauncher.launch(
                    IntentSenderRequest.Builder(sender).build()
                )
            }
        }
    }

2
intentSenderLauncher在哪里? - Android Dev

1

0

这是 https://developer.android.com/reference/android/provider/MediaStore#createDeleteRequest(android.content.ContentResolver,%2520java.util.Collection%3Candroid.net.Uri%3E) - user25

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接