标签: storage-access-framework

KitKat ACTION_OPEN_DOCUMENT不显示三星设备上的文档

我正在使用此处指定的新Kitkat存储访问框架(SAF):https: //developer.android.com/guide/topics/providers/document-provider.html

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, 0);
Run Code Online (Sandbox Code Playgroud)

这与示例代码相同,但图像过滤器不起作用.S5或Note3上没有显示任何内容.视频(视频/*)也是如此.我也尝试了不同的模式,如/无效.

这看起来像是一个应该由他们解决的三星问题,我只是想知道是否有人知道解决方法.

android android-4.4-kitkat samsung-touchwiz storage-access-framework

5
推荐指数
2
解决办法
3768
查看次数

从Lollipop的google驱动器获取文件路径(MediaStore.MediaColumns.DATA == null)

当用户点击谷歌硬盘中的"发送文件"按钮并选择我的应用程序.我想获取该文件的文件路径,然后允许用户将其上传到其他位置.

我为kitkat手机检查了这些类似的SO帖子:从URI,Android KitKat新的存储访问框架获取真实路径

Android - 将URI转换为棒棒糖上的文件路径

然而,解决方案似乎不再适用于Lollipop设备.

问题似乎是MediaStore.MediaColumns.DATA在ContentResolver上运行查询时返回null.

https://code.google.com/p/android/issues/detail?id=63651

您应该使用ContentResolver.openFileDescriptor()而不是尝试获取原始文件系统路径."_data"列不是CATEGORY_OPENABLE合同的一部分,因此不需要Drive返回它.

我已经阅读了CommonsWare的这篇博文,其中建议我"尝试直接使用Uri直接使用ContentResolver",这是我不明白的.如何直接在ContentResolvers中使用URI?

但是,我仍然不清楚如何最好地处理这些类型的URI.

我能找到的最佳解决方案是调用openFileDescriptor,然后将文件流复制到一个新文件中,然后将该新文件路径传递给我的上传活动.

 private static String getDriveFileAbsolutePath(Activity context, Uri uri) {
    if (uri == null) return null;
    ContentResolver resolver = context.getContentResolver();
    FileInputStream input = null;
    FileOutputStream output = null;
    String outputFilePath = new File(context.getCacheDir(), fileName).getAbsolutePath();
    try {
        ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
        FileDescriptor fd = pfd.getFileDescriptor();
        input = new FileInputStream(fd);
        output = new FileOutputStream(outputFilePath);
        int read = 0;
        byte[] bytes = new byte[4096];
        while ((read …
Run Code Online (Sandbox Code Playgroud)

android android-intent google-drive-api storage-access-framework

5
推荐指数
1
解决办法
1780
查看次数

尝试通过ACTION_OPEN_DOCUMENT为自定义DocumentsProvider尝试采用PersistableUriPermission()失败

我正在尝试编写一个自定义规则DocumentsProvider,以允许其他应用程序对其提供的Uris拥有持久权限

我有一个DocumentsProvider我声明AndroidManufest.xml如下

<provider
   android:name="com.cgogolin.myapp.MyContentProvider"
   android:authorities="com.cgogolin.myapp.MyContentProvider"
   android:grantUriPermissions="true"
   android:exported="true"
   android:permission="android.permission.MANAGE_DOCUMENTS"
   android:enabled="@bool/atLeastKitKat">
  <intent-filter>
    <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
  </intent-filter>
</provider>
Run Code Online (Sandbox Code Playgroud)

并且我的应用具有MANAGE_DOCUMENTS权限设置

<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
Run Code Online (Sandbox Code Playgroud)

(显然,这不是必需的,但是添加/删除它也没有关系)。然后,当我打开ACTION_OPEN_DOCUMENT选择器用户界面时,可以看到我的提供者

Intent openDocumentIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
openDocumentIntent.addCategory(Intent.CATEGORY_OPENABLE);
openDocumentIntent.setType("application/pdf");
openDocumentIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
startActivityForResult(openDocumentIntent, EDIT_REQUEST);
Run Code Online (Sandbox Code Playgroud)

然后,从我的提供商那里选择一个文件之后,在onActivityResult()我的App 的方法中,我可以通过我DocumentsProviderUri那里获取,成功打开我提供的文件intent.getData()

但是,尝试通过以下方式持久保留读或写权限:

getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Run Code Online (Sandbox Code Playgroud)

要么

getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Run Code Online (Sandbox Code Playgroud)

总是失败,并出现类似的异常

No permission grant found for UID 10210 and Uri content://com.cgogolin.myapp.MyContentProvider/document/tshjhczf.pdf
Run Code Online (Sandbox Code Playgroud)

如果我从Google驱动器中选取文件,或者在选择器UI中选择下载提供程序,则以这种方式获得许可就可以了。所以我认为问题出在我的提供者中。

尽管我进行了指定,为什么没有创建许可授予android:grantUriPermissions="true"

如何说服Android为我创建这样的权限授予?

毕竟我不认为自己可以做,因为我不知道UID打开选择器UI的过程,或者至少我不知道该怎么做。

permissions android android-contentprovider storage-access-framework

5
推荐指数
1
解决办法
4970
查看次数

使用Android Storage Access Framework重命名Google Drive文档会导致权限错误

我正在使用android的存储访问框架(SAF)与文档进行交互。我已经能够找到/读取/写入文档,没有任何麻烦,但是当我尝试对Google Drive文档使用DocumentsContract#renameDocument()重命名时遇到了问题。我发布的代码与内部存储中的文件配合正常。

为了使事情变得容易,我制作了一个示例应用程序并将其推送到github。您可以在以下位置找到该代码:https : //github.com/scottTomaszewski/SafExample,我与文档本身的交互都在此类https://github.com/scottTomaszewski/SafExample/blob/master/app/src/main中/java/com/example/safexample/SafDocument.java

重命名通过Google Drive的SAF界面获取的Uri时出现的错误是这样的(更易于阅读的版本:https : //github.com/scottTomaszewski/SafExample/blob/master/README.md

03-03 14:10:41.529 29069-29069/com.example.safexample W/grok: java.io.FileNotFoundException
        at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:144)
        at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
        at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1103)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:942)
        at android.content.ContentResolver.openInputStream(ContentResolver.java:662)
        at com.example.safexample.SafDocument$2.openStream(SafDocument.java:66)
        at com.google.common.io.ByteSource$AsCharSource.openStream(ByteSource.java:420)
        at com.google.common.io.CharSource.read(CharSource.java:147)
        at com.example.safexample.SafDocument.read(SafDocument.java:73)
        at com.example.safexample.MainActivity.onOptionsItemSelected(MainActivity.java:61)
        at android.app.Activity.onMenuItemSelected(Activity.java:2912)
        at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:404)
        at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:167)
        at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:100)
        at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:100)
        at android.support.v7.app.ToolbarActionBar$2.onMenuItemClick(ToolbarActionBar.java:69)
        at android.support.v7.widget.Toolbar$1.onMenuItemClick(Toolbar.java:169)
        at android.support.v7.widget.ActionMenuView$MenuBuilderCallback.onMenuItemSelected(ActionMenuView.java:760)
        at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:811)
        at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
        at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:958)
        at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:948)
        at android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:618)
        at android.support.v7.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:139)
        at android.view.View.performClick(View.java:5201)
        at android.view.View$PerformClick.run(View.java:21163)
        at android.os.Handler.handleCallback(Handler.java:746)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:148)
        at android.app.ActivityThread.main(ActivityThread.java:5443)
        at …
Run Code Online (Sandbox Code Playgroud)

android google-docs storage-access-framework

5
推荐指数
1
解决办法
659
查看次数

有没有一种直接的方法可以从文件 Uri 中获取父目录的 Uri?

假设一个 Uri 可以是以下之一:

  • 来自存储访问框架的 DocumentFile(即 DocumentFile.getUri())的 Uri。
  • 来自常规文件的 Uri(即 Uri.fromFile(File))

在这两种情况下,它都指目录下的文件。

有没有一种直接的方法来获取其父目录的 Uri 而不尝试两者中的每一个来查看哪个有效?

[编辑]:以下是 SAF 的示例:

乌里:

content://com.android.externalstorage.documents/tree/0000-0000%3Atest/document/0000-0000%3Atest%2Ffoo%2FMovies%2FRR%20parking%20lot%20a%202018_02_22_075101.mp4
Run Code Online (Sandbox Code Playgroud)

获取路径():

/tree/0000-0000:test/document/0000-0000:test/foo/Movies/RR 停车场 a 2018_02_22_075101.mp4

getPathSegments():

0 = "tree"
1 = "0000-0000:test" 
2 = "document"
3 = "0000-0000:test/foo/Movies/RR parking lot a 2018_02_22_075101.mp4"
Run Code Online (Sandbox Code Playgroud)

父文件夹应该是 test/foo/Movies。

以下是常规文件的示例:

乌里:

file:///storage/emulated/0/foo/Movies/RR%20parking%20lot%20a%202018_02_22_081351.mp4

获取路径():

/storage/emulated/0/foo/Movies/RR 停车场 a 2018_02_22_081351.mp4

getPathSegments():

0 = "storage"
1 = "emulated"
2 = "0"
3 = "foo"
4 = "Movies"
5 = "RR parking lot a 2018_02_22_081351.mp4"
Run Code Online (Sandbox Code Playgroud)

android-file storage-access-framework android-storage

5
推荐指数
1
解决办法
880
查看次数

如何在 Android 10 中替换 FileObserver?

android.os.FileObserver需要一个java.io.File功能。但是在 Android 10 中,谷歌限制了对著名的“存储访问框架”的访问,但您的应用程序私有目录除外。由于通过java.io.File中断和渲染访问任何内容FileObserver useless unless you intend to use it in your apps private directory. However I want to get noticed when something is changed in a certain directory on external storage. I would also like to avoid periodically checking for changes.

我尝试使用ContentResolver.registerContentObserver(uri,notifyForDescendants,observer)该方法并遇到了一些问题:

  • Uri到目前为止我插入的每一个都被接受了
  • 这既没有失败,也没有通知,如果Uri不工作
  • 我找不到文件告诉我哪个Uri真正有效

我所做的唯一工作是以下方法:

// works, but returns all changes to the external storage
contentResolver.registerContentObserver(MediaStore.Files.getContentUri("external"), true, contentObserver)
Run Code Online (Sandbox Code Playgroud)

不幸的是,这包括所有外部存储,并且仅Uri在发生更改时返回 Media 。content://media/external/file/67226 …

android fileobserver storage-access-framework android-10.0

5
推荐指数
1
解决办法
1209
查看次数

在 Android 11 上写入许多文件

设想

我正在尝试创建许多文件,作为用户的一项功能。例如,我可能会编写一个应用程序,为他们过去几周听过的每首歌曲创建一个文件(例如,带有歌词的文本文件)。我不能让用户为我生成的每个文件选择目录和文件名,这需要他们几个小时。用户应该可以从其他应用程序访问这些文档。

解决方案(不是真的)

在 Android 11 中,存储访问框架似乎没有用处。我注意到最初有两个有趣的选项:

  1. 创建一个新文件(创建用户与之交互的启动活动以仅保存一个文件),描述here
  2. 授予对目录内容的访问权限(允许读取访问,但无法写入任何文档),描述为here
  3. 注意:如果您面向 Android 10 并请求WRITE_EXTERNAL_STORAGE. 不幸的是,此权限在 Android 11 上被拒绝。因此,我无法使用此处发布的解决方案。
  4. 使用存储访问框架(特别是 ACTION_OPEN_DOCUMENT_TREE)访问目录以访问目录,但我无法将文件写入此目录。(我明白了FileNotFoundException,因为这不是普通路径,而是树路径。)

判决书

我想在我的情况下,我需要沿着“管理存储设备上的所有文件”的路线走下去,如此处所述这涉及使用ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION. 我宁愿不请求读取整个驱动器的权限,也不想让用户进入设置应用程序以授予权限。写入文件不应该需要任何权限...

您将如何为用户保存许多(非媒体)文件?

android storage-access-framework

5
推荐指数
1
解决办法
1万
查看次数

Android Documents Provider 如何使用以及我需要它吗?

我将创建一个用于从 Internet 访问文件的应用程序。我不想实现 UI,而是让它们从其他文件管理器中可见。因此,我选择实现一个文档提供程序。我做到了。在接下来的图片中,您可以看到我打开“文件”应用程序,找到“我的文档提供程序”并可以访问其文件。

在此输入图像描述 在此输入图像描述 在此输入图像描述

然而,它是一个模拟器。当我尝试在物理设备上使用该应用程序时,我发现我无法查看文档提供者。我使用小米红米 Note 5 Pro(android 9.0),没有“文件”应用程序。我测试了 Google Play 中排名前 20 的文件管理器,甚至 MIUI 内置的文件资源管理器,但它们都不能向我显示我的“我的文档提供程序”。

仅当尝试从 Gmail 应用程序附加文档时,我才设法看到我的文档提供程序。但这不是我需要的。

问题:

  1. 我认为 Documents Provider 不是我所需要的,对吗?
  2. 我对吗?这是与使用存储访问框架的其他客户端应用程序(如 Gmail、照片编辑器等)共享文件的一种额外方式,但并不是设计为用作访问文件的主要方法?
  3. 我是否必须使用 UI 和文件资源管理器的完整功能来实现自己的活动,作为用户访问其文件的主要方法,对吗?(就像 Google Drive 应用程序一样 - 它同时具有:自己的 UI,用于管理文件和可从系统选择器访问的文档提供程序)

更新

最后我找到了解决方案。据我了解,标准应用程序“文件”内置于任何设备中。然而,制造商可能“隐藏”这个应用程序并提供一些模拟。MIUI 上有一个“文件管理器”。在许多三星设备以及 Pixel 2 XL 上,它是“Google 文件”应用程序。他们都没有看到文档提供者。只有“文件”应用程序可以。

当您使用 SAF 时,“文件”应用程序仍然可以作为一个选项进行访问(例如使用 ACTION_OPEN_DOCUMENT 启动 Intent)。但您仍然无法独立启动该应用程序。不过,您可以从 Google Play 下载“文件快捷方式”。打开它时,您可以选择一个应将“文件快捷方式”作为快捷方式的应用程序。我选择“文件”应用程序,一切正常(直到我选中“记住我的选择”选项才起作用)。在 Pixel 2 和 Redmi Note 5 上,我都可以看到我的文档提供程序。这是一个好消息,因为我不必浪费时间来创建 UI!

PS 请注意,我上面描述的内容并非来自可靠来源。根据我的观察和理解,我使用了“隐藏应用程序”一词以及在任何设备上预安装“文件”的事实。

PPS 另外,我发现在 Pixel 2 上的“Google 文件”应用程序中还有一种打开“文件”的方法:浏览 -> 其他存储 -> 系统痕迹。由于某种原因,红米 Note 5 上没有“其他存储”按钮。

android storage-access-framework documents-provider

5
推荐指数
1
解决办法
1004
查看次数

从SAF内容URI中提取文件名

你好我的伙伴stackoverflows,

我正在编写一个应用程序,我在其中实现了一个处理共享意图的Activity.到目前为止它工作正常但在测试期间我遇到了Quickoffice(Android 4.4,KitKat)的问题,因为它返回一个URI,我无法从中获取文件名.我还尝试与其他应用程序共享,如Dropbox,它在那里工作.

我从Qickoffice应用程序获得的确切URI:

content://com.quickoffice.android.quickcommon.FileContentProvider/5cmeDeeatcdv8IFyu-bEr2w1jSHrvPmCzXGb_VvZulMBErE5Tmfd_5P5kckE68LaEYDVSp3q5r19%0A4sOkpYCEM_VqK6Y%3D%0A
Run Code Online (Sandbox Code Playgroud)

这是我先使用的代码:

public String getRealPathFromURI(Uri contentUri) {
    Cursor cursor = null;
    try {
        String[] proj = {MediaStore.Images.Media.DATA}; // = "_data"
        ContentResolver cr = getContentResolver(); 
        cursor = cr.query(contentUri, proj, null, null, null); // <--EXCEPTION

        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    } catch (Exception exception) {
        Log.d("clixend", "Exception: " + exception);
        Toast.makeText(this, "Exception: " + exception, Toast.LENGTH_LONG).show();
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误的地方:

09-23 16:54:17.664  32331-32341/? E/DatabaseUtils? Writing …
Run Code Online (Sandbox Code Playgroud)

java android storage-access-framework

4
推荐指数
1
解决办法
671
查看次数

对子目录 DocumentFile 对象的 Uri 调用 DocumentFile.fromTreeUri() 时出现意外行为

如果我调用 DocumentFile.fromTreeUri()主目录并列出其内容,它会DocumentFile按预期返回与目录中包含的文件夹和文件相关的所有对象,但是当我尝试调用DocumentFile.fromTreeUri()其中Uri的文件夹之一时DocumentFile,而不是返回所有DocumentFile相关对象按预期返回到子目录中包含的文件夹和文件,它实际上返回与其根目录上的调用完全相同的内容Uri

例如,我有一个包含 3 个文件的下载文件夹和一个包含 2 个文件的子文件夹,其结构如下

Download
-->file1
-->file2
-->file3
-->Subfolder
----->subfile1
----->subfile2
Run Code Online (Sandbox Code Playgroud)

如果我调用 通过意图回调选择的DocumentFile.fromTreeUri().listFiles()下载文件夹 它会返回我Uricontent://com.android.externalstorage.documents/tree/primary%3ADownload

-->file1
-->file2
-->file3
-->Subfolder
Run Code Online (Sandbox Code Playgroud)

到目前为止一切都很好,现在如果我尝试获取Uri与调用DocumentFile相关的对象,我得到的显然看起来是正确的,但是如果我调用它,当我列出它的内容时我会再次得到Subfoldermysub.getUri()content://com.android.externalstorage.documents/tree/primary%3ADownload/document/primary%3ADownload%2FSubfolderDocumentFile.fromTreeUri().listFiles()

-->file1
-->file2
-->file3
-->Subfolder
Run Code Online (Sandbox Code Playgroud)

与主下载文件夹 Uri 上的调用完全相同。如果我直接打电话,也会发生同样的情况mysub.listFiles()。这看起来是一个完全不合逻辑的设计。

我怎样才能真正从与其父DocumentFile文件夹相关的对象中正确获取与子文件夹相关的对象DocumentFile,以便子文件夹DocumentFile可以列出其内容而不是其父文件夹的内容?

android storage-access-framework android-10.0 android-11

4
推荐指数
1
解决办法
1408
查看次数