9

I am developing a simple audio player in android. I want to list the album's in the device.

I tried this code

String where = new String();
where = MediaStore.Audio.Media.IS_MUSIC + "=1";
private Cursor managedCursor;
managedCursor = managedQuery(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        new String[] {  
            MediaStore.Audio.Media.TITLE,
            MediaStore.Audio.Media._ID,
            MediaStore.Audio.Media.ALBUM,       
            MediaStore.Audio.Media.ALBUM_ID,    
            MediaStore.Audio.Media.ARTIST,
            MediaStore.Audio.Media.ARTIST_ID
        },
        where,                                  
        null,                                       
        MediaStore.Audio.Media.DEFAULT_SORT_ORDER   
    );

  ListAdapter adapter = new AlbumListAdapter(
        this,                                   
        R.layout.albumlist_item,                
        managedCursor,                          
        new String[] {                          
            MediaStore.Audio.Media.ALBUM,       
            MediaStore.Audio.Media.ARTIST           
        },
        new int[] {                             
            R.id.text_album, 
            R.id.text_artist 
        }
    );
    setListAdapter(adapter);

But this code is listing the all the song's in the device.

What is the structure of the Android Media store DB.

Any one please help.

John
  • 8,846
  • 8
  • 50
  • 85

4 Answers4

10

You should query the Albums like this

String[] projection = new String[] { Albums._ID, Albums.ALBUM, Albums.ARTIST, Albums.ALBUM_ART, Albums.NUMBER_OF_SONGS };
String selection = null;
String[] selectionArgs = null;
String sortOrder = Media.ALBUM + " ASC";
Cursor cursor = contentResolver.query(Albums.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder);

http://developer.android.com/reference/android/provider/MediaStore.Audio.Albums.html

Artists, Playlists and Genres can all be queried in a similar way using the correct EXTERNAL_CONTENT_URI and the corresponding projection.

Hope that helps...

JeffG
  • 3,312
  • 1
  • 26
  • 34
  • what exactly does "ASC" do? it did change the order of my media list, but i dont understand what its doing – Zen Mar 21 '14 at 21:05
  • 1
    ASC orders the result set in ascending order. In this example it will order the list in ascending order of album name. So alphabetically A, B, C, etc... The opposite is DESC for descending order – JeffG Mar 24 '14 at 10:30
  • Works great. But it arranges music files that start with small-letters and capital letters separately. So converted all media files to lowercase before sorting them :) – Zen Mar 24 '14 at 10:37
  • You could try String sortOrder = "lower(" + Media.ALBUM + ") ASC"; But I haven't tried this and am not sure if it will work. If I want to do advanced sorting, I convert the results into objects and then use a Comparator and Collection.sort to sort them. Hope that helps – JeffG Mar 24 '14 at 14:48
  • 1
    @JeffG It will show the list of albums but it also shows duplicate values if we have to get it unique we have to use DISTINCT for Albums but we cannot get multiple column selected mean even if we use distinct keyword for Album and we use the other coloums it.still iterate duplicate data. How can we get the Data uniquely – Akshay Mukadam Aug 04 '14 at 02:58
4

use this simplified code to get list of albums

public ArrayList<AlbumModel> getListOfAlbums(Context context) {

            String where = null;

            final Uri uri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
            final String _id = MediaStore.Audio.Albums._ID;
            final String album_name = MediaStore.Audio.Albums.ALBUM;
            final String artist = MediaStore.Audio.Albums.ARTIST;
            final String albumart = MediaStore.Audio.Albums.ALBUM_ART;
            final String tracks = MediaStore.Audio.Albums.NUMBER_OF_SONGS;

            final String[] columns = { _id, album_name, artist, albumart, tracks };
            Cursor cursor = context.getContentResolver().query(uri, columns, where,
                    null, null);

            ArrayList<AlbumModel> list = new ArrayList<AlbumModel>();

            // add playlsit to list

            if (cursor.moveToFirst()) {

                do {

                    AlbumModel albumData = new AlbumModel();

                    albumData
                            .setAlbumID(cursor.getLong(cursor.getColumnIndex(_id)));

                    albumData.setAlbumName(cursor.getString(cursor
                            .getColumnIndex(album_name)));

                    albumData.setALbumArtist(cursor.getString(cursor
                            .getColumnIndex(artist)));

                    albumData.setAlbumArt(cursor.getString(cursor
                            .getColumnIndex(albumart)));

                    albumData.setTracks(cursor.getString(cursor
                            .getColumnIndex(tracks)));

                    list.add(albumData);

                } while (cursor.moveToNext());
            }

            cursor.close();

            return list;
        }
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
2

This was the original answer

i sorted the unique albums by checking if they had already been added to my map

public Map<String, String> getAlbumList(Context c) {
    //setup map and cursor
    Map<String, String> result = new HashMap<String, String>();
    String selection = MediaStore.Audio.Media.IS_MUSIC + " !=0";
    final Cursor mCursor = c.getContentResolver().query(
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
            new String[] {MediaStore.Audio.Media.ALBUM,
                    MediaStore.Audio.Media.ARTIST,
                    MediaStore.Audio.Media.ALBUM_ID,}, selection, null,
                   "LOWER ("+MediaStore.Audio.Media.ALBUM + ") ASC");

    int count = mCursor.getCount();


    String[] mArtist = new String[count];
    String[] mAlbum = new String[count];
    String[] AlbumID = new String[count];

    int i = 0;
    int j = 0;
    if (mCursor.moveToFirst()) {

        do {
            mAlbum[i] = mCursor
                    .getString(mCursor
                            .getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
            mArtist[i] = mCursor.getString(mCursor
                    .getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));

            AlbumID[i] = Long.toString(mCursor
                    .getLong(mCursor
                            .getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID)));

                                    //checking for same previous value
            if(result.containsValue(mAlbum[i])){

            }else{
            result.put("artist" + j, mArtist[i]);
            result.put("album" + j, mAlbum[i]);
            result.put("AlbumID" + j, AlbumID[i]);
            j = j + 1;
            }
            i = i + 1;

        } while (mCursor.moveToNext());
    }

    result.put("count", Integer.toString(j));
    mCursor.close();
    return result;
}
}

perhaps not the prettiest solution to unique sorting of the albums... but it works exactly as intended without fumbling around with sqlite....

i'm passing in a context here because i'm using a listview in a fragment with a custom adapter without the activity context getContentResolver() doesn't work....

addendium to original answer

mArt, mData, mAlbums, and mArtists are Arraylists...

private void getList(View view){
    mArt.clear();
    mAlbums.clear();
    mArtists.clear();

    String[] projection = {"DISTINCT " + MediaStore.Audio.Media.ALBUM_ID,
            MediaStore.Audio.Media.ALBUM,
            MediaStore.Audio.Media.ARTIST

    } ;

    String[] projection2 = {"Distinct " + MediaStore.Audio.Albums.ALBUM,
    MediaStore.Audio.Albums.NUMBER_OF_SONGS};

    Cursor mCursor =  getActivity().getApplicationContext().getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, "0==0 ) GROUP BY (" + MediaStore.Audio.Media.ALBUM_ID, null, MediaStore.Audio.Media.ALBUM + " COLLATE NOCASE ASC");
    Cursor mCursor2 = getActivity().getApplicationContext().getContentResolver().query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, projection2, null, null, MediaStore.Audio.Albums.ALBUM + " COLLATE NOCASE ASC");

    if(mCursor != null && mCursor.getCount() > 0 && mCursor2 != null && mCursor2.getCount() > 0){
        CursorJoiner joiner = new CursorJoiner(mCursor, new String[]{MediaStore.Audio.Media.ALBUM},mCursor2, new String[]{MediaStore.Audio.Albums.ALBUM});

        for (CursorJoiner.Result joinerResult : joiner) {
            switch (joinerResult) {
                case LEFT:
                    break;
                case RIGHT:
                    break;
                case BOTH:
                    String songs_s = "Song";
                    int tracks = mCursor2.getInt(mCursor2.getColumnIndex(MediaStore.Audio.Albums.NUMBER_OF_SONGS));
                    if (tracks > 1) {
                        songs_s = "Songs";
                    }
                    mArtists.add(tracks + " " + songs_s + " By " + mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
                    mAlbums.add(mCursor2.getString(mCursor2.getColumnIndex(MediaStore.Audio.Albums.ALBUM)));
                    mArt.add(ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID))));

            }
        }
    }
    mCursor.close();
    mCursor2.close();
}

the GROUP BY clause in the selection guarantees unique albums, since album art is sotred by the album id this line:

mArt.add(ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), mCursor.getInt(mCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID))));

makes sure that the album art and the album are in the same position...

i am using a cursor joiner here so that I can get the artist, album, and number of songs in the album... this can only be returned from two separate queries...

me_
  • 681
  • 1
  • 8
  • 18
  • Does this code also sort the album id so that album names and album art matches? The problem i have is that i can sort the album names and when i want to show the album artwork it doesn't match the album name. How can i solve this? – Vince VD Jun 30 '18 at 13:47
  • I fetch the album ID from MediaStore.Audio.Media.ALBUM_ID and use that id in picasso to display the artwork. – Vince VD Jun 30 '18 at 13:49
  • I have a post which explains my problem https://stackoverflow.com/questions/51094052/how-can-i-sort-albums-from-songs – Vince VD Jun 30 '18 at 13:53
  • @vince yes it does... but after i wrote this answer i found a simpler way of doing this... i will edit the answer to reflect that... – me_ Jul 01 '18 at 04:43
  • the second code block returns three arraylists: mArt with album art uris, mAlbum with the name of the album, and mArtist with the name of the Artist and the number of songs on the album in a string like this "x Song(s) by Artist" you just need to know the position of any of these elements and all others are in the same place in their respective lists – me_ Jul 01 '18 at 05:09
  • @me_ i'm using a model class Song where i store all information. Can i use that instead of the arraylists? like song.setArtist(tracks + " " + songs_s + " By " + mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST))); – Vince VD Jul 01 '18 at 18:50
  • @me_ can you take a look at my post, there you can find the code that i use to get album artist and album. https://stackoverflow.com/questions/51094052/how-can-i-sort-albums-from-songs I just find it a bit hard to implement your code in mine. – Vince VD Jul 01 '18 at 19:18
  • DISTINCT in projection crashes app on android 10 – artman Sep 11 '19 at 11:30
  • @artman no... i just tested this code on Android 10.0 (Q), API 29, Revision 3. – me_ Sep 15 '19 at 07:43
  • @me_ thats weird, because I got stable crash on Pixel. – artman Sep 16 '19 at 08:08
2
public class Album {

private long id;
private String albumName;
private String artistName;
private int nr_of_songs;
private Bitmap albumImg;

public Album(long id, String albumName, String artistName, Bitmap albumImg,  int nr_of_songs) {
    this.albumImg = albumImg;
    this.id = id;
    this.albumName = albumName;
    this.artistName = artistName;
    this.nr_of_songs = nr_of_songs;
}

public void setId(long id) {
    this.id = id;
}
public void setAlbumName(String albumName) {
    this.albumName = albumName;
}
public void setArtistName(String artistName) {
    this.artistName = artistName;
}
public void setAlbumImg(Bitmap albumImg) {
    this.albumImg = albumImg;
}
public void setNr_of_songs(int nr_of_songs) {
    this.nr_of_songs = nr_of_songs;
}

public long getID(){
    return id;
}
public String getAlbumName(){
    return albumName;
}
public String getArtistName() {
    return artistName;
}
public Bitmap getAlbumImg() {
    return albumImg;
}
public int getNr_of_songs() {
    return nr_of_songs;
}

}


 public void getAlbumsLists(){
    String where = null;

    final Uri uri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
    final String _id = MediaStore.Audio.Albums._ID;
    final String album_name = MediaStore.Audio.Albums.ALBUM;
    final String artist = MediaStore.Audio.Albums.ARTIST;
    final String albumart = MediaStore.Audio.Albums.ALBUM_ART;
    final String tracks = MediaStore.Audio.Albums.NUMBER_OF_SONGS;

    final String[] columns = { _id, album_name, artist, albumart, tracks };
    Cursor cursor = context.getContentResolver().query(uri, columns, where, null, null);

    if(cursor!=null && cursor.moveToFirst()){

        do {

            long id = cursor.getLong(cursor.getColumnIndex(_id));
            String name = cursor.getString(cursor.getColumnIndex(album_name));
            String artist2 = cursor.getString(cursor.getColumnIndex(artist));
            String artPath = cursor.getString(cursor.getColumnIndex(albumart));
            Bitmap art = BitmapFactory.decodeFile(artPath);
            int nr =Integer.parseInt(cursor.getString(cursor.getColumnIndex(tracks)));

            albumList.add(new Album(id, name, artist2, art, nr));

        } while (cursor.moveToNext());
    }

    cursor.close();
}
Nicu P
  • 253
  • 2
  • 6