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">
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:name="ContextApplication">
android:label="@string/app_name">
<activity
android:name="LaunchActivity"
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;
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;

View File

@ -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);

View File

@ -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 ");

View File

@ -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();

View File

@ -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();
}

View File

@ -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];

View File

@ -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;
}
}
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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()
{

View File

@ -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;

View File

@ -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);
}

View File

@ -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<Song> songs = new ArrayList<Song>(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();
}