From 2023409bd963892425146f9b14f1e3457ce1af6d Mon Sep 17 00:00:00 2001 From: Christopher Eby Date: Mon, 19 Sep 2011 21:53:39 -0500 Subject: [PATCH] Remove ContextApplication --- AndroidManifest.xml | 3 +- src/org/kreed/vanilla/ContextApplication.java | 59 ----------------- src/org/kreed/vanilla/CoverBitmap.java | 47 ++++++++------ src/org/kreed/vanilla/CoverView.java | 2 +- src/org/kreed/vanilla/MediaAdapter.java | 9 ++- src/org/kreed/vanilla/MediaButtonHandler.java | 55 ++++++++-------- .../kreed/vanilla/MediaButtonReceiver.java | 2 +- src/org/kreed/vanilla/MediaUtils.java | 56 +++++++++++------ src/org/kreed/vanilla/NewPlaylistDialog.java | 4 +- src/org/kreed/vanilla/PlaybackActivity.java | 6 +- src/org/kreed/vanilla/PlaybackService.java | 28 ++++----- src/org/kreed/vanilla/Playlist.java | 37 ++++++----- src/org/kreed/vanilla/Song.java | 63 ++++++++----------- src/org/kreed/vanilla/SongNotification.java | 3 +- src/org/kreed/vanilla/SongSelector.java | 16 ++--- src/org/kreed/vanilla/SongTimeline.java | 29 +++++---- 16 files changed, 197 insertions(+), 222 deletions(-) delete mode 100644 src/org/kreed/vanilla/ContextApplication.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 1a61f50c..9d392d06 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -27,8 +27,7 @@ THE SOFTWARE. android:installLocation="auto"> + android:label="@string/app_name"> diff --git a/src/org/kreed/vanilla/ContextApplication.java b/src/org/kreed/vanilla/ContextApplication.java deleted file mode 100644 index e04e2aeb..00000000 --- a/src/org/kreed/vanilla/ContextApplication.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2010, 2011 Christopher Eby - * - * 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; - } -} diff --git a/src/org/kreed/vanilla/CoverBitmap.java b/src/org/kreed/vanilla/CoverBitmap.java index caabc846..63e3b790 100644 --- a/src/org/kreed/vanilla/CoverBitmap.java +++ b/src/org/kreed/vanilla/CoverBitmap.java @@ -22,6 +22,7 @@ package org.kreed.vanilla; +import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -66,10 +67,12 @@ public final class CoverBitmap { /** * 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_BIG = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, metrics); PADDING = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, metrics); @@ -78,10 +81,12 @@ public final class CoverBitmap { /** * 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); SONG_ICON = Bitmap.createScaledBitmap(bitmap, TEXT_SIZE, TEXT_SIZE, false); bitmap.recycle(); @@ -118,6 +123,7 @@ public final class CoverBitmap { * Create an image representing the given song. Includes cover art and * possibly song title/artist/ablum, depending on the given style. * + * @param context A context to use. * @param style One of CoverBitmap.STYLE_* * @param song The song to display information for * @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 * 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) { case STYLE_OVERLAPPING_BOX: - return createOverlappingBitmap(song, width, height, bitmap); + return createOverlappingBitmap(context, song, width, height, bitmap); case STYLE_INFO_BELOW: - return createSeparatedBitmap(song, width, height, bitmap); + return createSeparatedBitmap(context, song, width, height, bitmap); case STYLE_NO_INFO: - return createScaledBitmap(song, width, height); + return createScaledBitmap(context, song, width, height); case STYLE_NO_INFO_ZOOMED: - return createZoomedBitmap(song, width, height, bitmap); + return createZoomedBitmap(context, song, width, height, bitmap); default: 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) return null; if (TEXT_SIZE == -1) - loadTextSizes(); + loadTextSizes(context); Paint paint = new Paint(); paint.setAntiAlias(true); @@ -158,7 +164,7 @@ public final class CoverBitmap { String title = song.title == null ? "" : song.title; String album = song.album == null ? "" : song.album; String artist = song.artist == null ? "" : song.artist; - Bitmap cover = song.getCover(); + Bitmap cover = song.getCover(context); int titleSize = TEXT_SIZE_BIG; int subSize = TEXT_SIZE; @@ -238,15 +244,15 @@ public final class CoverBitmap { 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) return null; if (TEXT_SIZE == -1) - loadTextSizes(); + loadTextSizes(context); if (SONG_ICON == null) - loadIcons(); + loadIcons(context); boolean horizontal = width > height; @@ -256,7 +262,7 @@ public final class CoverBitmap { String title = song.title == null ? "" : song.title; String album = song.album == null ? "" : song.album; String artist = song.artist == null ? "" : song.artist; - Bitmap cover = song.getCover(); + Bitmap cover = song.getCover(context); int textSize = TEXT_SIZE; int padding = PADDING; @@ -340,12 +346,12 @@ public final class CoverBitmap { 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) return null; - Bitmap cover = song.getCover(); + Bitmap cover = song.getCover(context); if (cover == null) return null; @@ -379,17 +385,18 @@ public final class CoverBitmap { * preserved. At least one dimension of the result will match the provided * dimension exactly. * + * @param context A context to use. * @param song The song to display information for * @param width Maximum width of image * @param height Maximum height of image * @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) return null; - Bitmap cover = song.getCover(); + Bitmap cover = song.getCover(context); if (cover == null) return null; diff --git a/src/org/kreed/vanilla/CoverView.java b/src/org/kreed/vanilla/CoverView.java index c1d4be16..2310fc08 100644 --- a/src/org/kreed/vanilla/CoverView.java +++ b/src/org/kreed/vanilla/CoverView.java @@ -326,7 +326,7 @@ public final class CoverView extends View implements Handler.Callback { Bitmap bitmap = mBitmapCache.get(song.id); 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(); } else { mBitmapCache.touch(song.id); diff --git a/src/org/kreed/vanilla/MediaAdapter.java b/src/org/kreed/vanilla/MediaAdapter.java index 92c83ca0..67f42314 100644 --- a/src/org/kreed/vanilla/MediaAdapter.java +++ b/src/org/kreed/vanilla/MediaAdapter.java @@ -51,7 +51,7 @@ import java.io.Serializable; * MediaAdapter provides an adapter backed by a MediaStore content provider. * It generates simple one- or two-line text views to display each media * element. - * + * * Filtering is supported, as is a more specific type of filtering referred to * as limiting. Limiting is separate from filtering; a new filter will not * erase an active filter. Limiting is intended to allow only media belonging @@ -59,6 +59,8 @@ import java.io.Serializable; * See MediaView.getLimiter and setLimiter for details. */ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider { + private Context mContext; + /** * The type of media represented by this adapter. Must be one of the * 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); + mContext = context; mType = type; mExpandable = expandable; mLimiter = limiter; @@ -225,7 +228,7 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider { */ public Cursor runQuery(CharSequence constraint) { - ContentResolver resolver = ContextApplication.getContext().getContentResolver(); + ContentResolver resolver = mContext.getContentResolver(); String[] projection; if (mFields.length == 1) @@ -279,7 +282,7 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider { if (mLimiter.type == MediaUtils.TYPE_GENRE) { // Genre is not standard metadata for MediaStore.Audio.Media. // 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 { if (selection.length() != 0) selection.append(" AND "); diff --git a/src/org/kreed/vanilla/MediaButtonHandler.java b/src/org/kreed/vanilla/MediaButtonHandler.java index c55819a5..ed2d6c8b 100644 --- a/src/org/kreed/vanilla/MediaButtonHandler.java +++ b/src/org/kreed/vanilla/MediaButtonHandler.java @@ -56,6 +56,8 @@ public class MediaButtonHandler { * uninitialized. */ private static int mUseControls = -1; + + private Context mContext; /** * Whether the phone is currently in a call. 1 for yes, 0 for no, -1 for * uninitialized. @@ -75,11 +77,11 @@ public class MediaButtonHandler { * Retrieve the MediaButtonHandler singleton, creating it if necessary. * 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) - mInstance = new MediaButtonHandler(); + mInstance = new MediaButtonHandler(context.getApplicationContext()); return mInstance; } return null; @@ -88,10 +90,9 @@ public class MediaButtonHandler { /** * Construct a MediaButtonHandler. */ - private MediaButtonHandler() + private MediaButtonHandler(Context context) { - Context context = ContextApplication.getContext(); - + mContext = context; mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); mButtonReceiver = new ComponentName(context.getPackageName(), MediaButtonReceiver.class.getName()); 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 = settings.getBoolean("media_button", true) ? 1 : 0; - } - - public static void reloadPreference() - { - loadPreference(); - if (useHeadsetControls()) { - getInstance().registerMediaButton(); + mUseControls = -1; + if (useHeadsetControls(context)) { + getInstance(context).registerMediaButton(); } else { unregisterMediaButton(); } @@ -121,11 +121,16 @@ public class MediaButtonHandler { /** * Return whether headset controls should be used, loading the preference * if necessary. + * + * @param context A context to use. */ - public static boolean useHeadsetControls() + public static boolean useHeadsetControls(Context context) { - if (mUseControls == -1) - loadPreference(); + if (mUseControls == -1) { + SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); + mUseControls = settings.getBoolean("media_button", true) ? 1 : 0; + } + return mUseControls == 1; } @@ -135,8 +140,7 @@ public class MediaButtonHandler { private boolean isInCall() { if (mInCall == -1) { - Context context = ContextApplication.getContext(); - TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); + TelephonyManager manager = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); mInCall = (byte)(manager.getCallState() == TelephonyManager.CALL_STATE_IDLE ? 0 : 1); } return mInCall == 1; @@ -157,12 +161,11 @@ public class MediaButtonHandler { * * @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(context, PlaybackService.class); + Intent intent = new Intent(mContext, PlaybackService.class); intent.setAction(action); - context.startService(intent); + mContext.startService(intent); } /** @@ -170,7 +173,7 @@ public class MediaButtonHandler { */ public boolean processKey(KeyEvent event) { - if (event == null || isInCall() || !useHeadsetControls()) + if (event == null || isInCall() || !useHeadsetControls(mContext)) return false; int action = event.getAction(); diff --git a/src/org/kreed/vanilla/MediaButtonReceiver.java b/src/org/kreed/vanilla/MediaButtonReceiver.java index c2576282..ca2e7d16 100644 --- a/src/org/kreed/vanilla/MediaButtonReceiver.java +++ b/src/org/kreed/vanilla/MediaButtonReceiver.java @@ -31,7 +31,7 @@ public class MediaButtonReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) { - boolean handled = MediaButtonHandler.getInstance().process(intent); + boolean handled = MediaButtonHandler.getInstance(context).process(intent); if (handled && isOrderedBroadcast()) abortBroadcast(); } diff --git a/src/org/kreed/vanilla/MediaUtils.java b/src/org/kreed/vanilla/MediaUtils.java index 2076adea..e7236948 100644 --- a/src/org/kreed/vanilla/MediaUtils.java +++ b/src/org/kreed/vanilla/MediaUtils.java @@ -53,18 +53,34 @@ public class MediaUtils { */ 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 * album of the specified id. * + * @param context A context to use. * @param type One of the TYPE_* constants, excluding playlists. * @param id The MediaStore id of the artist or album. * @param projection The columns to query. * @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; StringBuilder selection = new StringBuilder(); @@ -99,12 +115,13 @@ public class MediaUtils { * Return a cursor containing the ids of all the songs in the playlist * with the given id. * + * @param context A context to use. * @param id The id of the playlist in MediaStore.Audio.Playlists. * @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); String sort = MediaStore.Audio.Playlists.Members.PLAY_ORDER; 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 * with the given id. * + * @param context A context to use. * @param id The id of the genre in MediaStore.Audio.Genres. * @param projection The columns to query. * @param selection The selection to pass to the query, or null. * @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); String sort = MediaStore.Audio.Genres.Members.TITLE_KEY; return resolver.query(uri, projection, selection, selectionArgs, sort); @@ -130,6 +148,7 @@ public class MediaUtils { /** * 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_* * constants. * @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 * 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) { case TYPE_ARTIST: case TYPE_ALBUM: case TYPE_SONG: - return getMediaCursor(type, id, projection, selection); + return getMediaCursor(context, type, id, projection, selection); case TYPE_PLAYLIST: assert(selection == null); - return getPlaylistCursor(id, projection); + return getPlaylistCursor(context, id, projection); case TYPE_GENRE: - return queryGenre(id, projection, selection, null); + return queryGenre(context, id, projection, selection, null); default: 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 * + * @param context A context to use. * @param type Type the id represents. Must be one of the Song.TYPE_* * constants. * @param id The id of the element in the MediaStore content provider for * 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) return new long[] { id }; Cursor cursor; 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 - cursor = query(type, id, Song.EMPTY_PROJECTION, null); + cursor = query(context, type, id, Song.EMPTY_PROJECTION, null); if (cursor == null) return null; @@ -204,7 +224,7 @@ public class MediaUtils { ContentResolver resolver = context.getContentResolver(); 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) { 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 * 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. Honeycomb introduced an API to query the genre of the song. // We should look into it when ICS is released. - ContentResolver resolver = ContextApplication.getContext().getContentResolver(); + ContentResolver resolver = context.getContentResolver(); // query ids of all the genres Uri uri = MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI; @@ -269,7 +289,7 @@ public class MediaUtils { */ public static void shuffle(long[] list) { - Random random = ContextApplication.getRandom(); + Random random = getRandom(); for (int i = list.length; --i != -1; ) { int j = random.nextInt(i + 1); long tmp = list[j]; @@ -287,7 +307,7 @@ public class MediaUtils { public static void shuffle(Song[] list, int end) { assert(end <= list.length && end >= 0); - Random random = ContextApplication.getRandom(); + Random random = getRandom(); for (int i = end; --i != -1; ) { int j = random.nextInt(i + 1); Song tmp = list[j]; diff --git a/src/org/kreed/vanilla/NewPlaylistDialog.java b/src/org/kreed/vanilla/NewPlaylistDialog.java index ad45c807..0087c12d 100644 --- a/src/org/kreed/vanilla/NewPlaylistDialog.java +++ b/src/org/kreed/vanilla/NewPlaylistDialog.java @@ -125,7 +125,7 @@ public class NewPlaylistDialog extends Dialog implements TextWatcher, View.OnCli mPositiveButton.setEnabled(true); // Update the action button based on whether there is an // 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); } } @@ -150,4 +150,4 @@ public class NewPlaylistDialog extends Dialog implements TextWatcher, View.OnCli break; } } -} \ No newline at end of file +} diff --git a/src/org/kreed/vanilla/PlaybackActivity.java b/src/org/kreed/vanilla/PlaybackActivity.java index 5bf57513..929c135d 100644 --- a/src/org/kreed/vanilla/PlaybackActivity.java +++ b/src/org/kreed/vanilla/PlaybackActivity.java @@ -113,7 +113,7 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View PlaybackService service = PlaybackService.get(this); service.userActionTriggered(); - MediaButtonHandler buttons = MediaButtonHandler.getInstance(); + MediaButtonHandler buttons = MediaButtonHandler.getInstance(this); if (buttons != null) 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_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - return MediaButtonHandler.getInstance().processKey(event); + return MediaButtonHandler.getInstance(this).processKey(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_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - return MediaButtonHandler.getInstance().processKey(event); + return MediaButtonHandler.getInstance(this).processKey(event); } return super.onKeyUp(keyCode, event); diff --git a/src/org/kreed/vanilla/PlaybackService.java b/src/org/kreed/vanilla/PlaybackService.java index bf349651..6703bfce 100644 --- a/src/org/kreed/vanilla/PlaybackService.java +++ b/src/org/kreed/vanilla/PlaybackService.java @@ -184,9 +184,9 @@ public final class PlaybackService extends Service implements Handler.Callback, HandlerThread thread = new HandlerThread("PlaybackService"); thread.start(); - mTimeline = new SongTimeline(); + mTimeline = new SongTimeline(this); mTimeline.setCallback(this); - mPendingSeek = mTimeline.loadState(this); + mPendingSeek = mTimeline.loadState(); mMediaPlayer = new MediaPlayer(); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); @@ -268,7 +268,7 @@ public final class PlaybackService extends Service implements Handler.Callback, } userActionTriggered(); - MediaButtonHandler buttons = MediaButtonHandler.getInstance(); + MediaButtonHandler buttons = MediaButtonHandler.getInstance(this); if (buttons != null) buttons.registerMediaButton(); } @@ -285,7 +285,7 @@ public final class PlaybackService extends Service implements Handler.Callback, stopForeground(true); if (mMediaPlayer != null) { - mTimeline.saveState(this, mMediaPlayer.getCurrentPosition()); + mTimeline.saveState(mMediaPlayer.getCurrentPosition()); mMediaPlayer.release(); mMediaPlayer = null; } @@ -338,7 +338,7 @@ public final class PlaybackService extends Service implements Handler.Callback, if (mMediaPlayer != null) mMediaPlayer.setVolume(volume, volume); } else if ("media_button".equals(key)) { - MediaButtonHandler.reloadPreference(); + MediaButtonHandler.reloadPreference(this); } else if ("use_idle_timeout".equals(key) || "idle_timeout".equals(key)) { mIdleTimeout = settings.getBoolean("use_idle_timeout", false) ? settings.getInt("idle_timeout", 3600) : 0; userActionTriggered(); @@ -417,7 +417,7 @@ public final class PlaybackService extends Service implements Handler.Callback, mMediaPlayer.start(); if (mNotificationMode != NEVER) - startForeground(NOTIFICATION_ID, new SongNotification(mCurrentSong, true)); + startForeground(NOTIFICATION_ID, new SongNotification(this, mCurrentSong, true)); if (mWakeLock != null) mWakeLock.acquire(); @@ -427,7 +427,7 @@ public final class PlaybackService extends Service implements Handler.Callback, if (mNotificationMode == ALWAYS) { stopForeground(false); - mNotificationManager.notify(NOTIFICATION_ID, new SongNotification(mCurrentSong, false)); + mNotificationManager.notify(NOTIFICATION_ID, new SongNotification(this, mCurrentSong, false)); } else { stopForeground(true); } @@ -512,7 +512,7 @@ public final class PlaybackService extends Service implements Handler.Callback, private void updateNotification() { 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 mNotificationManager.cancel(NOTIFICATION_ID); } @@ -587,7 +587,7 @@ public final class PlaybackService extends Service implements Handler.Callback, Song song = mTimeline.shiftCurrentSong(delta); mCurrentSong = song; 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; synchronized (mStateLock) { updateState((mState | flag) & ~FLAG_NO_MEDIA); @@ -713,7 +713,7 @@ public final class PlaybackService extends Service implements Handler.Callback, switch (state) { case TelephonyManager.CALL_STATE_RINGING: case TelephonyManager.CALL_STATE_OFFHOOK: { - MediaButtonHandler buttons = MediaButtonHandler.getInstance(); + MediaButtonHandler buttons = MediaButtonHandler.getInstance(PlaybackService.this); if (buttons != null) buttons.setInCall(true); @@ -726,7 +726,7 @@ public final class PlaybackService extends Service implements Handler.Callback, break; } case TelephonyManager.CALL_STATE_IDLE: { - MediaButtonHandler buttons = MediaButtonHandler.getInstance(); + MediaButtonHandler buttons = MediaButtonHandler.getInstance(PlaybackService.this); if (buttons != null) buttons.setInCall(false); @@ -742,7 +742,7 @@ public final class PlaybackService extends Service implements Handler.Callback, public void onMediaChange() { - if (Song.isSongAvailable()) { + if (Song.isSongAvailable(this)) { if ((mState & FLAG_NO_MEDIA) != 0) setCurrentSong(0); } else { @@ -804,7 +804,7 @@ public final class PlaybackService extends Service implements Handler.Callback, case SAVE_STATE: // For unexpected terminations: crashes, task killers, etc. // In most cases onDestroy will handle this - mTimeline.saveState(this, 0); + mTimeline.saveState(0); break; case PROCESS_SONG: processSong((Song)message.obj); @@ -994,7 +994,7 @@ public final class PlaybackService extends Service implements Handler.Callback, id = current.albumId; break; case MediaUtils.TYPE_GENRE: - id = MediaUtils.queryGenreForSong(current.id); + id = MediaUtils.queryGenreForSong(this, current.id); break; default: throw new IllegalArgumentException("Unsupported media type: " + type); diff --git a/src/org/kreed/vanilla/Playlist.java b/src/org/kreed/vanilla/Playlist.java index f1e041bc..03c205aa 100644 --- a/src/org/kreed/vanilla/Playlist.java +++ b/src/org/kreed/vanilla/Playlist.java @@ -25,6 +25,7 @@ package org.kreed.vanilla; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; +import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.provider.MediaStore; @@ -56,11 +57,12 @@ public class Playlist { /** * Queries all the playlists known to the MediaStore. * + * @param context A context to use. * @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; String[] projection = { MediaStore.Audio.Playlists._ID, 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. * + * @param context A context to use. * @param name The name of the playlist. * @return The id of the playlist, or -1 if there is no playlist with the * given name. */ - public static long getPlaylist(String name) + public static long getPlaylist(Context context, String name) { long id = -1; - ContentResolver resolver = ContextApplication.getContext().getContentResolver(); + ContentResolver resolver = context.getContentResolver(); Cursor cursor = resolver.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, new String[] { MediaStore.Audio.Playlists._ID }, 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 * name already exists, it will be overwritten. * + * @param context A context to use. * @param name The name of the 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(); - long id = getPlaylist(name); + ContentResolver resolver = context.getContentResolver(); + long id = getPlaylist(context, name); if (id == -1) { // 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. * + * @param context A context to use. * @param playlistId The MediaStore.Audio.Playlist id of the playlist to * modify. * @param toAdd The MediaStore ids of all the songs to be added to the * 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); String[] projection = new String[] { MediaStore.Audio.Playlists.Members.PLAY_ORDER }; Cursor cursor = resolver.query(uri, projection, null, null, null); @@ -168,31 +173,33 @@ public class Playlist { /** * Delete the playlist with the given id. * + * @param context A context to use. * @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); - ContextApplication.getContext().getContentResolver().delete(uri, null, null); + context.getContentResolver().delete(uri, null, null); } /** * Rename the playlist with the given id. * + * @param context A context to use. * @param id The Media.Audio.Playlists id of 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. if (existingId == id) return; // There is already a playlist with this name. Kill it. if (existingId != -1) - deletePlaylist(existingId); + deletePlaylist(context, existingId); - ContentResolver resolver = ContextApplication.getContext().getContentResolver(); + ContentResolver resolver = context.getContentResolver(); ContentValues values = new ContentValues(1); values.put(MediaStore.Audio.Playlists.NAME, newName); resolver.update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, values, MediaStore.Audio.Playlists._ID + "=" + id, null); diff --git a/src/org/kreed/vanilla/Song.java b/src/org/kreed/vanilla/Song.java index b2cae8ba..e6651898 100644 --- a/src/org/kreed/vanilla/Song.java +++ b/src/org/kreed/vanilla/Song.java @@ -146,13 +146,16 @@ public class Song { public int flags; /** - * @return true if it's possible to retrieve any songs, otherwise false. For example, false - * could be returned if there are no songs in the library. + * Determine if any songs are available from 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) { - ContentResolver resolver = ContextApplication.getContext().getContentResolver(); + ContentResolver resolver = context.getContentResolver(); Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0"; 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 * device's library. + * + * @param context A context to use. */ - public static long[] loadAllSongs() + public static long[] loadAllSongs(Context context) { mAllSongsIdx = 0; mRandomCacheEnd = -1; - ContentResolver resolver = ContextApplication.getContext().getContentResolver(); + ContentResolver resolver = context.getContentResolver(); Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0"; 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 * MediaStore. + * + * @param context A context to use. */ - public static Song randomSong() + public static Song randomSong(Context context) { long[] songs = mAllSongs; if (songs == null) { - songs = loadAllSongs(); + songs = loadAllSongs(context); if (songs == null) return null; mAllSongs = songs; @@ -225,7 +232,7 @@ public class Song { } if (mAllSongsIdx >= mRandomCacheEnd) { - ContentResolver resolver = ContextApplication.getContext().getContentResolver(); + ContentResolver resolver = context.getContentResolver(); Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; StringBuilder selection = new StringBuilder("_ID IN ("); @@ -346,9 +353,10 @@ public class 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 */ - public Bitmap getCover() + public Bitmap getCover(Context context) { if (id == -1 || mDisableCoverArt) return null; @@ -358,10 +366,17 @@ public class Song { if (cover != null) return cover; - Context context = ContextApplication.getContext(); 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); if (deletedCover != null) @@ -384,30 +399,6 @@ public class Song { 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 public String toString() { diff --git a/src/org/kreed/vanilla/SongNotification.java b/src/org/kreed/vanilla/SongNotification.java index 66a58d52..56e5d253 100644 --- a/src/org/kreed/vanilla/SongNotification.java +++ b/src/org/kreed/vanilla/SongNotification.java @@ -62,9 +62,8 @@ public class SongNotification extends Notification { * @param song The Song to display information about. * @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); int action = Integer.parseInt(prefs.getString("notification_action", "0")); int statusIcon = playing ? R.drawable.status_icon : R.drawable.status_icon_paused; diff --git a/src/org/kreed/vanilla/SongSelector.java b/src/org/kreed/vanilla/SongSelector.java index 6b98f18d..1444b49f 100644 --- a/src/org/kreed/vanilla/SongSelector.java +++ b/src/org/kreed/vanilla/SongSelector.java @@ -448,7 +448,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem menu.add(0, MENU_DELETE, 0, R.string.delete); playlistMenu.add(type, MENU_NEW_PLAYLIST, id, R.string.new_playlist); - Playlist[] playlists = Playlist.getPlaylists(); + Playlist[] playlists = Playlist.getPlaylists(this); if (playlists != null) { for (int i = 0; i != playlists.length; ++i) 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) { - long[] ids = MediaUtils.getAllSongIdsWith(type, mediaId); - Playlist.addToPlaylist(playlistId, ids); + long[] ids = MediaUtils.getAllSongIdsWith(this, type, mediaId); + Playlist.addToPlaylist(this, playlistId, ids); String message = getResources().getQuantityString(R.plurals.added_to_playlist, ids.length, ids.length, title); 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) { if (type == MediaUtils.TYPE_PLAYLIST) { - Playlist.deletePlaylist(id); + Playlist.deletePlaylist(this, id); String message = getResources().getString(R.string.playlist_deleted, title); Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); } else { @@ -635,7 +635,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem NewPlaylistDialog dialog = (NewPlaylistDialog)message.obj; if (dialog.isAccepted()) { String name = dialog.getText(); - long playlistId = Playlist.createPlaylist(name); + long playlistId = Playlist.createPlaylist(this, name); addToPlaylist(playlistId, message.arg1, message.arg2, name); } break; @@ -646,7 +646,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem case MSG_RENAME_PLAYLIST: { NewPlaylistDialog dialog = (NewPlaylistDialog)message.obj; if (dialog.isAccepted()) - Playlist.renamePlaylist(message.arg2, dialog.getText()); + Playlist.renamePlaylist(this, message.arg2, dialog.getText()); } default: return super.handleMessage(message); @@ -700,11 +700,11 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem mStatusText.setText(text); if (mCoverSize == -1) { - DisplayMetrics metrics = ContextApplication.getContext().getResources().getDisplayMetrics(); + DisplayMetrics metrics = getResources().getDisplayMetrics(); 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.setVisibility(cover == null ? View.GONE : View.VISIBLE); } diff --git a/src/org/kreed/vanilla/SongTimeline.java b/src/org/kreed/vanilla/SongTimeline.java index 3c84fdec..e641cc79 100644 --- a/src/org/kreed/vanilla/SongTimeline.java +++ b/src/org/kreed/vanilla/SongTimeline.java @@ -92,6 +92,8 @@ public final class SongTimeline { */ private static final long STATE_FILE_MAGIC = 0xf89daa2fac33L; + private Context mContext; + /** * All the songs currently contained in the timeline. Each Song object * should be unique, even if it refers to the same media. @@ -142,6 +144,11 @@ public final class SongTimeline { */ private Callback mCallback; + public SongTimeline(Context context) + { + mContext = context; + } + /** * 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 * 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 */ - public int loadState(Context context) + public int loadState() { int extra = -1; try { synchronized (this) { - DataInputStream in = new DataInputStream(context.openFileInput(STATE_FILE)); + DataInputStream in = new DataInputStream(mContext.openFileInput(STATE_FILE)); if (in.readLong() == STATE_FILE_MAGIC) { int n = in.readInt(); if (n > 0) { @@ -207,7 +213,7 @@ public final class SongTimeline { // return its results in. Collections.sort(songs, new IdComparator()); - ContentResolver resolver = context.getContentResolver(); + ContentResolver resolver = mContext.getContentResolver(); Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 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 * 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. */ - public void saveState(Context context, int extra) + public void saveState(int extra) { try { - DataOutputStream out = new DataOutputStream(context.openFileOutput(STATE_FILE, 0)); + DataOutputStream out = new DataOutputStream(mContext.openFileOutput(STATE_FILE, 0)); out.writeLong(STATE_FILE_MAGIC); synchronized (this) { @@ -360,7 +365,7 @@ public final class SongTimeline { private Song shuffleAll() { ArrayList songs = new ArrayList(mSongs); - Collections.shuffle(songs, ContextApplication.getRandom()); + Collections.shuffle(songs, MediaUtils.getRandom()); mShuffledSongs = songs; return songs.get(0); } @@ -401,7 +406,7 @@ public final class SongTimeline { song = timeline.get(0); break; case FINISH_RANDOM: - song = Song.randomSong(); + song = Song.randomSong(mContext); timeline.add(song); break; default: @@ -473,9 +478,9 @@ public final class SongTimeline { { Cursor cursor; 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 - cursor = MediaUtils.query(type, id, Song.FILLED_PROJECTION, selection); + cursor = MediaUtils.query(mContext, type, id, Song.FILLED_PROJECTION, selection); if (cursor == null) return 0; int count = cursor.getCount(); @@ -516,7 +521,7 @@ public final class SongTimeline { } if (mShuffle) - Collections.shuffle(timeline.subList(start, timeline.size()), ContextApplication.getRandom()); + Collections.shuffle(timeline.subList(start, timeline.size()), MediaUtils.getRandom()); broadcastChangedSongs(); }