Android SAF:来自 DocumentsContract.getTreeDocumentId 的文件夹(在磁盘上)的 URI 与先前授权的 URI 具有不同的形式
Android SAF: Uri of a folder (on disk) from DocumentsContract.getTreeDocumentId has different form, in respect to the previously authorized one
我正在为我的应用程序测试存储访问框架。
在我的应用程序中,用户选择一个文件夹作为应用程序本身文件系统的访问点。
使用此方法:
static public void openPickerForFolderSelection(Activity activity, int requestCode)
{
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); //useful?
activity.startActivityForResult(intent, requestCode);
}
在 onActivityResult 方法中调用了以下代码:
static public String takePermanentReadWritePermissions(Activity activity, Intent data)
{
int takeFlags = data.getFlags()
&
(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
ContentResolver resolver = activity.getContentResolver();
resolver.takePersistableUriPermission(data.getData(),takeFlags);
return data.getData().toString();
}
例如,Uri 是:
content://com.android.providers.downloads.documents/tree/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Faccesspoint1
其实这个Uri恰好是通过这个方法检测到授权的:
static boolean arePermissionsGranted(Activity activity, String uriString)
{
ContentResolver resolver = activity.getContentResolver();
List<UriPermission> list = resolver.getPersistedUriPermissions();
for (int i = 0; i < list.size(); i++){
Log.d("compare",uriString+" "+Uri.decode(list.get(i).getUri().toString())+" "+String.valueOf(list.get(i).isWritePermission())+" "+String.valueOf(list.get(i).isReadPermission()));
if(((Uri.decode(list.get(i).getUri().toString())).equals( uriString)) && list.get(i).isWritePermission()&& list.get(i).isReadPermission()){
return true;
}
然后,当需要在所选文件夹中创建新文件夹时,将调用以下方法,但在尝试创建文件夹之前出现错误:
static public boolean createFolderInFolder(Activity activity,String parentFolderUriString,String folderName)
{
boolean result=false;
ContentResolver contentResolver;
Uri parentFolderUri=null;
String id=DocumentsContract.getTreeDocumentId(Uri.parse(parentFolderUriString));
Log.d("id ",id);
parentFolderUri= DocumentsContract.buildTreeDocumentUri(PROVIDER_AUTHORITY,id);
try {
//error here
DocumentsContract.createDocument(contentResolver,parentFolderUri,DocumentsContract.Document.MIME_TYPE_DIR,folderName);
result=true;
} catch (FileNotFoundException e) {
result =false;
}
return result;
}
我得到:
java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.docs.storagebackend.StorageBackendContentProvider from ProcessRecord{1426e0c 13943:com.example.app/u0a439} (pid=13943, uid=10439) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
(注意使用
parentFolderUri=Uri.parse(parentFolderUriString);
直接导致"invalid Uri"错误)
事实是权限授予的 Uri 不同于 DocumentsContract.getTreeDocumentId(Uri.parse(parentFolderUriString));
这是日志:
D/compare: content://com.android.providers.downloads.documents/tree/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Faccesspoint1 content://com.android.providers.downloads.documents/tree/raw:/storage/emulated/0/Download/accesspoint1 true true
确实id值是:
raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Faccesspoint1
导致最终 Uri 值
content://com.google.android.apps.docs.storage/tree/raw%253A%252Fstorage%252Femulated%252F0%252FDownload%252Faccesspoint1
因此,如果我不希望发出任何安全异常(权限被拒绝),那么我似乎也应该在这个不同版本的 Uri 上请求权限?但是似乎不可能在不同的形式 uri 上获得权限。
如何解决?
题外话:您不需要 intent.addFlags()
在“openPickerForFolderSelection
”方法中。
现在谈谈你的主题 - 我认为你只需要使用 DocumentContract.buildDocumentUriUsingTree() 来获取你需要的 uri(当我尝试以这种方式创建 file/directory 时至少这对我有用),所以它会是这样的:
Uri oldParentUri = Uri.parse(parentFolderUriString);
String id = DocumentsContract.getTreeDocumentId(oldParentUri );
Uri parentFolderUri= DocumentsContract.buildChildDocumentsUriUsingTree(oldParentUri , id);
另一方面,我建议您使用 DocumentFile API - imo 更容易。您的“createFolderInFolder
”将如下所示:
Uri parentFolderUri = Uri.parse(parentFolderUriString);
DocumentFile parentFolder = DocumentFile.fromTreeUri(context, parentFolderUri);
parentFolder.createDirectory(folderName);
然后您可以轻松获取创建的文件夹 uri 并从那里做一些事情。
我正在为我的应用程序测试存储访问框架。
在我的应用程序中,用户选择一个文件夹作为应用程序本身文件系统的访问点。
使用此方法:
static public void openPickerForFolderSelection(Activity activity, int requestCode)
{
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); //useful?
activity.startActivityForResult(intent, requestCode);
}
在 onActivityResult 方法中调用了以下代码:
static public String takePermanentReadWritePermissions(Activity activity, Intent data)
{
int takeFlags = data.getFlags()
&
(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
ContentResolver resolver = activity.getContentResolver();
resolver.takePersistableUriPermission(data.getData(),takeFlags);
return data.getData().toString();
}
例如,Uri 是:
content://com.android.providers.downloads.documents/tree/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Faccesspoint1
其实这个Uri恰好是通过这个方法检测到授权的:
static boolean arePermissionsGranted(Activity activity, String uriString)
{
ContentResolver resolver = activity.getContentResolver();
List<UriPermission> list = resolver.getPersistedUriPermissions();
for (int i = 0; i < list.size(); i++){
Log.d("compare",uriString+" "+Uri.decode(list.get(i).getUri().toString())+" "+String.valueOf(list.get(i).isWritePermission())+" "+String.valueOf(list.get(i).isReadPermission()));
if(((Uri.decode(list.get(i).getUri().toString())).equals( uriString)) && list.get(i).isWritePermission()&& list.get(i).isReadPermission()){
return true;
}
然后,当需要在所选文件夹中创建新文件夹时,将调用以下方法,但在尝试创建文件夹之前出现错误:
static public boolean createFolderInFolder(Activity activity,String parentFolderUriString,String folderName)
{
boolean result=false;
ContentResolver contentResolver;
Uri parentFolderUri=null;
String id=DocumentsContract.getTreeDocumentId(Uri.parse(parentFolderUriString));
Log.d("id ",id);
parentFolderUri= DocumentsContract.buildTreeDocumentUri(PROVIDER_AUTHORITY,id);
try {
//error here
DocumentsContract.createDocument(contentResolver,parentFolderUri,DocumentsContract.Document.MIME_TYPE_DIR,folderName);
result=true;
} catch (FileNotFoundException e) {
result =false;
}
return result;
}
我得到:
java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.docs.storagebackend.StorageBackendContentProvider from ProcessRecord{1426e0c 13943:com.example.app/u0a439} (pid=13943, uid=10439) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
(注意使用
parentFolderUri=Uri.parse(parentFolderUriString);
直接导致"invalid Uri"错误)
事实是权限授予的 Uri 不同于 DocumentsContract.getTreeDocumentId(Uri.parse(parentFolderUriString));
这是日志:
D/compare: content://com.android.providers.downloads.documents/tree/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Faccesspoint1 content://com.android.providers.downloads.documents/tree/raw:/storage/emulated/0/Download/accesspoint1 true true
确实id值是:
raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Faccesspoint1
导致最终 Uri 值
content://com.google.android.apps.docs.storage/tree/raw%253A%252Fstorage%252Femulated%252F0%252FDownload%252Faccesspoint1
因此,如果我不希望发出任何安全异常(权限被拒绝),那么我似乎也应该在这个不同版本的 Uri 上请求权限?但是似乎不可能在不同的形式 uri 上获得权限。
如何解决?
题外话:您不需要 intent.addFlags()
在“openPickerForFolderSelection
”方法中。
现在谈谈你的主题 - 我认为你只需要使用 DocumentContract.buildDocumentUriUsingTree() 来获取你需要的 uri(当我尝试以这种方式创建 file/directory 时至少这对我有用),所以它会是这样的:
Uri oldParentUri = Uri.parse(parentFolderUriString);
String id = DocumentsContract.getTreeDocumentId(oldParentUri );
Uri parentFolderUri= DocumentsContract.buildChildDocumentsUriUsingTree(oldParentUri , id);
另一方面,我建议您使用 DocumentFile API - imo 更容易。您的“createFolderInFolder
”将如下所示:
Uri parentFolderUri = Uri.parse(parentFolderUriString);
DocumentFile parentFolder = DocumentFile.fromTreeUri(context, parentFolderUri);
parentFolder.createDirectory(folderName);
然后您可以轻松获取创建的文件夹 uri 并从那里做一些事情。