Android SAF: Uri папки (на диске) из DocumentsContract.getTreeDocumentId имеет другую форму по сравнению с ранее авторизованной

Я тестирую Storage Access Framework для своего приложения.

В моем приложении пользователь выбирает папку в качестве точки доступа к файловой системе для самого приложения.

Этот метод используется:

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

Действительно, значение идентификатора:

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.

Как это решить?


person P5music    schedule 20.08.2019    source источник


Ответы (1)


Оффтоп: вам не нужно intent.addFlags() в методе 'openPickerForFolderSelection'.

Теперь по вашей теме - я думаю, вам просто нужно использовать DocumentContract.buildDocumentUriUsingTree(), чтобы получить нужный вам uri (по крайней мере, это сработало для меня, когда я пытался создать файл/каталог таким образом), так что это будет что-то вроде:

Uri oldParentUri = Uri.parse(parentFolderUriString);
String id = DocumentsContract.getTreeDocumentId(oldParentUri );
Uri parentFolderUri= DocumentsContract.buildChildDocumentsUriUsingTree(oldParentUri , id);

С другой стороны, я предлагаю вам использовать API DocumentFile — это намного проще имхо. тогда ваш 'createFolderInFolder' будет выглядеть так:

Uri parentFolderUri = Uri.parse(parentFolderUriString);
DocumentFile parentFolder = DocumentFile.fromTreeUri(context, parentFolderUri);
parentFolder.createDirectory(folderName);

Затем вы можете легко получить созданную папку uri и что-то сделать оттуда.

person MJegorovas    schedule 13.09.2019