How to update metadata of audio file in Android Q media store?
C

3

7

Updating metadata of audio file in media store is not working in Android Q OS, it works in all other OS.

I am using content provider with uri specified as MediaStore.Audio.Media.EXTERNAL_CONTENT_URI. It is working fine in all below Android Q device. Below is the code that I am using to update track metadata.

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});

For Android Q device, rowsUpdated is always 0 with no exception. How are other music player updating tracks metadata in Android Q ?

Cripps answered 5/9, 2019 at 11:4 Comment(0)
B
12

Finally, it took some time but I figured that out.

First, you need to obtain access to file. Here you can read about that

Next, I found out that to update title or artist fields (maybe others to, I didn't test them) you need to set column MediaStore.Audio.Media.IS_PENDING value to 1. Like that:

    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)

And then you can edit fields that you need. Also to end the update process set MediaStore.Audio.Media.IS_PENDING to 0 again:

    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)

So in one function, it would look like this:

    @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)
    }

It's written in Kotlin but I think you will figure out how to do that in java.

UPDATE

By updating MediaStore you don't updating real file at any android version. That means, if a file would be updated (for example: renamed) and/or scanned by MediaScannerConnection your changes will be lost. This answer is right.

Blessed answered 10/2, 2020 at 14:40 Comment(5)
I am not able to find IS_PENDING column in MediaStore.Audio.Media.Cripps
@Velu: Make sure that your compileSdkVersion is set to 29 or higher.Fleck
Your answer is actually right. Although you probably don't change some meta-data or data when not writing to the file, updating via ContentResolver might touch the real file in Android Q if for example the DISPLAY_NAME changes. This is considered a placement changing column and does not require PENDING status. Most columns though require PENDING status. See MediaProvider implementation's sMutableColumns for all columns that do not require PENDING statusLexeme
This worked for me. And if I opt-out of scoped storage then I didn't have to get access to the file using RecoverableSecurityException, in fact the exception doesn't get thrown, it worked with out that. However the same things doesn't work for album, https://mcmap.net/q/1476230/-java-lang-unsupportedoperationexception-albums-cannot-be-directly-modified/4732846Cripps
not working on not self (not own) audio files in API >= QWelldressed
D
5

Using Android Q and beyond you have to first get the file

i.e

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

Helper Function

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

Then alter the metadata via a third party, I use J Audio Tagger

Then over write the old file

// 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())
        }

This works fine when the file was created by your app

Duotone answered 4/2, 2020 at 23:21 Comment(2)
If you please share some info: The version of J Audio Tagger, targetSdkVersion, compileSdkVersion?Dyestuff
org:jaudiotagger:2.0.3, compileSdkVersion 29, targetSdkVersion 29Duotone
A
2

I've been updating meta data in the MediaStore through a ContentResolver, but this no longer works with Android Q (API 29). The following code gives me a warning, and the description is not updated:

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: Ignoring mutation of description from com.example.android.someapp.app

This Medium post describes how Google has changed the API for accessing and updating files, but what about updating just the meta data? The warning seems to tell me Google no longer wants to allow third party apps to use the MediaStore, and I also found where the warning comes from:  https://android.googlesource.com/platform/packages/providers/MediaProvider/+/master/src/com/android/providers/media/MediaProvider.java#2960

Aimeeaimil answered 22/2, 2020 at 23:1 Comment(1)
Please also see #60366894Aimeeaimil

© 2022 - 2024 — McMap. All rights reserved.