package org.music.player;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.support.v4.util.LruCache;
import java.io.FileDescriptor;
/**
* Represents a Song backed by the MediaStore. Includes basic metadata and
* utilities to retrieve songs from the MediaStore.
*/
public class Song implements Comparable<Song> {
/**
* Indicates that this song was randomly selected from all songs.
*/
public static final int FLAG_RANDOM = 0x1;
/**
* If set, this song has no cover art. If not set, this song may or may not
* have cover art.
*/
public static final int FLAG_NO_COVER = 0x2;
/**
* The number of flags.
*/
public static final int FLAG_COUNT = 2;
public static final String[] EMPTY_PROJECTION = {
MediaStore.Audio.Media._ID,
};
public static final String[] FILLED_PROJECTION = {
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.ARTIST_ID,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.TRACK,
};
public static final String[] EMPTY_PLAYLIST_PROJECTION = {
MediaStore.Audio.Playlists.Members.AUDIO_ID,
};
public static final String[] FILLED_PLAYLIST_PROJECTION = {
MediaStore.Audio.Playlists.Members.AUDIO_ID,
MediaStore.Audio.Playlists.Members.DATA,
MediaStore.Audio.Playlists.Members.TITLE,
MediaStore.Audio.Playlists.Members.ALBUM,
MediaStore.Audio.Playlists.Members.ARTIST,
MediaStore.Audio.Playlists.Members.ALBUM_ID,
MediaStore.Audio.Playlists.Members.ARTIST_ID,
MediaStore.Audio.Playlists.Members.DURATION,
MediaStore.Audio.Playlists.Members.TRACK,
};
/**
* A cache of 6 MiB of covers.
*/
private static class CoverCache extends LruCache<Long, Bitmap> {
private final Context mContext;
public CoverCache(Context context)
{
super(6 * 1024 * 1024);
mContext = context;
}
@Override
public Bitmap create(Long key)
{
Uri uri = Uri.parse("content://media/external/audio/media/" + key + "/albumart");
ContentResolver res = mContext.getContentResolver();
try {
ParcelFileDescriptor parcelFileDescriptor = res.openFileDescriptor(uri, "r");
if (parcelFileDescriptor != null) {
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, BITMAP_OPTIONS);
}
} catch (Exception e) {
// no cover art found
}
return null;
}
@Override
protected int sizeOf(Long key, Bitmap value)
{
return value.getRowBytes() * value.getHeight();
}
}
/**
* The cache instance.
*/
private static CoverCache sCoverCache = null;
/**
* If true, will not attempt to load any cover art in getCover()
*/
public static boolean mDisableCoverArt = false;
/**
* Id of this song in the MediaStore
*/
public long id;
/**
* Id of this song's album in the MediaStore
*/
public long albumId;
/**
* Id of this song's artist in the MediaStore
*/
public long artistId;
/**
* Path to the data for this song
*/
public String path;
/**
* Song title
*/
public String title;
/**
* Album name
*/
public String album;
/**
* Artist name
*/
public String artist;
/**
* Length of the song in milliseconds.
*/
public long duration;
/**
* The position of the song in its album.
*/
public int trackNumber;
/**
* Song flags. Currently {@link #FLAG_RANDOM} or {@link #FLAG_NO_COVER}.
*/
public int flags;
/**
* Initialize the song with the specified id. Call populate to fill fields
* in the song.
*/
public Song(long id)
{
this.id = id;
}
/**
* Initialize the song with the specified id and flags. Call populate to
* fill fields in the song.
*/
public Song(long id, int flags)
{
this.id = id;
this.flags = flags;
}
/**
* Return true if this song was retrieved from randomSong().
*/
public boolean isRandom()
{
return (flags & FLAG_RANDOM) != 0;
}
/**
* Populate fields with data from the supplied cursor.
*
* @param cursor Cursor queried with FILLED_PROJECTION projection
*/
public void populate(Cursor cursor)
{
id = cursor.getLong(0);
path = cursor.getString(1);
title = cursor.getString(2);
album = cursor.getString(3);
artist = cursor.getString(4);
albumId = cursor.getLong(5);
artistId = cursor.getLong(6);
duration = cursor.getLong(7);
trackNumber = cursor.getInt(8);
}
/**
* Get the id of the given song.
*
* @param song The Song to get the id from.
* @return The id, or 0 if the given song is null.
*/
public static long getId(Song song)
{
if (song == null)
return 0;
return song.id;
}
static final BitmapFactory.Options BITMAP_OPTIONS = new BitmapFactory.Options();
static {
BITMAP_OPTIONS.inPreferredConfig = Bitmap.Config.RGB_565;
BITMAP_OPTIONS.inDither = false;
}
/**
* Query the album art for this song.
*
* @param context A context to use.
* @return The album art or null if no album art could be found
*/
public Bitmap getCover(Context context)
{
if (mDisableCoverArt || id == -1 || (flags & FLAG_NO_COVER) != 0)
return null;
if (sCoverCache == null)
sCoverCache = new CoverCache(context.getApplicationContext());
Bitmap cover = sCoverCache.get(id);
if (cover == null)
flags |= FLAG_NO_COVER;
return cover;
}
@Override
public String toString()
{
return String.format("%d %d %s", id, albumId, path);
}
/**
* Compares the album ids of the two songs; if equal, compares track order.
*/
@Override
public int compareTo(Song other)
{
if (albumId == other.albumId)
return trackNumber - other.trackNumber;
if (albumId > other.albumId)
return 1;
return -1;
}
}