Android SAF (Storage Access FrameWork): получить конкретный файл Uri из TreeUri

Я беру PersistableUriPermission с внешней SD-карты и сохраняю для дальнейшего использования. Теперь я хочу, чтобы когда пользователь предоставлял мне путь к файлу из списка файлов в моем приложении, я хотел редактировать документ и переименовывать его.

Итак, у меня есть путь к файлу для редактирования.

Мой вопрос заключается в том, как мне получить Uri этого файла из моего TreeUri для редактирования файла.

1 ответ

Доступ к файлам Sd-Card

использование DOCUMENT_TREE диалог для получения SD-карты Uri,

Сообщите пользователю о том, как выбрать sd-card в диалоге. (с картинками или gif-анимацией)

// call for document tree dialog
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT_TREE);

На onActivityResult у вас будет выбранный каталог Uri, (SdCardUri)

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case REQUEST_CODE_OPEN_DOCUMENT_TREE:
            if (resultCode == Activity.RESULT_OK) {
                sdCardUri = data.getData();
             }
             break;
     }
  }

Теперь нужно проверить, если пользователь,

а. выбрал SD-карту

б. выберите SD-карту, на которой находится наш файл (на некоторых устройствах может быть несколько SD-карт).


Мы проверяем и a, и b, находя файл по иерархии, от sd root до нашего файла. Если файл найден, выполняются условия a и b.

//First we get `DocumentFile` from the `TreeUri` which in our case is `sdCardUri`.
DocumentFile documentFile = DocumentFile.fromTreeUri(this, sdCardUri);

//Then we split file path into array of strings.
//ex: parts:{"", "storage", "extSdCard", "MyFolder", "MyFolder", "myImage.jpg"}
// There is a reason for having two similar names "MyFolder" in 
//my exmple file path to show you similarity in names in a path will not 
//distract our hiarchy search that is provided below.
String[] parts = (file.getPath()).split("\\/");

// findFile method will search documentFile for the first file 
// with the expected `DisplayName`

// We skip first three items because we are already on it.(sdCardUri = /storage/extSdCard)
for (int i = 3; i < parts.length; i++) {
    if (documentFile != null) {
        documentFile = documentFile.findFile(parts[i]);
    }
  }

if (documentFile == null) {

    // File not found on tree search
    // User selected a wrong directory as the sd-card
    // Here must inform user about how to get the correct sd-card
    // and invoke file chooser dialog again.

 } else {

    // File found on sd-card and it is a correct sd-card directory
    // save this path as a root for sd-card on your database(SQLite, XML, txt,...)

    // Now do whatever you like to do with documentFile.
    // Here I do deletion to provide an example.


    if (documentFile.delete()) {// if delete file succeed 
        // Remove information related to your media from ContentResolver,
        // which documentFile.delete() didn't do the trick for me. 
        // Must do it otherwise you will end up with showing an empty
        // ImageView if you are getting your URLs from MediaStore.
        // 
        Uri mediaContentUri = ContentUris.withAppendedId(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                longMediaId);
        getContentResolver().delete(mediaContentUri , null, null);
    }


 }

Замечания:

Вы должны предоставить разрешение на доступ к внешнему хранилищу внутри манифеста и для os>=Marshmallow внутри приложения. /questions/1728553/oshibka-razresheniya-polzovatelya-android-m-prochitajte-i-pishite/1728564#1728564


Редактировать файлы SD-карты

Для редактирования существующего изображения на вашей SD-карте вам не нужны никакие из перечисленных выше шагов, если вы хотите вызвать другое приложение, чтобы сделать это для вас.

Здесь мы вызываем все действия (из всех установленных приложений) с возможностью редактирования изображений. (Программисты отмечают свои приложения в манифесте за его возможности обеспечивать доступность из других приложений (действий)).

в событии clickBitton:

String mimeType = getMimeTypeFromMediaContentUri(mediaContentUri);
startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_EDIT).setDataAndType(mediaContentUri, mimeType).putExtra(Intent.EXTRA_STREAM, mediaContentUri).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION), "Edit"), REQUEST_CODE_SHARE_EDIT_SET_AS_INTENT);

и вот как получить mimeType:

public String getMimeTypeFromMediaContentUri(Uri uri) {
    String mimeType;
    if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
        ContentResolver cr = getContentResolver();
        mimeType = cr.getType(uri);
    } else {
        String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
                .toString());
        mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
                fileExtension.toLowerCase());
    }
    return mimeType;
}

Замечания:

На Android KitKat(4.4) не просите пользователя выбрать SD-карту, потому что на этой версии Android DocumentProviderнеприменимо, поэтому у нас нет шансов получить доступ к SD-карте при таком подходе. Посмотрите на уровень API для DocumentProvider https://developer.android.com/reference/android/provider/DocumentsProvider.html
Я не смог найти ничего, что работает на Android KitKat(4.4). Если вы нашли что-нибудь полезное с KitKat, пожалуйста, поделитесь с нами.

В версиях ниже KitKat доступ к SD-карте уже предоставляется ОС.

Другие вопросы по тегам