Reputation: 12571
My app needs to list all music on the device, including: Song title, artist, album, genre and more.
The way I know of to get the genre for a song, is to create another Cursor
with the song ID like this:
Uri uri = MediaStore.Audio.Genres.getContentUriForAudioId("external", (int) id);
Cursor genresCursor = mContentResolver.query(uri, genresProjection, null, null, null);
However, if I do this within the Cursor
that lists all songs, the load time will increase a lot. (for the amount of songs on my phone, from 0.5s to about 3s).
This is my code:
// If you change this to `false`, it will load a lot quicker.
private static final boolean LOAD_GENRE = true;
private static String[] mediaProjection = {
MediaStore.Audio.AudioColumns._ID,
MediaStore.Audio.AudioColumns.DATA,
MediaStore.Audio.AudioColumns.TITLE,
MediaStore.Audio.AudioColumns.ARTIST,
MediaStore.Audio.AudioColumns.ALBUM,
MediaStore.Audio.AudioColumns.ALBUM_ID,
MediaStore.Audio.AudioColumns.DURATION
};
private static String[] genresProjection = {
MediaStore.Audio.GenresColumns.NAME
};
private ArrayList<MediaMetadataCompat> getAllMusic() {
Uri uriAudio = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = mContentResolver.query(uriAudio, mediaProjection, null, null, null);
if (cursor != null) {
int count = cursor.getCount();
if (count > 0) {
ArrayList<MediaMetadataCompat> audioFiles = new ArrayList<>();
while (cursor.moveToNext()) {
MediaMetadataCompat song = buildFromCursor(cursor);
if (song != null) {
audioFiles.add(song);
}
}
cursor.close();
return audioFiles;
}
}
return new ArrayList<>();
}
private MediaMetadataCompat buildFromCursor(Cursor cursor, boolean loadGenre) {
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.AudioColumns._ID));
String data = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA));
String title = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.AudioColumns.TITLE));
String artist = ...;
String album = ...;
long albumId = ...;
long duration = ...;
//File file = new File(data);
//if (!file.exists()) {
// Log.e(TAG, "Skipping song because it does not exist: " + title);
// return null;
//}
Uri uri = MediaStore.Audio.Genres.getContentUriForAudioId("external", (int) id);
if (LOAD_GENRE) {
String genre = null;
Cursor genresCursor = mContentResolver.query(uri, genresProjection, null, null, null);
if (genresCursor != null && genresCursor.moveToFirst()) {
int genreColumnIndex = genresCursor.getColumnIndex(MediaStore.Audio.GenresColumns.NAME);
do {
String genreColumn = genresCursor.getString(genreColumnIndex);
if (genre == null) {
genre = genreColumn;
} else {
genre += " " + genre;
}
} while (genresCursor.moveToNext());
genresCursor.close();
}
}
// Workaround to avoid ANOTHER cursor to load album art.
Uri albumArtUri = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), albumId);
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, String.valueOf(id))
.put...
.put...;
return builder.build();
}
Is there any way I can improve this code, to avoid a nested Cursor
? I already found a workaround for the album art. Is it possible to combine them in some way? Will a CursorJoiner
work for this? The problem is that to get the genre, you need to have the media id. Thanks.
Upvotes: 0
Views: 1387
Reputation: 2042
A SO user Suhaib Roomey posted this recently (get genre_id and audio_id from audio_genres_map from mediastore). Perhaps it helps in what you are trying to achieve
String[] genresProjection = {
Audio.Genres.Members.AUDIO_ID,
Audio.Genres.Members.GENRE_ID
};
context.getContentResolver().query(Uri.parse("content://media/external/audio/genres/all/members"), genresProjection, null, null, null);
Upvotes: 1