如何在 Android Q 中请求删除非拥有文件的文件

Per*_*abs 6 android saf delete-file

在 Android Q 中,不是默认文件管理器或图库的应用程序只能修改和/或删除它们拥有的图像文件,因此,应用程序创建的图像文件。

授予读/写权限不允许修改或删除不属于应用程序的任何文件。

这意味着不仅其他应用程序创建的文件无法访问,而且如果某个应用程序被卸载然后重新安装,那么该应用程序将失去该应用程序先前创建的所有公共文件的所有权。因此,重新安装后,它不能再修改或删除它们。

当想要修改一个图像文件或删除大量的多个图像文件,这些文件以前属于某个应用程序,但由于重新安装而失去了所有权,那么实现此类操作(删除或修改)的过程是什么?

最好的解决方案是不使用 SAF 文件选择器,以避免请求用户通过 SAF 选择和授予位置。

而如果唯一的解决方案是使用SAF文件选择器,那么如何触发直接提示删除一组已知的特定文件,而无需请求树访问权限,而不必告诉用户浏览、搜索和自己做呢?

Per*_*abs 14

我的最终结论。

对于 API >= 29,在没有用户交互的情况下不可能删除非拥有的文件,并且没有办法绕过这个事实。

Android 10/Q (API 29)中,必须捕获RecoverableSecurityException ,然后请求用户权限,最后如果获得许可则执行删除。

Android 11/R(API 30)中得到了很大的改进。可以批量删除,甚至可以将已拥有的文件合并在同一批次中。请求后无需处理任何事情,如果用户同意,系统将负责删除。 限制是它仅处理媒体文件(图像、视频、音频)。对于其他文件类型,会引发IllegalArgumentException并显示消息:“所有请求的项目必须由特定 ID 引用”在 MediaStore 源代码中检查此消息)。

请注意,API 30 中有一个新的MANAGE_EXTERNAL_STORAGE权限,但其使用需要在开发人员控制台中执行额外的步骤,例如解释为什么需要该权限。

例子:

public static void delete(final Activity activity, final Uri[] uriList, final int requestCode)
        throws SecurityException, IntentSender.SendIntentException, IllegalArgumentException
{
    final ContentResolver resolver = activity.getContentResolver();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
    {
        // WARNING: if the URI isn't a MediaStore Uri and specifically
        // only for media files (images, videos, audio) then the request
        // will throw an IllegalArgumentException, with the message:
        // 'All requested items must be referenced by specific ID'

        // No need to handle 'onActivityResult' callback, when the system returns
        // from the user permission prompt the files will be already deleted.
        // Multiple 'owned' and 'not-owned' files can be combined in the 
        // same batch request. The system will automatically delete them 
        // using the same prompt dialog, making the experience homogeneous.

        final List<Uri> list = new ArrayList<>();
        Collections.addAll(list, uriList);

        final PendingIntent pendingIntent = MediaStore.createDeleteRequest(resolver, list);
        activity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0, null);
    }
    else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q)
    {
        try
        {
            // In Android == Q a RecoverableSecurityException is thrown for not-owned.
            // For a batch request the deletion will stop at the failed not-owned
            // file, so you may want to restrict deletion in Android Q to only
            // 1 file at a time, to make the experience less ugly.
            // Fortunately this gets solved in Android R.

            for (final Uri uri : uriList)
            {
                resolver.delete(uri, null, null);
            }
        }
        catch (RecoverableSecurityException ex)
        {
            final IntentSender intent = ex.getUserAction()
                    .getActionIntent()
                    .getIntentSender();

            // IMPORTANT: still need to perform the actual deletion
            // as usual, so again getContentResolver().delete(...),
            // in your 'onActivityResult' callback, as in Android Q
            // all this extra code is necessary 'only' to get the permission,
            // as the system doesn't perform any actual deletion at all.
            // The onActivityResult doesn't have the target Uri, so you
            // need to cache it somewhere.
            activity.startIntentSenderForResult(intent, requestCode, null, 0, 0, 0, null);
        }
    }
    else
    {
        // As usual for older APIs
        
        for (final Uri uri : uriList)
        {
            resolver.delete(uri, null, null);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Com*_*are 3

实现此类操作(删除或修改)的程序是什么?

AFAIK,你唯一的选择是使用 SAF 并通过这种方式获得权利。

更好的解决方案是不使用 SAF 文件选择器,以避免请求用户通过 SAF 选择和授予位置。

那是不可能的。如果是的话,这将是一个安全缺陷。请理解,虽然您认为这些是您的文件,但从操作系统的角度来看,它们只是设备上的文件。如果应用程序可以对任意文件进行任意修改访问,那么这将是我们之前相当不安全的东西的倒退。

如何触发直接提示删除一组已知的特定文件

SAF 中没有删除文档或删除树 UI 选项,但这不是一个坏主意。

也不用告诉用户去浏览、搜索、自己做吗?

你也许可以解决这个问题你可以试试这个:

步骤#1:获取Uri其中一个MediaStore条目的 a(例如,使用ContentUrisa 中的 ID 之一query()来表示您的内容)

步骤#2:用于getDocumentUri()将其转换为指向相同内容的MediaStore UriSAFUri

步骤#3:将该 SAFUri作为EXTRA_INITIAL_URI值放入ACTION_OPEN_DOCUMENT_TREE Intent,并使用它来尝试将树选择器预先填充到您的内容目录中

步骤#4:验证Uri您返回的信息ACTION_OPEN_DOCUMENT_TREE是否是您所期望的信息(它包含您的文件,它与EXTRA_INITIAL_URI, 或类似的内容相匹配)

此时,您现在可以使用获取树的DocumentFile.fromTreeUri()a 来删除文件DocumentFile,然后从那里列出树中的文件并将其删除。

Uri您从第 2 步获得的内容是否EXTRA_INITIAL_URI适用于第 3 步还不清楚,因为我还没有尝试过(尽管它在我下周初的待办事项列表中......)。

  • 感谢您的回答,Android Q 变得越来越复杂。在我看来,就像 Android 有一个通用的共享面板、通用的权限提示等一样,那么有了 Android Q 中的所有新文件限制,至少 Android 团队已经实现了某种形式就好了系统级别的新删除提示面板,这可以使删除文件操作在应用程序之间更安全、同质和一致,从而保持安全性并使开发人员的生活更轻松。 (3认同)