Как обновить метаданные аудиофайла в магазине мультимедиа Android Q?

Обновление метаданных аудиофайла в хранилище мультимедиа не работает в ОС Android Q, оно работает во всех других ОС.

Я использую поставщика контента с uri, указанным как MediaStore.Audio.Media.EXTERNAL_CONTENT_URI. Он отлично работает на всех устройствах ниже Android Q. Ниже приведен код, который я использую для обновления метаданных трека.

ContentValues cv = new ContentValues();
ContentResolver resolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;

cv.put(MediaStore.Audio.Media.TITLE, newTitle);
cv.put(MediaStore.Audio.Media.ALBUM, newAlbumName);
cv.put(MediaStore.Audio.Media.ARTIST, newArtistName);

int rowsUpdated = resolver.update(uri, cv, 
MediaStore.Audio.Media._ID + " = ? ", new String[]{audioId});

Для устройства Android Q rowUpdated всегда равен 0 без исключения. Как другой музыкальный проигрыватель обновляет метаданные треков в Android Q?




Ответы (3)


Наконец, потребовалось время, но я понял это.

Во-первых, вам нужно получить доступ к файлу. Здесь вы можете прочитать о это

Затем я обнаружил, что для обновления полей заголовка или исполнителя (возможно, других, я их не тестировал) вам нужно установить значение столбца MediaStore.Audio.Media.IS_PENDING равным 1. Вот так:

    val id = //Your audio file id

    val values = ContentValues()
    values.put(MediaStore.Audio.Media.IS_PENDING, 1)

    val uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
    contentResolver.update(uri, values, null, null)

А затем вы можете редактировать поля, которые вам нужны. Также, чтобы завершить процесс обновления, снова установите MediaStore.Audio.Media.IS_PENDING в 0:

    val id = //Your audio file id
    val title = //New title
    val artist = //New artist

    val values = ContentValues()
    values.put(MediaStore.Audio.Media.IS_PENDING, 0)
    values.put(MediaStore.Audio.Media.TITLE, title)
    values.put(MediaStore.Audio.Media.ARTIST, artist)

    val uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
    contentResolver.update(uri, values, null, null)

Итак, в одной функции это будет выглядеть так:

    @RequiresApi(value = android.os.Build.VERSION_CODES.Q)
    fun updateMetadata(contentResolver: ContentResolver, id: Long, title: String, artist: String) {
        val uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
        val values = ContentValues()

        values.put(MediaStore.Audio.Media.IS_PENDING, 1)
        contentResolver.update(uri, values, null, null)

        values.clear()
        values.put(MediaStore.Audio.Media.IS_PENDING, 0)
        values.put(MediaStore.Audio.Media.TITLE, title)
        values.put(MediaStore.Audio.Media.ARTIST, artist)
        contentResolver.update(uri, values, null, null)
    }

Он написан на Kotlin, но я думаю, вы поймете, как это сделать на java.

UPD

Обновляя MediaStore, вы не обновляете настоящий файл ни в одной версии Android. Это означает, что если файл будет обновлен (например, переименован) и / или отсканирован MediaScannerConnection, ваши изменения будут потеряны. Этот ответ правильный.

person KingEnderBrine    schedule 10.02.2020
comment
Я не могу найти столбец IS_PENDING в MediaStore.Audio.Media. - person Velu; 31.03.2020
comment
@Velu: Убедитесь, что ваш compileSdkVersion установлен на 29 или выше. - person CommonsWare; 04.04.2020
comment
Ваш ответ действительно правильный. Хотя вы, вероятно, не меняете некоторые метаданные или данные, когда не записываете в файл, обновление через ContentResolver может коснуться реального файла в Android Q, если, например, изменяется DISPLAY_NAME. Это считается столбцом изменения места размещения и не требует PENDING статуса. Однако для большинства столбцов требуется статус PENDING. См. sMutableColumns реализации MediaProvider для всех столбцов, для которых не требуется PENDING статус - person CodeRed; 11.04.2020
comment
Это сработало для меня. И если я откажусь от ограниченного хранилища, мне не нужно было получать доступ к файлу с помощью RecoverableSecurityException, на самом деле исключение не генерируется, оно сработало без этого. Однако то же самое не работает с альбомом, stackoverflow.com/q/62277997/4732846 - person Velu; 09.06.2020
comment
не работает с несобственными (не собственными) аудиофайлами в API ›= Q - person Farzad; 22.06.2021

Используя Android Q и выше, вы должны сначала получить файл

i.e

resolver.openInputStream(uri)?.use { stream -> outputFile.copyInputStreamToFile(stream) }
return outputFile.absolutePath

Вспомогательная функция

private fun File.copyInputStreamToFile(inputStream: InputStream?) {
    this.outputStream().use { fileOut ->
        inputStream?.copyTo(fileOut)
    }
}

Затем измените метаданные с помощью третьей стороны, я использую J Audio Tagger

Затем перезапишите старый файл

// From https://developer.android.com/reference/android/content/ContentProvider
// String: Access mode for the file. May be
// "r" for read-only access,
// "w" for write-only access (erasing whatever data is currently in the file),
// "wa" for write-only access to append to any existing data,
// "rw" for read and write access on any existing data, and
// "rwt" for read and write access that truncates any existing file. This value must never be null.

mContext.application.contentResolver.openOutputStream(uri, "w")?.use { stream ->
            stream.write(file.readBytes())
        }

Это отлично работает, когда файл был создан вашим приложением

person Andy Cass    schedule 04.02.2020
comment
Если вы поделитесь информацией: версия J Audio Tagger, targetSdkVersion, compileSdkVersion? - person mustafiz012; 31.03.2020
comment
org: jaudiotagger: 2.0.3, compileSdkVersion 29, targetSdkVersion 29 - person Andy Cass; 10.05.2020

Я обновляю метаданные в MediaStore через ContentResolver, но это больше не работает с Android Q (API 29). Следующий код выдает предупреждение, а описание не обновляется:

ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DESCRIPTION, "Some text");

res = getContext().getContentResolver().update(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        values,
        MediaStore.Images.Media._ID + "= ?", new String[]{sImageId});

android.process.media W / MediaProvider: игнорирование изменения описания из com.example.android.someapp.app

В этом сообщении на Medium описывается как Google изменил API для доступа к файлам и их обновления, а как насчет обновления только метаданных? Предупреждение, похоже, говорит мне, что Google больше не хочет разрешать сторонним приложениям использовать MediaStore, и я также обнаружил, откуда приходит предупреждение: https://android.googlesource.com/platform/packages/providers/MediaProvider/+/master/src/com/android/providers/media/MediaProvider.java#2960.

person joakimk    schedule 22.02.2020
comment
Также см. stackoverflow.com/questions/60366394 / - person joakimk; 12.03.2020