Scoped Storage: как удалить несколько аудиофайлов через MediaStore? - PullRequest
0 голосов
/ 08 октября 2019

Я пытаюсь удалить аудиофайлы из внешнего хранилища устройства (например, в папке /storage/emulated/0/Music). После анализа образца MediaStore я получил следующее решение для API 28 и более ранних версий:

fun deleteTracks(trackIds: LongArray): Int {
    val whereClause = buildWildcardInClause(trackIds.size) // _id IN (?, ?, ?, ...)
    return resolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, whereClause, Array(trackIds.size) { trackIds[it].toString() })
}

Я заметил, что приведенный выше код удаляет только дорожки из базы данных MediaStore, ноне с устройства (файлы повторно добавляются в MediaStore после перезагрузки). Поэтому я изменил этот код для запроса столбца Media.DATA, а затем использовал эту информацию для удаления связанных файлов. Это работает как задумано.

Но теперь, когда Android Q представил Scoped Storage, Media.DATAAlbums.ALBUM_ART) теперь устарели, поскольку приложение может не иметь доступа к этим файлам. ContentResolver.openFileDescriptor можно использовать только для чтения файлов, но не для их удаления.

Тогда каков рекомендуемый способ удаления треков с Android Q? В примере не показано, как удалить несколько файлов из MediaStore, а MediaStore.Audio.Media работает иначе, чем MediaStore.Images.Media.

1 Ответ

1 голос
/ 11 октября 2019

Есть два способа сделать это в Android 10, который зависит от значения android:requestLegacyExternalStorage в файле AndroidManifest.xml приложения.

Опция 1: Включено хранилище с ограничениями (android:requestLegacyExternalStorage="false")

Если ваше приложение добавило содержимое для начала (с использованием contentResolver.insert), то вышеописанный метод (с использованием contentResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, whereClause, idsArray)) должен работать. В Android 10 этим приложением можно свободно манипулировать любым контентом, передаваемым в Media Store.

Для контента, добавленного пользователем или принадлежащего другому приложению, этот процесс несколько сложнее.

Чтобы удалить отдельный элемент из хранилища мультимедиа, не передавайте универсальный URI хранилища мультимедиа в delete (т. Е. MediaStore.Audio.Media.EXTERNAL_CONTENT_URI), а используйте специальный URI элемента.

context.contentResolver.delete(
    audioContentUri,
    "${MediaStore.Audio.Media._ID} = ?",
    arrayOf(audioContentId)

(Я почти уверен, что в предложении «где» нет необходимости, но для меня это имело смысл:)

На Android 10 это, скорее всего, выдаст RecoverableSecurityException,если ваш targetSdkVersion равен> = 29. Внутри этого исключения находится IntentSender, который может быть запущен вашим Activity, чтобы запросить у пользователя разрешение на его изменение или удаление. (Отправитель находится в recoverableSecurityException.userAction.actionIntent.intentSender)

Поскольку пользователь должен предоставить разрешение для каждого элемента, их невозможно удалить навалом. (Или, возможно, можно запросить разрешение на изменение или удаление всего альбома по одной песне за раз, а затем использовать запрос delete, который вы используете выше. В этот момент ваше приложение получит разрешение на их удаление, и Media Storeбудет делать это все сразу. Но сначала нужно запросить каждую песню.)

Вариант 2: Legacy Storage включен (android:requestLegacyExternalStorage="true")

В этом случаепростой вызов contentResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, whereClause, idsArray) не только удаляет элементы из базы данных, но и удаляет файлы.

Для работы этого приложения должно быть разрешение WRITE_EXTERNAL_STORAGE, но я протестировал код, который у вас есть ввопрос и проверил, что файлы также были удалены.

Итак, для более старых версий API, я думаю, вам нужно использовать contentResolver.delete и отсоединить файл самостоятельно (или отсоединить файл и отправить запрос на него вповторно сканировать с помощью MediaScannerConnection).

Для Android 10+ contentResolver.delete удалит как индекс, так и само содержимое.

...