Getting album art for all music files - android

I am trying to fetch the album art of all the songs present on the phone. I am using MediaStore to fetch all the songs title,artist etc. How should I fetch album art ? I tried using MediaMetaDataRetriever but getting confused how to use it for multiple files. Can anyone please tweak this code?
Activity class:
public void getSongList() {
// retrieve song info
ContentResolver musicResolver = getContentResolver();
Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicUri, null, null, null,
null);
metaRetriver.setDataSource(MainActivity.this,musicUri); // now how to loop over this
if (musicCursor != null && musicCursor.moveToFirst()) {
// get columns
int titleColumn = musicCursor.getColumnIndex(MediaColumns.TITLE);
int idColumn = musicCursor.getColumnIndex(BaseColumns._ID);
int artistColumn = musicCursor.getColumnIndex(AudioColumns.ARTIST);
// add songs to list
do {
long thisId = musicCursor.getLong(idColumn);
String thisTitle = musicCursor.getString(titleColumn);
String thisArtist = musicCursor.getString(artistColumn);
songList.add(new Song(thisId, thisTitle, thisArtist));
} while (musicCursor.moveToNext());
}

Once you have the album id, which you can get from that same cursor, you can query a different URI for the cover art path. see below for an example of approximately how I do it:
private static String getCoverArtPath(Context context, long androidAlbumId) {
String path = null;
Cursor c = context.getContentResolver().query(
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Audio.Albums.ALBUM_ART},
MediaStore.Audio.Albums._ID + "=?",
new String[]{Long.toString(androidAlbumId)},
null);
if (c != null) {
if (c.moveToFirst()) {
path = c.getString(0);
}
c.close();
}
return path;
}
You could get a Map of all album art by id using something like this (untested)
private static Map<Long, String> getCoverArtPaths(Context context) {
String HashMap<Long, String> map = new HashMap<Long, String>();
Cursor c = context.getContentResolver().query(
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Audio.Albums._ID, MediaStore.Audio.Albums.ALBUM_ART},
null,
null,
null);
if (c != null) {
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
map.add(c.getLong(0), c.getString(1));
}
c.close();
}
// returns a mapping of Album ID => art file path
return map;
}

I got it working like this. Hope it helps someone :)
public void getSongList() {
// retrieve song info
ContentResolver musicResolver = getContentResolver();
Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicUri, null, null, null,
null);
if (musicCursor != null && musicCursor.moveToFirst()) {
// get columns
int titleColumn = musicCursor.getColumnIndex(MediaColumns.TITLE);
int idColumn = musicCursor.getColumnIndex(BaseColumns._ID);
int artistColumn = musicCursor.getColumnIndex(AudioColumns.ARTIST);
int column_index = musicCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
// add songs to list
do {
long thisId = musicCursor.getLong(idColumn);
String pathId = musicCursor.getString(column_index);
Log.d(this.getClass().getName(), "path id=" + pathId);
metaRetriver.setDataSource(pathId);
try {
art = metaRetriver.getEmbeddedPicture();
Options opt = new Options();
opt.inSampleSize = 2;
songImage = BitmapFactory .decodeByteArray(art, 0, art.length,opt);
}
catch (Exception e)
{ imgAlbumArt.setBackgroundColor(Color.GRAY);
}
String thisTitle = musicCursor.getString(titleColumn);
String thisArtist = musicCursor.getString(artistColumn);
songList.add(new Song(thisId, thisTitle, thisArtist,songImage));
// if(songImage!=null)
// {
// songImage.recycle();
// }
} while (musicCursor.moveToNext());
}

For Fast rendering
If you are using recylerview, listview to render song list and it is making slow due to getting album art from song path then you can first check if ImageView has already background image set then don't process anything. It will reduce lot of processing and make scrolling fast even if song list is very large. I was facing same problem. I just did same as mentioned and recyclerview rendering with album art became smooth otherwise it was getting stuck during scroll.
My be this will help someone.
My code for recyclerview:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
try {
if (holder instanceof MusicAdapter.MusicViewHolder) {
MusicAdapter.MusicViewHolder vh = (MusicAdapter.MusicViewHolder) holder;
vh.tvTitle.setText(musicList.get(position).title.toString());
vh.tvArtistAndAblum.setText(musicList.get(position).artist.toString() + " | " + musicList.get(position).album.toString());
Drawable background = vh.ivMusicIcon.getBackground();
if(background == null) {
String pathId = musicList.get(position).path;
MediaMetadataRetriever metaRetriver = new MediaMetadataRetriever();
metaRetriver.setDataSource(pathId);
try {
byte[] art = metaRetriver.getEmbeddedPicture();
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inSampleSize = 2;
Bitmap songImage = BitmapFactory.decodeByteArray(art, 0, art.length,opt);
BitmapDrawable ob = new BitmapDrawable(context.getResources(), songImage);
vh.ivMusicIcon.setBackground(ob);
}
catch (Exception e)
{
vh.ivMusicIcon.setImageResource(R.drawable.compact_disc);
}
}
} else if (holder instanceof FooterViewHolder) {
FooterViewHolder vh = (FooterViewHolder) holder;
}
} catch (Exception e) {
e.printStackTrace();
}
}

Related

Android 8.1 lost responsiveness

When I launch my application after upgrading from android 7 to 8.1 I noticed that it lost a lot of responsiveness.
When I'm trying to get songs from device memory I get a lot of errors like:
E/SEC_DRM_PLUGIN_Omafl: OmaPlugin::onOpenDecryptSession(fd)::Drm2IsDrmFileByExtFd::file is NOT DRM by extension
That works much slower than on the Android 7.
Code:
public ArrayList<Song> getSongList() {
MediaMetadataRetriever metaRetriver = new MediaMetadataRetriever();
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
ArrayList<Song> songList = new ArrayList<>();
ContentResolver musicResolver = context.getContentResolver();
Uri musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicUri, null, selection, null, null);
if (musicCursor != null && musicCursor.moveToFirst()) {
int titleColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int idColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media._ID);
int artistColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
int durationColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.DURATION);
int column_index = musicCursor.getColumnIndex(MediaStore.Audio.Media.DATA);
do {
long id = musicCursor.getLong(idColumn);
String path = musicCursor.getString(column_index);
String title = musicCursor.getString(titleColumn);
String artist = musicCursor.getString(artistColumn);
String duration = musicCursor.getString(durationColumn);
try {
metaRetriver.setDataSource(path);
songList.add(new Song(id, title, artist, path, duration));
}catch (Exception exc){}
} while (musicCursor.moveToNext());
}
return songList;
}
I had to delete the one line of code that was causing the error when trying to get every single song from memory:
mediaMetadataRetriver.setDataSource(pathOfSong);
Hope it will help you.

IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it

I am new to android. I am implementing music player using service. Here is the code in which error is focused. Can anyone help me out to solve this error?
public void getSong() {
ContentResolver contentResolver = getContentResolver();
Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(musicUri, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
do {
long thisId = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media._ID));
String thisTitle = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
String thisArtist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
int albumId = cursor.getColumnIndex(android.provider.MediaStore.Audio.Albums.ALBUM_ART);
String thisArt = cursor.getString(albumId);
musicArrayList.add(new Music(thisId, thisTitle, thisArtist, thisArt));
} while (cursor.moveToNext());
}
}
Make sure table in which you are fetching the data having same column name
Check out this http://www.rjkfw.com/android/s3035.html
Check null before extracting data.
ListView audioListView = (ListView) findViewById(R.id.songView);
ArrayList<string> audioList = new ArrayList<>();
String[] proj = { MediaStore.Audio.Media._ID,MediaStore.Audio.Media.DISPLAY_NAME };// Can include more data for more details and check it.
Cursor audioCursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, proj, null, null, null);
if(audioCursor != null){
if(audioCursor.moveToFirst()){
do{
int audioIndex = audioCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME);
audioList.add(audioCursor.getString(audioIndex));
}while(audioCursor.moveToNext());
}
}
audioCursor.close();
ArrayAdapter<string> adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,android.R.id.text1, audioList);
audioListView.setAdapter(adapter);
Error Solved, Since I was looking for album of particular song, null value is passed in cursor. so I changed my code of cursor as below:
public void getSong() {
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM},
MediaStore.Audio.Media.IS_MUSIC + "=1",
null,
null);
if (cursor != null && cursor.moveToFirst()) {
do {
long thisId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
String thisTitle = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
String thisArtist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
String thisImage = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
songList.add(new Music(thisId, thisTitle, thisArtist, thisImage));
}while (cursor.moveToNext());
}
And I have tried by calling getsongImage(URI) method in playSong() in service class for getting Album as below:
public void getSongImage(Uri uri) {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(this,uri);
byte [] data = mmr.getEmbeddedPicture();
if(data==null){
activitySongDetailBinding.imgMusic.setImageResource(R.drawable.img);
}else {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
activitySongDetailBinding.imgMusic.setImageBitmap(bitmap);
}
}
where uri=ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songsArrayList.get(songPosn).getId())

How to get audio path to play audio file?

This is my code that gets list of songs:
List<String> getsonglist() {
List<String> songlist = new ArrayList<>();
ContentResolver contentResolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
// query failed, handle error.
} else if (!cursor.moveToFirst()) {
// no media on the device
} else {
int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
do {
long thisId = cursor.getLong(idColumn);
String thisTitle = cursor.getString(titleColumn);
// ...process entry...
songlist.add(thisTitle);
} while (cursor.moveToNext());
}
return songlist;
}
I want to play that song by giving its path to music player() method.
You can get full path like this:
String fullPath = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
So, Your function would be:
List<String> getsonglist() {
List<String> songlist = new ArrayList<>();
ContentResolver contentResolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
// query failed, handle error.
} else if (!cursor.moveToFirst()) {
// no media on the device
} else {
do {
String fullPath = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
// ...process entry...
Log.e("Full Path : ", fullPath);
songlist.add(fullPath);
} while (cursor.moveToNext());
}
return songlist;
}
Try this one
String fullPath = cursor.getString(cursor.getColumnIndex("_data"));

How to get songs and other media from an Album ID?

I want to get songs and other MEDIA details from Album Id. All I have is Album Id and I tried many solutions but none of them succeed.
My code snippet:
ContentResolver contentResolver = getContentResolver();
Uri mediaUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, albumId);
Log.wtf("SKJDBKJ", mediaUri.toString());
Cursor mediaCursor = contentResolver.query(mediaUri, null, null, null, null);
// if the cursor is null.
if(mediaCursor != null && mediaCursor.moveToFirst())
{
Log.wtf("DSJK", "entered cursor");
//get Columns
int titleColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM);
int idColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media._ID);
int artistColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
// Store the title, id and artist name in Song Array list.
do
{
long thisId = mediaCursor.getLong(idColumn);
String thisTitle = mediaCursor.getString(titleColumn);
String thisArtist = mediaCursor.getString(artistColumn);
Log.wtf("Title", thisTitle);
// Add the info to our array.
songArrayList.add(new Song(thisId, thisTitle, thisArtist));
}
while (mediaCursor.moveToNext());
// For best practices, close the cursor after use.
mediaCursor.close();
}
Log for mediaUri returns path to current album, e.g. : content://media/external/audio/media/41.
Someone tell me how do I do it?
I have done it by myself.You can write simple sqlite query for 'selection' string on contentResolver.query .This example can show you how to get songs by album Id. I think this is best way.
ArrayList<Song> songs = new ArrayList<>();
String selection = "is_music != 0";
if (albumId > 0) {
selection = selection + " and album_id = " + albumId;
}
String[] projection = {
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.ALBUM_ID
};
final String sortOrder = MediaStore.Audio.AudioColumns.TITLE + " COLLATE LOCALIZED ASC";
Cursor cursor = null;
try {
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
cursor = getActivity().getContentResolver().query(uri, projection, selection, null, sortOrder);
if (cursor != null) {
cursor.moveToFirst();
int position = 1;
while (!cursor.isAfterLast()) {
Song song = new Song();
song.setTitle(cursor.getString(0));
song.setDuration(cursor.getLong(4));
song.setArtist(cursor.getString(1));
song.setPath(cursor.getString(2));
song.setPosition(position);
song.setAlbumId(cursor.getLong(6));
cursor.moveToNext();
}
}
} catch (Exception e) {
Log.e("Media", e.toString());
} finally {
if (cursor != null) {
cursor.close();
}
}
I have figured it out myself after a LOT of trial and errors. I don't know if it's the best and safest way to do so, but as far as it's working I am happy.
I changed my code a bit and compared Album IDs. Here's the snippet:
ContentResolver contentResolver = getContentResolver();
Uri mediaUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Log.wtf("SKJDBKJ", mediaUri.toString());
Cursor mediaCursor = contentResolver.query(mediaUri, null, null, null, null);
// if the cursor is null.
if(mediaCursor != null && mediaCursor.moveToFirst())
{
Log.wtf("DSJK", "entered cursor");
//get Columns
int titleColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int idColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media._ID);
int artistColumn = mediaCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
int albumId = mediaCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID);
// Store the title, id and artist name in Song Array list.
do
{
long thisId = mediaCursor.getLong(idColumn);
long thisalbumId = mediaCursor.getLong(albumId);
String thisTitle = mediaCursor.getString(titleColumn);
String thisArtist = mediaCursor.getString(artistColumn);
// Add the info to our array.
if(this.albumId == thisalbumId)
{
Log.wtf("SAME2SAME", String.valueOf(thisalbumId));
Log.wtf("SAME2SAME", String.valueOf(this.albumId));
songArrayList.add(new Song(thisId, thisTitle, thisArtist));
}
}
while (mediaCursor.moveToNext());
// For best practices, close the cursor after use.
mediaCursor.close();
}
I changed:
Albums to Media in MediaStore.Audio.Audio.xxx.
Got the album Id of Media.
Compared that to album Id I receive from bundle extras.
Only add those songs in the arrayList.
I guess this'll be the way for Artists too.
when you put the cursor.movenext function in to the while loop, the first line of
MediaStore database will be passed without saving the data because the function move the cursor and then check so its better to make a value instead of putting the movenext function into the while.
try {
Projection = new String[]{
MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ALBUM
, MediaStore.Audio.Media.ARTIST,MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.SIZE};
Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Projection, null, null, null);
cursor.moveToFirst();
for (int i = 0; i < 5; i++) {
columns[i] = cursor.getColumnIndex(Projection[i]);
}
Music tempMusic = new Music();
while (next) {
tempMusic.setName(cursor.getString(columns[0]));
tempMusic.setAlbum(cursor.getString(columns[1]));
tempMusic.setArtist(cursor.getString(columns[2]));
tempMusic.setUri(cursor.getString(columns[3]));
tempMusic.setSize(cursor.getDouble(columns[4]));
if(!cursor.moveToNext())
{
next=false;
}
musicss.add(tempMusic);
tempMusic = new Music();
}
public Bitmap getAlbumArt(Long albumId) {
Bitmap albumArt = null;
try {
final Uri AlbumArtUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(AlbumArtUri, albumId);
ParcelFileDescriptor pfd = GlobalSongList.getInstance().getContentResolver().openFileDescriptor(uri, "r");
if (pfd != null) {
FileDescriptor fd = pfd.getFileDescriptor();
albumArt = BitmapFactory.decodeFileDescriptor(fd);
}
} catch (Exception e) {
}
return albumArt;
}
ALBUM_ID is a field in the MediaStore and you can query it in the same way you query for title,artist,etc.
public Long getAlbumId(String id)
{
Cursor musicCursor;
String[] mProjection = {MediaStore.Audio.Media._ID, MediaStore.Audio.Media.ALBUM_ID};
String[] mArgs = {id};
musicCursor = musicResolver.query(musicUri, mProjection, MediaStore.Audio.Media._ID + " = ?", mArgs, null);
musicCursor.moveToFirst();
Long albumId=musicCursor.getLong(musicCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
return albumId;
}
This is how I did.
Works perfect.
String selection= MediaStore.Audio.Media.IS_MUSIC+"!=0";
String[] projection={
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Albums.ALBUM_ID,
MediaStore.Audio.Media.DURATION
};
Cursor cursor=getActivity().managedQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,projection,selection,null,"title asc");
List<String> data=new ArrayList<>();
List<String> songs=new ArrayList<>();
List<String> artists=new ArrayList<>();
List<String> albums=new ArrayList<>();
List<String> album_id=new ArrayList<>();
List<String> durations=new ArrayList<>();
cursor.moveToFirst();
while (cursor.moveToNext()){
if(cursor.getString(5).equals(mAlbumId)) {
data.add(cursor.getString(1));
songs.add(cursor.getString(2));
artists.add(cursor.getString(3));
albums.add(cursor.getString(4));
album_id.add(mAlbumId);
durations.add(cursor.getString(6));
}
}

Android: How to list mp3 albums

I want to try develop simple android music player.
I have this method below to list album title, but it gives duplicate albums list.
public void getAlbumList() {
//query external audio
Activity a=getActivity();
ContentResolver musicResolver = a.getContentResolver();
Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
//String[] projection = null;
//String sortOrder = null;
String selectionMimeType = MediaStore.Files.FileColumns.MIME_TYPE + "=?";
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("mp3");
String[] selectionArgsMp3 = new String[]{ mimeType };
Cursor musicCursor = musicResolver.query(musicUri, null, selectionMimeType, selectionArgsMp3, null);
//iterate over results if valid
if(musicCursor!=null && musicCursor.moveToFirst()){
//get columns
int atitleColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.ALBUM);
int idColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.ALBUM_ID);
//int bitmap = musicCursor.getColumnIndex
// (android.provider.MediaStore.Audio.Albums.ALBUM_ART);
//add songs to list
while (musicCursor.moveToNext()){
long thisId = musicCursor.getLong(idColumn);
String thisaTitle = musicCursor.getString(atitleColumn);
albumList.add(new Album(thisId, thisaTitle));
}
}
}
I have tried How to use MediaStore query to get Artists without duplicates? but it doesn't work.
Thanks in advance.
add this to your Album class
#Override
public boolean equals(Object obj) {
if (obj instanceof Album) {
Album album = (Album) obj;
//here we compare if the 2 album objects are same or not (edit code as needed)
return (album.getaTitle().equals(this.aTitle) && album.getId == this.Id);
} else {
return false;
}
}
#Override
public int hashCode() {
int hashcode = 0;
hashcode = this.Id*20;
hashcode += this.aTitle.hashCode();
//here we generate the album object hashcode using Id and artistname
return hashcode;
}
then instead of adding your objects to a list, use this :
HashSet<Album> albumList = new HashSet<Album>();
albumList.add(new Album(thisId, thisaTitle));
//use simple for each loop to iterate
for(Album album:albumList){
System.out.println(album);
}
A hashset aromatically handles duplications

Categories

Resources