Remove ContextApplication

This commit is contained in:
Christopher Eby 2011-09-19 21:53:39 -05:00
parent 5837753b4a
commit 2023409bd9
16 changed files with 197 additions and 222 deletions

View File

@ -27,8 +27,7 @@ THE SOFTWARE.
android:installLocation="auto"> android:installLocation="auto">
<application <application
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:label="@string/app_name" android:label="@string/app_name">
android:name="ContextApplication">
<activity <activity
android:name="LaunchActivity" android:name="LaunchActivity"
android:theme="@style/NoBackground"> android:theme="@style/NoBackground">

View File

@ -1,59 +0,0 @@
/*
* Copyright (C) 2010, 2011 Christopher Eby <kreed@kreed.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kreed.vanilla;
import java.util.Random;
import android.app.Application;
import android.content.Context;
/**
* Subclass of Application that provides various static utility functions
*/
public class ContextApplication extends Application {
private static ContextApplication mInstance;
private static Random mRandom;
public ContextApplication()
{
mInstance = this;
}
/**
* Returns a shared, application-wide Random instance.
*/
public static Random getRandom()
{
if (mRandom == null)
mRandom = new Random();
return mRandom;
}
/**
* Provides an easy to access Context instance.
*/
public static Context getContext()
{
return mInstance;
}
}

View File

@ -22,6 +22,7 @@
package org.kreed.vanilla; package org.kreed.vanilla;
import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
@ -66,10 +67,12 @@ public final class CoverBitmap {
/** /**
* Initialize the regular text size members. * Initialize the regular text size members.
*
* @param context A context to use.
*/ */
private static void loadTextSizes() private static void loadTextSizes(Context context)
{ {
DisplayMetrics metrics = ContextApplication.getContext().getResources().getDisplayMetrics(); DisplayMetrics metrics = context.getResources().getDisplayMetrics();
TEXT_SIZE = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, metrics); TEXT_SIZE = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, metrics);
TEXT_SIZE_BIG = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, metrics); TEXT_SIZE_BIG = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, metrics);
PADDING = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, metrics); PADDING = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, metrics);
@ -78,10 +81,12 @@ public final class CoverBitmap {
/** /**
* Initialize the icon bitmaps. * Initialize the icon bitmaps.
*
* @param context A context to use.
*/ */
private static void loadIcons() private static void loadIcons(Context context)
{ {
Resources res = ContextApplication.getContext().getResources(); Resources res = context.getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.ic_tab_songs_selected); Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.ic_tab_songs_selected);
SONG_ICON = Bitmap.createScaledBitmap(bitmap, TEXT_SIZE, TEXT_SIZE, false); SONG_ICON = Bitmap.createScaledBitmap(bitmap, TEXT_SIZE, TEXT_SIZE, false);
bitmap.recycle(); bitmap.recycle();
@ -118,6 +123,7 @@ public final class CoverBitmap {
* Create an image representing the given song. Includes cover art and * Create an image representing the given song. Includes cover art and
* possibly song title/artist/ablum, depending on the given style. * possibly song title/artist/ablum, depending on the given style.
* *
* @param context A context to use.
* @param style One of CoverBitmap.STYLE_* * @param style One of CoverBitmap.STYLE_*
* @param song The song to display information for * @param song The song to display information for
* @param width Maximum width of image * @param width Maximum width of image
@ -128,29 +134,29 @@ public final class CoverBitmap {
* @return The image, or null if the song was null, or width or height * @return The image, or null if the song was null, or width or height
* were less than 1 * were less than 1
*/ */
public static Bitmap createBitmap(int style, Song song, int width, int height, Bitmap bitmap) public static Bitmap createBitmap(Context context, int style, Song song, int width, int height, Bitmap bitmap)
{ {
switch (style) { switch (style) {
case STYLE_OVERLAPPING_BOX: case STYLE_OVERLAPPING_BOX:
return createOverlappingBitmap(song, width, height, bitmap); return createOverlappingBitmap(context, song, width, height, bitmap);
case STYLE_INFO_BELOW: case STYLE_INFO_BELOW:
return createSeparatedBitmap(song, width, height, bitmap); return createSeparatedBitmap(context, song, width, height, bitmap);
case STYLE_NO_INFO: case STYLE_NO_INFO:
return createScaledBitmap(song, width, height); return createScaledBitmap(context, song, width, height);
case STYLE_NO_INFO_ZOOMED: case STYLE_NO_INFO_ZOOMED:
return createZoomedBitmap(song, width, height, bitmap); return createZoomedBitmap(context, song, width, height, bitmap);
default: default:
throw new IllegalArgumentException("Invalid bitmap type given: " + style); throw new IllegalArgumentException("Invalid bitmap type given: " + style);
} }
} }
private static Bitmap createOverlappingBitmap(Song song, int width, int height, Bitmap bitmap) private static Bitmap createOverlappingBitmap(Context context, Song song, int width, int height, Bitmap bitmap)
{ {
if (song == null || width < 1 || height < 1) if (song == null || width < 1 || height < 1)
return null; return null;
if (TEXT_SIZE == -1) if (TEXT_SIZE == -1)
loadTextSizes(); loadTextSizes(context);
Paint paint = new Paint(); Paint paint = new Paint();
paint.setAntiAlias(true); paint.setAntiAlias(true);
@ -158,7 +164,7 @@ public final class CoverBitmap {
String title = song.title == null ? "" : song.title; String title = song.title == null ? "" : song.title;
String album = song.album == null ? "" : song.album; String album = song.album == null ? "" : song.album;
String artist = song.artist == null ? "" : song.artist; String artist = song.artist == null ? "" : song.artist;
Bitmap cover = song.getCover(); Bitmap cover = song.getCover(context);
int titleSize = TEXT_SIZE_BIG; int titleSize = TEXT_SIZE_BIG;
int subSize = TEXT_SIZE; int subSize = TEXT_SIZE;
@ -238,15 +244,15 @@ public final class CoverBitmap {
return bitmap; return bitmap;
} }
private static Bitmap createSeparatedBitmap(Song song, int width, int height, Bitmap bitmap) private static Bitmap createSeparatedBitmap(Context context, Song song, int width, int height, Bitmap bitmap)
{ {
if (song == null || width < 1 || height < 1) if (song == null || width < 1 || height < 1)
return null; return null;
if (TEXT_SIZE == -1) if (TEXT_SIZE == -1)
loadTextSizes(); loadTextSizes(context);
if (SONG_ICON == null) if (SONG_ICON == null)
loadIcons(); loadIcons(context);
boolean horizontal = width > height; boolean horizontal = width > height;
@ -256,7 +262,7 @@ public final class CoverBitmap {
String title = song.title == null ? "" : song.title; String title = song.title == null ? "" : song.title;
String album = song.album == null ? "" : song.album; String album = song.album == null ? "" : song.album;
String artist = song.artist == null ? "" : song.artist; String artist = song.artist == null ? "" : song.artist;
Bitmap cover = song.getCover(); Bitmap cover = song.getCover(context);
int textSize = TEXT_SIZE; int textSize = TEXT_SIZE;
int padding = PADDING; int padding = PADDING;
@ -340,12 +346,12 @@ public final class CoverBitmap {
return bitmap; return bitmap;
} }
private static Bitmap createZoomedBitmap(Song song, int width, int height, Bitmap bitmap) private static Bitmap createZoomedBitmap(Context context, Song song, int width, int height, Bitmap bitmap)
{ {
if (song == null || width < 1 || height < 1) if (song == null || width < 1 || height < 1)
return null; return null;
Bitmap cover = song.getCover(); Bitmap cover = song.getCover(context);
if (cover == null) if (cover == null)
return null; return null;
@ -379,17 +385,18 @@ public final class CoverBitmap {
* preserved. At least one dimension of the result will match the provided * preserved. At least one dimension of the result will match the provided
* dimension exactly. * dimension exactly.
* *
* @param context A context to use.
* @param song The song to display information for * @param song The song to display information for
* @param width Maximum width of image * @param width Maximum width of image
* @param height Maximum height of image * @param height Maximum height of image
* @return The scaled Bitmap, or null if a cover could not be found. * @return The scaled Bitmap, or null if a cover could not be found.
*/ */
public static Bitmap createScaledBitmap(Song song, int width, int height) public static Bitmap createScaledBitmap(Context context, Song song, int width, int height)
{ {
if (song == null || width < 1 || height < 1) if (song == null || width < 1 || height < 1)
return null; return null;
Bitmap cover = song.getCover(); Bitmap cover = song.getCover(context);
if (cover == null) if (cover == null)
return null; return null;

View File

@ -326,7 +326,7 @@ public final class CoverView extends View implements Handler.Callback {
Bitmap bitmap = mBitmapCache.get(song.id); Bitmap bitmap = mBitmapCache.get(song.id);
if (bitmap == null) { if (bitmap == null) {
mBitmapCache.put(song.id, CoverBitmap.createBitmap(mCoverStyle, song, getWidth(), getHeight(), mBitmapCache.discardOldest())); mBitmapCache.put(song.id, CoverBitmap.createBitmap(getContext(), mCoverStyle, song, getWidth(), getHeight(), mBitmapCache.discardOldest()));
postInvalidate(); postInvalidate();
} else { } else {
mBitmapCache.touch(song.id); mBitmapCache.touch(song.id);

View File

@ -59,6 +59,8 @@ import java.io.Serializable;
* See MediaView.getLimiter and setLimiter for details. * See MediaView.getLimiter and setLimiter for details.
*/ */
public class MediaAdapter extends CursorAdapter implements FilterQueryProvider { public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
private Context mContext;
/** /**
* The type of media represented by this adapter. Must be one of the * The type of media represented by this adapter. Must be one of the
* MediaUtils.FIELD_* constants. Determines which content provider to query for * MediaUtils.FIELD_* constants. Determines which content provider to query for
@ -113,6 +115,7 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
{ {
super(context, null, requery); super(context, null, requery);
mContext = context;
mType = type; mType = type;
mExpandable = expandable; mExpandable = expandable;
mLimiter = limiter; mLimiter = limiter;
@ -225,7 +228,7 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
*/ */
public Cursor runQuery(CharSequence constraint) public Cursor runQuery(CharSequence constraint)
{ {
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = mContext.getContentResolver();
String[] projection; String[] projection;
if (mFields.length == 1) if (mFields.length == 1)
@ -279,7 +282,7 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
if (mLimiter.type == MediaUtils.TYPE_GENRE) { if (mLimiter.type == MediaUtils.TYPE_GENRE) {
// Genre is not standard metadata for MediaStore.Audio.Media. // Genre is not standard metadata for MediaStore.Audio.Media.
// We have to query it through a separate provider. : / // We have to query it through a separate provider. : /
return MediaUtils.queryGenre(mLimiter.id, projection, selection.toString(), selectionArgs); return MediaUtils.queryGenre(mContext, mLimiter.id, projection, selection.toString(), selectionArgs);
} else { } else {
if (selection.length() != 0) if (selection.length() != 0)
selection.append(" AND "); selection.append(" AND ");

View File

@ -56,6 +56,8 @@ public class MediaButtonHandler {
* uninitialized. * uninitialized.
*/ */
private static int mUseControls = -1; private static int mUseControls = -1;
private Context mContext;
/** /**
* Whether the phone is currently in a call. 1 for yes, 0 for no, -1 for * Whether the phone is currently in a call. 1 for yes, 0 for no, -1 for
* uninitialized. * uninitialized.
@ -75,11 +77,11 @@ public class MediaButtonHandler {
* Retrieve the MediaButtonHandler singleton, creating it if necessary. * Retrieve the MediaButtonHandler singleton, creating it if necessary.
* Returns null if headset controls are not enabled. * Returns null if headset controls are not enabled.
*/ */
public static MediaButtonHandler getInstance() public static MediaButtonHandler getInstance(Context context)
{ {
if (useHeadsetControls()) { if (useHeadsetControls(context)) {
if (mInstance == null) if (mInstance == null)
mInstance = new MediaButtonHandler(); mInstance = new MediaButtonHandler(context.getApplicationContext());
return mInstance; return mInstance;
} }
return null; return null;
@ -88,10 +90,9 @@ public class MediaButtonHandler {
/** /**
* Construct a MediaButtonHandler. * Construct a MediaButtonHandler.
*/ */
private MediaButtonHandler() private MediaButtonHandler(Context context)
{ {
Context context = ContextApplication.getContext(); mContext = context;
mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
mButtonReceiver = new ComponentName(context.getPackageName(), MediaButtonReceiver.class.getName()); mButtonReceiver = new ComponentName(context.getPackageName(), MediaButtonReceiver.class.getName());
try { try {
@ -102,17 +103,16 @@ public class MediaButtonHandler {
} }
} }
private static void loadPreference() /**
* Reload the preference and enable/disable buttons as appropriate.
*
* @param context A context to use.
*/
public static void reloadPreference(Context context)
{ {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(ContextApplication.getContext()); mUseControls = -1;
mUseControls = settings.getBoolean("media_button", true) ? 1 : 0; if (useHeadsetControls(context)) {
} getInstance(context).registerMediaButton();
public static void reloadPreference()
{
loadPreference();
if (useHeadsetControls()) {
getInstance().registerMediaButton();
} else { } else {
unregisterMediaButton(); unregisterMediaButton();
} }
@ -121,11 +121,16 @@ public class MediaButtonHandler {
/** /**
* Return whether headset controls should be used, loading the preference * Return whether headset controls should be used, loading the preference
* if necessary. * if necessary.
*
* @param context A context to use.
*/ */
public static boolean useHeadsetControls() public static boolean useHeadsetControls(Context context)
{ {
if (mUseControls == -1) if (mUseControls == -1) {
loadPreference(); SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
mUseControls = settings.getBoolean("media_button", true) ? 1 : 0;
}
return mUseControls == 1; return mUseControls == 1;
} }
@ -135,8 +140,7 @@ public class MediaButtonHandler {
private boolean isInCall() private boolean isInCall()
{ {
if (mInCall == -1) { if (mInCall == -1) {
Context context = ContextApplication.getContext(); TelephonyManager manager = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
mInCall = (byte)(manager.getCallState() == TelephonyManager.CALL_STATE_IDLE ? 0 : 1); mInCall = (byte)(manager.getCallState() == TelephonyManager.CALL_STATE_IDLE ? 0 : 1);
} }
return mInCall == 1; return mInCall == 1;
@ -157,12 +161,11 @@ public class MediaButtonHandler {
* *
* @param action One of the PlaybackService.ACTION_* actions. * @param action One of the PlaybackService.ACTION_* actions.
*/ */
private static void act(String action) private void act(String action)
{ {
Context context = ContextApplication.getContext(); Intent intent = new Intent(mContext, PlaybackService.class);
Intent intent = new Intent(context, PlaybackService.class);
intent.setAction(action); intent.setAction(action);
context.startService(intent); mContext.startService(intent);
} }
/** /**
@ -170,7 +173,7 @@ public class MediaButtonHandler {
*/ */
public boolean processKey(KeyEvent event) public boolean processKey(KeyEvent event)
{ {
if (event == null || isInCall() || !useHeadsetControls()) if (event == null || isInCall() || !useHeadsetControls(mContext))
return false; return false;
int action = event.getAction(); int action = event.getAction();

View File

@ -31,7 +31,7 @@ public class MediaButtonReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) public void onReceive(Context context, Intent intent)
{ {
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) { if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
boolean handled = MediaButtonHandler.getInstance().process(intent); boolean handled = MediaButtonHandler.getInstance(context).process(intent);
if (handled && isOrderedBroadcast()) if (handled && isOrderedBroadcast())
abortBroadcast(); abortBroadcast();
} }

View File

@ -53,18 +53,34 @@ public class MediaUtils {
*/ */
public static final int TYPE_GENRE = 5; public static final int TYPE_GENRE = 5;
/**
* Cached random instance.
*/
private static Random sRandom;
/**
* Returns a cached random instanced, creating it if necessary.
*/
public static Random getRandom()
{
if (sRandom == null)
sRandom = new Random();
return sRandom;
}
/** /**
* Return a cursor containing the ids of all the songs with artist or * Return a cursor containing the ids of all the songs with artist or
* album of the specified id. * album of the specified id.
* *
* @param context A context to use.
* @param type One of the TYPE_* constants, excluding playlists. * @param type One of the TYPE_* constants, excluding playlists.
* @param id The MediaStore id of the artist or album. * @param id The MediaStore id of the artist or album.
* @param projection The columns to query. * @param projection The columns to query.
* @param select An extra selection to pass to the query, or null. * @param select An extra selection to pass to the query, or null.
*/ */
private static Cursor getMediaCursor(int type, long id, String[] projection, String select) private static Cursor getMediaCursor(Context context, int type, long id, String[] projection, String select)
{ {
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
StringBuilder selection = new StringBuilder(); StringBuilder selection = new StringBuilder();
@ -99,12 +115,13 @@ public class MediaUtils {
* Return a cursor containing the ids of all the songs in the playlist * Return a cursor containing the ids of all the songs in the playlist
* with the given id. * with the given id.
* *
* @param context A context to use.
* @param id The id of the playlist in MediaStore.Audio.Playlists. * @param id The id of the playlist in MediaStore.Audio.Playlists.
* @param projection The columns to query. * @param projection The columns to query.
*/ */
private static Cursor getPlaylistCursor(long id, String[] projection) private static Cursor getPlaylistCursor(Context context, long id, String[] projection)
{ {
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", id); Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", id);
String sort = MediaStore.Audio.Playlists.Members.PLAY_ORDER; String sort = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
return resolver.query(uri, projection, null, null, sort); return resolver.query(uri, projection, null, null, sort);
@ -114,14 +131,15 @@ public class MediaUtils {
* Return a cursor containing the ids of all the songs in the genre * Return a cursor containing the ids of all the songs in the genre
* with the given id. * with the given id.
* *
* @param context A context to use.
* @param id The id of the genre in MediaStore.Audio.Genres. * @param id The id of the genre in MediaStore.Audio.Genres.
* @param projection The columns to query. * @param projection The columns to query.
* @param selection The selection to pass to the query, or null. * @param selection The selection to pass to the query, or null.
* @param selectionArgs The arguments to substitute into the selection. * @param selectionArgs The arguments to substitute into the selection.
*/ */
public static Cursor queryGenre(long id, String[] projection, String selection, String[] selectionArgs) public static Cursor queryGenre(Context context, long id, String[] projection, String selection, String[] selectionArgs)
{ {
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
Uri uri = MediaStore.Audio.Genres.Members.getContentUri("external", id); Uri uri = MediaStore.Audio.Genres.Members.getContentUri("external", id);
String sort = MediaStore.Audio.Genres.Members.TITLE_KEY; String sort = MediaStore.Audio.Genres.Members.TITLE_KEY;
return resolver.query(uri, projection, selection, selectionArgs, sort); return resolver.query(uri, projection, selection, selectionArgs, sort);
@ -130,6 +148,7 @@ public class MediaUtils {
/** /**
* Returns a Cursor queried with the given information. * Returns a Cursor queried with the given information.
* *
* @param context A context to use.
* @param type Type the id represents. Must be one of the Song.TYPE_* * @param type Type the id represents. Must be one of the Song.TYPE_*
* constants. * constants.
* @param id The id of the element in the MediaStore content provider for * @param id The id of the element in the MediaStore content provider for
@ -137,18 +156,18 @@ public class MediaUtils {
* @param selection An extra selection to be passed to the query. May be * @param selection An extra selection to be passed to the query. May be
* null. Must not be used with type == TYPE_SONG or type == TYPE_PLAYLIST * null. Must not be used with type == TYPE_SONG or type == TYPE_PLAYLIST
*/ */
public static Cursor query(int type, long id, String[] projection, String selection) public static Cursor query(Context context, int type, long id, String[] projection, String selection)
{ {
switch (type) { switch (type) {
case TYPE_ARTIST: case TYPE_ARTIST:
case TYPE_ALBUM: case TYPE_ALBUM:
case TYPE_SONG: case TYPE_SONG:
return getMediaCursor(type, id, projection, selection); return getMediaCursor(context, type, id, projection, selection);
case TYPE_PLAYLIST: case TYPE_PLAYLIST:
assert(selection == null); assert(selection == null);
return getPlaylistCursor(id, projection); return getPlaylistCursor(context, id, projection);
case TYPE_GENRE: case TYPE_GENRE:
return queryGenre(id, projection, selection, null); return queryGenre(context, id, projection, selection, null);
default: default:
throw new IllegalArgumentException("Specified type not valid: " + type); throw new IllegalArgumentException("Specified type not valid: " + type);
} }
@ -157,21 +176,22 @@ public class MediaUtils {
/** /**
* Return an array containing all the song ids that match the specified parameters * Return an array containing all the song ids that match the specified parameters
* *
* @param context A context to use.
* @param type Type the id represents. Must be one of the Song.TYPE_* * @param type Type the id represents. Must be one of the Song.TYPE_*
* constants. * constants.
* @param id The id of the element in the MediaStore content provider for * @param id The id of the element in the MediaStore content provider for
* the given type. * the given type.
*/ */
public static long[] getAllSongIdsWith(int type, long id) public static long[] getAllSongIdsWith(Context context, int type, long id)
{ {
if (type == TYPE_SONG) if (type == TYPE_SONG)
return new long[] { id }; return new long[] { id };
Cursor cursor; Cursor cursor;
if (type == MediaUtils.TYPE_PLAYLIST) if (type == MediaUtils.TYPE_PLAYLIST)
cursor = query(type, id, Song.EMPTY_PLAYLIST_PROJECTION, null); cursor = query(context, type, id, Song.EMPTY_PLAYLIST_PROJECTION, null);
else else
cursor = query(type, id, Song.EMPTY_PROJECTION, null); cursor = query(context, type, id, Song.EMPTY_PROJECTION, null);
if (cursor == null) if (cursor == null)
return null; return null;
@ -204,7 +224,7 @@ public class MediaUtils {
ContentResolver resolver = context.getContentResolver(); ContentResolver resolver = context.getContentResolver();
String[] projection = new String [] { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.DATA }; String[] projection = new String [] { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.DATA };
Cursor cursor = getMediaCursor(type, id, projection, null); Cursor cursor = getMediaCursor(context, type, id, projection, null);
if (cursor != null) { if (cursor != null) {
PlaybackService service = PlaybackService.hasInstance() ? PlaybackService.get(context) : null; PlaybackService service = PlaybackService.hasInstance() ? PlaybackService.get(context) : null;
@ -230,13 +250,13 @@ public class MediaUtils {
* Query the MediaStore to determine the id of the genre the song belongs * Query the MediaStore to determine the id of the genre the song belongs
* to. * to.
*/ */
public static long queryGenreForSong(long id) public static long queryGenreForSong(Context context, long id)
{ {
// This is terribly inefficient, but it seems to be the only way to do // This is terribly inefficient, but it seems to be the only way to do
// this. Honeycomb introduced an API to query the genre of the song. // this. Honeycomb introduced an API to query the genre of the song.
// We should look into it when ICS is released. // We should look into it when ICS is released.
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
// query ids of all the genres // query ids of all the genres
Uri uri = MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI; Uri uri = MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI;
@ -269,7 +289,7 @@ public class MediaUtils {
*/ */
public static void shuffle(long[] list) public static void shuffle(long[] list)
{ {
Random random = ContextApplication.getRandom(); Random random = getRandom();
for (int i = list.length; --i != -1; ) { for (int i = list.length; --i != -1; ) {
int j = random.nextInt(i + 1); int j = random.nextInt(i + 1);
long tmp = list[j]; long tmp = list[j];
@ -287,7 +307,7 @@ public class MediaUtils {
public static void shuffle(Song[] list, int end) public static void shuffle(Song[] list, int end)
{ {
assert(end <= list.length && end >= 0); assert(end <= list.length && end >= 0);
Random random = ContextApplication.getRandom(); Random random = getRandom();
for (int i = end; --i != -1; ) { for (int i = end; --i != -1; ) {
int j = random.nextInt(i + 1); int j = random.nextInt(i + 1);
Song tmp = list[j]; Song tmp = list[j];

View File

@ -125,7 +125,7 @@ public class NewPlaylistDialog extends Dialog implements TextWatcher, View.OnCli
mPositiveButton.setEnabled(true); mPositiveButton.setEnabled(true);
// Update the action button based on whether there is an // Update the action button based on whether there is an
// existing playlist with the given name. // existing playlist with the given name.
int res = Playlist.getPlaylist(string) == -1 ? mActionRes : R.string.overwrite; int res = Playlist.getPlaylist(getContext(), string) == -1 ? mActionRes : R.string.overwrite;
mPositiveButton.setText(res); mPositiveButton.setText(res);
} }
} }

View File

@ -113,7 +113,7 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View
PlaybackService service = PlaybackService.get(this); PlaybackService service = PlaybackService.get(this);
service.userActionTriggered(); service.userActionTriggered();
MediaButtonHandler buttons = MediaButtonHandler.getInstance(); MediaButtonHandler buttons = MediaButtonHandler.getInstance(this);
if (buttons != null) if (buttons != null)
buttons.setInCall(false); buttons.setInCall(false);
} }
@ -127,7 +127,7 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_NEXT:
case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
return MediaButtonHandler.getInstance().processKey(event); return MediaButtonHandler.getInstance(this).processKey(event);
} }
return super.onKeyDown(keyCode, event); return super.onKeyDown(keyCode, event);
@ -141,7 +141,7 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_NEXT:
case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
return MediaButtonHandler.getInstance().processKey(event); return MediaButtonHandler.getInstance(this).processKey(event);
} }
return super.onKeyUp(keyCode, event); return super.onKeyUp(keyCode, event);

View File

@ -184,9 +184,9 @@ public final class PlaybackService extends Service implements Handler.Callback,
HandlerThread thread = new HandlerThread("PlaybackService"); HandlerThread thread = new HandlerThread("PlaybackService");
thread.start(); thread.start();
mTimeline = new SongTimeline(); mTimeline = new SongTimeline(this);
mTimeline.setCallback(this); mTimeline.setCallback(this);
mPendingSeek = mTimeline.loadState(this); mPendingSeek = mTimeline.loadState();
mMediaPlayer = new MediaPlayer(); mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
@ -268,7 +268,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
} }
userActionTriggered(); userActionTriggered();
MediaButtonHandler buttons = MediaButtonHandler.getInstance(); MediaButtonHandler buttons = MediaButtonHandler.getInstance(this);
if (buttons != null) if (buttons != null)
buttons.registerMediaButton(); buttons.registerMediaButton();
} }
@ -285,7 +285,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
stopForeground(true); stopForeground(true);
if (mMediaPlayer != null) { if (mMediaPlayer != null) {
mTimeline.saveState(this, mMediaPlayer.getCurrentPosition()); mTimeline.saveState(mMediaPlayer.getCurrentPosition());
mMediaPlayer.release(); mMediaPlayer.release();
mMediaPlayer = null; mMediaPlayer = null;
} }
@ -338,7 +338,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
if (mMediaPlayer != null) if (mMediaPlayer != null)
mMediaPlayer.setVolume(volume, volume); mMediaPlayer.setVolume(volume, volume);
} else if ("media_button".equals(key)) { } else if ("media_button".equals(key)) {
MediaButtonHandler.reloadPreference(); MediaButtonHandler.reloadPreference(this);
} else if ("use_idle_timeout".equals(key) || "idle_timeout".equals(key)) { } else if ("use_idle_timeout".equals(key) || "idle_timeout".equals(key)) {
mIdleTimeout = settings.getBoolean("use_idle_timeout", false) ? settings.getInt("idle_timeout", 3600) : 0; mIdleTimeout = settings.getBoolean("use_idle_timeout", false) ? settings.getInt("idle_timeout", 3600) : 0;
userActionTriggered(); userActionTriggered();
@ -417,7 +417,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
mMediaPlayer.start(); mMediaPlayer.start();
if (mNotificationMode != NEVER) if (mNotificationMode != NEVER)
startForeground(NOTIFICATION_ID, new SongNotification(mCurrentSong, true)); startForeground(NOTIFICATION_ID, new SongNotification(this, mCurrentSong, true));
if (mWakeLock != null) if (mWakeLock != null)
mWakeLock.acquire(); mWakeLock.acquire();
@ -427,7 +427,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
if (mNotificationMode == ALWAYS) { if (mNotificationMode == ALWAYS) {
stopForeground(false); stopForeground(false);
mNotificationManager.notify(NOTIFICATION_ID, new SongNotification(mCurrentSong, false)); mNotificationManager.notify(NOTIFICATION_ID, new SongNotification(this, mCurrentSong, false));
} else { } else {
stopForeground(true); stopForeground(true);
} }
@ -512,7 +512,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
private void updateNotification() private void updateNotification()
{ {
if ((mNotificationMode == ALWAYS || mNotificationMode == WHEN_PLAYING && (mState & FLAG_PLAYING) != 0) && mCurrentSong != null) if ((mNotificationMode == ALWAYS || mNotificationMode == WHEN_PLAYING && (mState & FLAG_PLAYING) != 0) && mCurrentSong != null)
mNotificationManager.notify(NOTIFICATION_ID, new SongNotification(mCurrentSong, (mState & FLAG_PLAYING) != 0)); mNotificationManager.notify(NOTIFICATION_ID, new SongNotification(this, mCurrentSong, (mState & FLAG_PLAYING) != 0));
else else
mNotificationManager.cancel(NOTIFICATION_ID); mNotificationManager.cancel(NOTIFICATION_ID);
} }
@ -587,7 +587,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
Song song = mTimeline.shiftCurrentSong(delta); Song song = mTimeline.shiftCurrentSong(delta);
mCurrentSong = song; mCurrentSong = song;
if (song == null || song.id == -1 || song.path == null) { if (song == null || song.id == -1 || song.path == null) {
if (Song.isSongAvailable()) { if (Song.isSongAvailable(this)) {
int flag = (mState & FLAG_RANDOM) == 0 ? FLAG_EMPTY_QUEUE : FLAG_ERROR; int flag = (mState & FLAG_RANDOM) == 0 ? FLAG_EMPTY_QUEUE : FLAG_ERROR;
synchronized (mStateLock) { synchronized (mStateLock) {
updateState((mState | flag) & ~FLAG_NO_MEDIA); updateState((mState | flag) & ~FLAG_NO_MEDIA);
@ -713,7 +713,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
switch (state) { switch (state) {
case TelephonyManager.CALL_STATE_RINGING: case TelephonyManager.CALL_STATE_RINGING:
case TelephonyManager.CALL_STATE_OFFHOOK: { case TelephonyManager.CALL_STATE_OFFHOOK: {
MediaButtonHandler buttons = MediaButtonHandler.getInstance(); MediaButtonHandler buttons = MediaButtonHandler.getInstance(PlaybackService.this);
if (buttons != null) if (buttons != null)
buttons.setInCall(true); buttons.setInCall(true);
@ -726,7 +726,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
break; break;
} }
case TelephonyManager.CALL_STATE_IDLE: { case TelephonyManager.CALL_STATE_IDLE: {
MediaButtonHandler buttons = MediaButtonHandler.getInstance(); MediaButtonHandler buttons = MediaButtonHandler.getInstance(PlaybackService.this);
if (buttons != null) if (buttons != null)
buttons.setInCall(false); buttons.setInCall(false);
@ -742,7 +742,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
public void onMediaChange() public void onMediaChange()
{ {
if (Song.isSongAvailable()) { if (Song.isSongAvailable(this)) {
if ((mState & FLAG_NO_MEDIA) != 0) if ((mState & FLAG_NO_MEDIA) != 0)
setCurrentSong(0); setCurrentSong(0);
} else { } else {
@ -804,7 +804,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
case SAVE_STATE: case SAVE_STATE:
// For unexpected terminations: crashes, task killers, etc. // For unexpected terminations: crashes, task killers, etc.
// In most cases onDestroy will handle this // In most cases onDestroy will handle this
mTimeline.saveState(this, 0); mTimeline.saveState(0);
break; break;
case PROCESS_SONG: case PROCESS_SONG:
processSong((Song)message.obj); processSong((Song)message.obj);
@ -994,7 +994,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
id = current.albumId; id = current.albumId;
break; break;
case MediaUtils.TYPE_GENRE: case MediaUtils.TYPE_GENRE:
id = MediaUtils.queryGenreForSong(current.id); id = MediaUtils.queryGenreForSong(this, current.id);
break; break;
default: default:
throw new IllegalArgumentException("Unsupported media type: " + type); throw new IllegalArgumentException("Unsupported media type: " + type);

View File

@ -25,6 +25,7 @@ package org.kreed.vanilla;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.provider.MediaStore; import android.provider.MediaStore;
@ -56,11 +57,12 @@ public class Playlist {
/** /**
* Queries all the playlists known to the MediaStore. * Queries all the playlists known to the MediaStore.
* *
* @param context A context to use.
* @return An array of Playlists * @return An array of Playlists
*/ */
public static Playlist[] getPlaylists() public static Playlist[] getPlaylists(Context context)
{ {
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
Uri media = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI; Uri media = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
String[] projection = { MediaStore.Audio.Playlists._ID, MediaStore.Audio.Playlists.NAME }; String[] projection = { MediaStore.Audio.Playlists._ID, MediaStore.Audio.Playlists.NAME };
String sort = MediaStore.Audio.Playlists.NAME; String sort = MediaStore.Audio.Playlists.NAME;
@ -87,15 +89,16 @@ public class Playlist {
/** /**
* Retrieves the id for a playlist with the given name. * Retrieves the id for a playlist with the given name.
* *
* @param context A context to use.
* @param name The name of the playlist. * @param name The name of the playlist.
* @return The id of the playlist, or -1 if there is no playlist with the * @return The id of the playlist, or -1 if there is no playlist with the
* given name. * given name.
*/ */
public static long getPlaylist(String name) public static long getPlaylist(Context context, String name)
{ {
long id = -1; long id = -1;
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, Cursor cursor = resolver.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
new String[] { MediaStore.Audio.Playlists._ID }, new String[] { MediaStore.Audio.Playlists._ID },
MediaStore.Audio.Playlists.NAME + "=?", MediaStore.Audio.Playlists.NAME + "=?",
@ -114,13 +117,14 @@ public class Playlist {
* Create a new playlist with the given name. If a playlist with the given * Create a new playlist with the given name. If a playlist with the given
* name already exists, it will be overwritten. * name already exists, it will be overwritten.
* *
* @param context A context to use.
* @param name The name of the playlist. * @param name The name of the playlist.
* @return The id of the new playlist. * @return The id of the new playlist.
*/ */
public static long createPlaylist(String name) public static long createPlaylist(Context context, String name)
{ {
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
long id = getPlaylist(name); long id = getPlaylist(context, name);
if (id == -1) { if (id == -1) {
// We need to create a new playlist. // We need to create a new playlist.
@ -140,14 +144,15 @@ public class Playlist {
/** /**
* Add the given set of song ids to the playlist with the given id. * Add the given set of song ids to the playlist with the given id.
* *
* @param context A context to use.
* @param playlistId The MediaStore.Audio.Playlist id of the playlist to * @param playlistId The MediaStore.Audio.Playlist id of the playlist to
* modify. * modify.
* @param toAdd The MediaStore ids of all the songs to be added to the * @param toAdd The MediaStore ids of all the songs to be added to the
* playlist. * playlist.
*/ */
public static void addToPlaylist(long playlistId, long[] toAdd) public static void addToPlaylist(Context context, long playlistId, long[] toAdd)
{ {
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId); Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId);
String[] projection = new String[] { MediaStore.Audio.Playlists.Members.PLAY_ORDER }; String[] projection = new String[] { MediaStore.Audio.Playlists.Members.PLAY_ORDER };
Cursor cursor = resolver.query(uri, projection, null, null, null); Cursor cursor = resolver.query(uri, projection, null, null, null);
@ -168,31 +173,33 @@ public class Playlist {
/** /**
* Delete the playlist with the given id. * Delete the playlist with the given id.
* *
* @param context A context to use.
* @param id The Media.Audio.Playlists id of the playlist. * @param id The Media.Audio.Playlists id of the playlist.
*/ */
public static void deletePlaylist(long id) public static void deletePlaylist(Context context, long id)
{ {
Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, id); Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, id);
ContextApplication.getContext().getContentResolver().delete(uri, null, null); context.getContentResolver().delete(uri, null, null);
} }
/** /**
* Rename the playlist with the given id. * Rename the playlist with the given id.
* *
* @param context A context to use.
* @param id The Media.Audio.Playlists id of the playlist. * @param id The Media.Audio.Playlists id of the playlist.
* @param newName The new name for the playlist. * @param newName The new name for the playlist.
*/ */
public static void renamePlaylist(long id, String newName) public static void renamePlaylist(Context context, long id, String newName)
{ {
long existingId = getPlaylist(newName); long existingId = getPlaylist(context, newName);
// We are already called the requested name; nothing to do. // We are already called the requested name; nothing to do.
if (existingId == id) if (existingId == id)
return; return;
// There is already a playlist with this name. Kill it. // There is already a playlist with this name. Kill it.
if (existingId != -1) if (existingId != -1)
deletePlaylist(existingId); deletePlaylist(context, existingId);
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues(1); ContentValues values = new ContentValues(1);
values.put(MediaStore.Audio.Playlists.NAME, newName); values.put(MediaStore.Audio.Playlists.NAME, newName);
resolver.update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, values, MediaStore.Audio.Playlists._ID + "=" + id, null); resolver.update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, values, MediaStore.Audio.Playlists._ID + "=" + id, null);

View File

@ -146,13 +146,16 @@ public class Song {
public int flags; public int flags;
/** /**
* @return true if it's possible to retrieve any songs, otherwise false. For example, false * Determine if any songs are available from the library.
* could be returned if there are no songs in the library. *
* @param context A context to use.
* @return True if it's possible to retrieve any songs, false otherwise. For
* example, false could be returned if there are no songs in the library.
*/ */
public static boolean isSongAvailable() public static boolean isSongAvailable(Context context)
{ {
if (mSongCount == -1) { if (mSongCount == -1) {
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0"; String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0";
Cursor cursor = resolver.query(media, new String[]{"count(_id)"}, selection, null, null); Cursor cursor = resolver.query(media, new String[]{"count(_id)"}, selection, null, null);
@ -169,13 +172,15 @@ public class Song {
/** /**
* Returns a shuffled array contaning the ids of all the songs on the * Returns a shuffled array contaning the ids of all the songs on the
* device's library. * device's library.
*
* @param context A context to use.
*/ */
public static long[] loadAllSongs() public static long[] loadAllSongs(Context context)
{ {
mAllSongsIdx = 0; mAllSongsIdx = 0;
mRandomCacheEnd = -1; mRandomCacheEnd = -1;
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0"; String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0";
Cursor cursor = resolver.query(media, EMPTY_PROJECTION, selection, null, null); Cursor cursor = resolver.query(media, EMPTY_PROJECTION, selection, null, null);
@ -208,13 +213,15 @@ public class Song {
/** /**
* Returns a song randomly selected from all the songs in the Android * Returns a song randomly selected from all the songs in the Android
* MediaStore. * MediaStore.
*
* @param context A context to use.
*/ */
public static Song randomSong() public static Song randomSong(Context context)
{ {
long[] songs = mAllSongs; long[] songs = mAllSongs;
if (songs == null) { if (songs == null) {
songs = loadAllSongs(); songs = loadAllSongs(context);
if (songs == null) if (songs == null)
return null; return null;
mAllSongs = songs; mAllSongs = songs;
@ -225,7 +232,7 @@ public class Song {
} }
if (mAllSongsIdx >= mRandomCacheEnd) { if (mAllSongsIdx >= mRandomCacheEnd) {
ContentResolver resolver = ContextApplication.getContext().getContentResolver(); ContentResolver resolver = context.getContentResolver();
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
StringBuilder selection = new StringBuilder("_ID IN ("); StringBuilder selection = new StringBuilder("_ID IN (");
@ -346,9 +353,10 @@ public class Song {
/** /**
* Query the album art for this song. * 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 * @return The album art or null if no album art could be found
*/ */
public Bitmap getCover() public Bitmap getCover(Context context)
{ {
if (id == -1 || mDisableCoverArt) if (id == -1 || mDisableCoverArt)
return null; return null;
@ -358,10 +366,17 @@ public class Song {
if (cover != null) if (cover != null)
return cover; return cover;
Context context = ContextApplication.getContext();
ContentResolver res = context.getContentResolver(); ContentResolver res = context.getContentResolver();
cover = getCoverFromMediaFile(res); try {
ParcelFileDescriptor parcelFileDescriptor = res.openFileDescriptor(getCoverUri(), "r");
if (parcelFileDescriptor != null) {
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
cover = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, BITMAP_OPTIONS);
}
} catch (IllegalStateException e) {
} catch (FileNotFoundException e) {
}
Bitmap deletedCover = mCoverCache.put(id, cover); Bitmap deletedCover = mCoverCache.put(id, cover);
if (deletedCover != null) if (deletedCover != null)
@ -384,30 +399,6 @@ public class Song {
return Uri.parse("content://media/external/audio/media/" + id + "/albumart"); return Uri.parse("content://media/external/audio/media/" + id + "/albumart");
} }
/**
* Attempts to read the album art directly from a media file using the
* media ContentProvider.
*
* @param resolver A ContentResolver to use.
* @return The album art or null if no album art could be found.
*/
private Bitmap getCoverFromMediaFile(ContentResolver resolver)
{
Bitmap cover = null;
try {
ParcelFileDescriptor parcelFileDescriptor = resolver.openFileDescriptor(getCoverUri(), "r");
if (parcelFileDescriptor != null) {
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
cover = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, BITMAP_OPTIONS);
}
} catch (IllegalStateException e) {
} catch (FileNotFoundException e) {
}
return cover;
}
@Override @Override
public String toString() public String toString()
{ {

View File

@ -62,9 +62,8 @@ public class SongNotification extends Notification {
* @param song The Song to display information about. * @param song The Song to display information about.
* @param playing True if music is playing. * @param playing True if music is playing.
*/ */
public SongNotification(Song song, boolean playing) public SongNotification(Context context, Song song, boolean playing)
{ {
Context context = ContextApplication.getContext();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
int action = Integer.parseInt(prefs.getString("notification_action", "0")); int action = Integer.parseInt(prefs.getString("notification_action", "0"));
int statusIcon = playing ? R.drawable.status_icon : R.drawable.status_icon_paused; int statusIcon = playing ? R.drawable.status_icon : R.drawable.status_icon_paused;

View File

@ -448,7 +448,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
menu.add(0, MENU_DELETE, 0, R.string.delete); menu.add(0, MENU_DELETE, 0, R.string.delete);
playlistMenu.add(type, MENU_NEW_PLAYLIST, id, R.string.new_playlist); playlistMenu.add(type, MENU_NEW_PLAYLIST, id, R.string.new_playlist);
Playlist[] playlists = Playlist.getPlaylists(); Playlist[] playlists = Playlist.getPlaylists(this);
if (playlists != null) { if (playlists != null) {
for (int i = 0; i != playlists.length; ++i) for (int i = 0; i != playlists.length; ++i)
playlistMenu.add(type, (int)playlists[i].id + 100, id, playlists[i].name); playlistMenu.add(type, (int)playlists[i].id + 100, id, playlists[i].name);
@ -470,8 +470,8 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
*/ */
private void addToPlaylist(long playlistId, int type, long mediaId, CharSequence title) private void addToPlaylist(long playlistId, int type, long mediaId, CharSequence title)
{ {
long[] ids = MediaUtils.getAllSongIdsWith(type, mediaId); long[] ids = MediaUtils.getAllSongIdsWith(this, type, mediaId);
Playlist.addToPlaylist(playlistId, ids); Playlist.addToPlaylist(this, playlistId, ids);
String message = getResources().getQuantityString(R.plurals.added_to_playlist, ids.length, ids.length, title); String message = getResources().getQuantityString(R.plurals.added_to_playlist, ids.length, ids.length, title);
Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
@ -489,7 +489,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
private void delete(int type, long id, String title) private void delete(int type, long id, String title)
{ {
if (type == MediaUtils.TYPE_PLAYLIST) { if (type == MediaUtils.TYPE_PLAYLIST) {
Playlist.deletePlaylist(id); Playlist.deletePlaylist(this, id);
String message = getResources().getString(R.string.playlist_deleted, title); String message = getResources().getString(R.string.playlist_deleted, title);
Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
} else { } else {
@ -635,7 +635,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
NewPlaylistDialog dialog = (NewPlaylistDialog)message.obj; NewPlaylistDialog dialog = (NewPlaylistDialog)message.obj;
if (dialog.isAccepted()) { if (dialog.isAccepted()) {
String name = dialog.getText(); String name = dialog.getText();
long playlistId = Playlist.createPlaylist(name); long playlistId = Playlist.createPlaylist(this, name);
addToPlaylist(playlistId, message.arg1, message.arg2, name); addToPlaylist(playlistId, message.arg1, message.arg2, name);
} }
break; break;
@ -646,7 +646,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
case MSG_RENAME_PLAYLIST: { case MSG_RENAME_PLAYLIST: {
NewPlaylistDialog dialog = (NewPlaylistDialog)message.obj; NewPlaylistDialog dialog = (NewPlaylistDialog)message.obj;
if (dialog.isAccepted()) if (dialog.isAccepted())
Playlist.renamePlaylist(message.arg2, dialog.getText()); Playlist.renamePlaylist(this, message.arg2, dialog.getText());
} }
default: default:
return super.handleMessage(message); return super.handleMessage(message);
@ -700,11 +700,11 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
mStatusText.setText(text); mStatusText.setText(text);
if (mCoverSize == -1) { if (mCoverSize == -1) {
DisplayMetrics metrics = ContextApplication.getContext().getResources().getDisplayMetrics(); DisplayMetrics metrics = getResources().getDisplayMetrics();
mCoverSize = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 64, metrics); mCoverSize = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 64, metrics);
} }
Bitmap cover = CoverBitmap.createScaledBitmap(song, mCoverSize, mCoverSize); Bitmap cover = CoverBitmap.createScaledBitmap(this, song, mCoverSize, mCoverSize);
mCover.setImageBitmap(cover); mCover.setImageBitmap(cover);
mCover.setVisibility(cover == null ? View.GONE : View.VISIBLE); mCover.setVisibility(cover == null ? View.GONE : View.VISIBLE);
} }

View File

@ -92,6 +92,8 @@ public final class SongTimeline {
*/ */
private static final long STATE_FILE_MAGIC = 0xf89daa2fac33L; private static final long STATE_FILE_MAGIC = 0xf89daa2fac33L;
private Context mContext;
/** /**
* All the songs currently contained in the timeline. Each Song object * All the songs currently contained in the timeline. Each Song object
* should be unique, even if it refers to the same media. * should be unique, even if it refers to the same media.
@ -142,6 +144,11 @@ public final class SongTimeline {
*/ */
private Callback mCallback; private Callback mCallback;
public SongTimeline(Context context)
{
mContext = context;
}
/** /**
* Compares the ids of songs. * Compares the ids of songs.
*/ */
@ -172,16 +179,15 @@ public final class SongTimeline {
* Initializes the timeline with the state stored in the state file created * Initializes the timeline with the state stored in the state file created
* by a call to save state. * by a call to save state.
* *
* @param context The Context to open the state file with
* @return The optional extra data, or -1 if loading failed * @return The optional extra data, or -1 if loading failed
*/ */
public int loadState(Context context) public int loadState()
{ {
int extra = -1; int extra = -1;
try { try {
synchronized (this) { synchronized (this) {
DataInputStream in = new DataInputStream(context.openFileInput(STATE_FILE)); DataInputStream in = new DataInputStream(mContext.openFileInput(STATE_FILE));
if (in.readLong() == STATE_FILE_MAGIC) { if (in.readLong() == STATE_FILE_MAGIC) {
int n = in.readInt(); int n = in.readInt();
if (n > 0) { if (n > 0) {
@ -207,7 +213,7 @@ public final class SongTimeline {
// return its results in. // return its results in.
Collections.sort(songs, new IdComparator()); Collections.sort(songs, new IdComparator());
ContentResolver resolver = context.getContentResolver(); ContentResolver resolver = mContext.getContentResolver();
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = resolver.query(media, Song.FILLED_PROJECTION, selection.toString(), null, "_id"); Cursor cursor = resolver.query(media, Song.FILLED_PROJECTION, selection.toString(), null, "_id");
@ -263,13 +269,12 @@ public final class SongTimeline {
* This can be passed to the appropriate constructor to initialize the * This can be passed to the appropriate constructor to initialize the
* timeline with this state. * timeline with this state.
* *
* @param context The Context to open the state file with
* @param extra Optional extra data to be included. Should not be -1. * @param extra Optional extra data to be included. Should not be -1.
*/ */
public void saveState(Context context, int extra) public void saveState(int extra)
{ {
try { try {
DataOutputStream out = new DataOutputStream(context.openFileOutput(STATE_FILE, 0)); DataOutputStream out = new DataOutputStream(mContext.openFileOutput(STATE_FILE, 0));
out.writeLong(STATE_FILE_MAGIC); out.writeLong(STATE_FILE_MAGIC);
synchronized (this) { synchronized (this) {
@ -360,7 +365,7 @@ public final class SongTimeline {
private Song shuffleAll() private Song shuffleAll()
{ {
ArrayList<Song> songs = new ArrayList<Song>(mSongs); ArrayList<Song> songs = new ArrayList<Song>(mSongs);
Collections.shuffle(songs, ContextApplication.getRandom()); Collections.shuffle(songs, MediaUtils.getRandom());
mShuffledSongs = songs; mShuffledSongs = songs;
return songs.get(0); return songs.get(0);
} }
@ -401,7 +406,7 @@ public final class SongTimeline {
song = timeline.get(0); song = timeline.get(0);
break; break;
case FINISH_RANDOM: case FINISH_RANDOM:
song = Song.randomSong(); song = Song.randomSong(mContext);
timeline.add(song); timeline.add(song);
break; break;
default: default:
@ -473,9 +478,9 @@ public final class SongTimeline {
{ {
Cursor cursor; Cursor cursor;
if (type == MediaUtils.TYPE_PLAYLIST) if (type == MediaUtils.TYPE_PLAYLIST)
cursor = MediaUtils.query(type, id, Song.FILLED_PLAYLIST_PROJECTION, selection); cursor = MediaUtils.query(mContext, type, id, Song.FILLED_PLAYLIST_PROJECTION, selection);
else else
cursor = MediaUtils.query(type, id, Song.FILLED_PROJECTION, selection); cursor = MediaUtils.query(mContext, type, id, Song.FILLED_PROJECTION, selection);
if (cursor == null) if (cursor == null)
return 0; return 0;
int count = cursor.getCount(); int count = cursor.getCount();
@ -516,7 +521,7 @@ public final class SongTimeline {
} }
if (mShuffle) if (mShuffle)
Collections.shuffle(timeline.subList(start, timeline.size()), ContextApplication.getRandom()); Collections.shuffle(timeline.subList(start, timeline.size()), MediaUtils.getRandom());
broadcastChangedSongs(); broadcastChangedSongs();
} }