diff --git a/src/org/kreed/vanilla/CoverView.java b/src/org/kreed/vanilla/CoverView.java index da652dfa..b2f82cad 100644 --- a/src/org/kreed/vanilla/CoverView.java +++ b/src/org/kreed/vanilla/CoverView.java @@ -58,6 +58,13 @@ public final class CoverView extends View implements Handler.Callback { */ private int mCoverStyle; + public interface Callback { + public void nextSong(); + public void previousSong(); + } + + private Callback mCallback; + /** * The current set of songs: previous, current, and next. */ @@ -93,40 +100,26 @@ public final class CoverView extends View implements Handler.Callback { } /** - * Setup the Handler to act on the given looper. This must be called before + * Setup the Handler and callback. This must be called before * the CoverView is used. * - * @param looper The Looper to use. + * @param looper A looper created on a worker thread. + * @param callback The callback for nextSong/previousSong + * @param style One of CoverBitmap.STYLE_* */ - public void setupHandler(Looper looper) + public void setup(Looper looper, Callback callback, int style) { mHandler = new Handler(looper, this); - } - - /** - * Set the cover style. Must be one of CoverBitmap.STYLE_. Will only apply - * to bitmaps rendered after this method is called. - */ - public void setCoverStyle(int style) - { + mCallback = callback; mCoverStyle = style; } - /** - * Query the service for initial song info. - */ - public void initialize() - { - mTimelinePos = ContextApplication.getService().getTimelinePos(); - querySongs(true); - } - /** * Move to the next or previous song. * * @param delta -1 or 1, indicate the previous or next song, respectively */ - public void go(int delta) + private void go(int delta) { int i = delta > 0 ? STORE_SIZE - 1 : 0; int from = delta > 0 ? 1 : 0; @@ -134,7 +127,6 @@ public final class CoverView extends View implements Handler.Callback { System.arraycopy(mSongs, from, mSongs, to, STORE_SIZE - 1); mSongs[i] = null; - mTimelinePos += delta; resetScroll(); invalidate(); } @@ -314,8 +306,11 @@ public final class CoverView extends View implements Handler.Callback { } else if (mTentativeCover != -1) { int delta = mTentativeCover - 1; mTentativeCover = -1; - mHandler.sendMessage(mHandler.obtainMessage(PlaybackActivity.MSG_SET_SONG, delta, 0)); - go(delta); + if (delta == 1) + mCallback.nextSong(); + else + mCallback.previousSong(); + resetScroll(); } } @@ -363,6 +358,22 @@ public final class CoverView extends View implements Handler.Callback { } } + public void setCurrentSong(Song song) + { + mTimelinePos = ContextApplication.getService().getTimelinePos(); + + for (int delta = -STORE_SIZE / 2; delta <= STORE_SIZE / 2; ++delta) { + if (mSongs[delta + STORE_SIZE / 2] == song) { + if (delta != 0) + go(delta); + querySongs(false); + return; + } + } + + querySongs(true); + } + /** * Handle an intent broadcasted by the PlaybackService. This must be called * to react to song changes in PlaybackService. @@ -375,12 +386,6 @@ public final class CoverView extends View implements Handler.Callback { if (PlaybackService.EVENT_REPLACE_SONG.equals(action)) { int i = STORE_SIZE / 2 + intent.getIntExtra("pos", 0); setSong(i, (Song)intent.getParcelableExtra("song")); - } else if (PlaybackService.EVENT_CHANGED.equals(action)) { - mTimelinePos = intent.getIntExtra("pos", 0); - Song currentSong = mSongs[STORE_SIZE / 2]; - Song playingSong = intent.getParcelableExtra("song"); - boolean force = currentSong == null || !currentSong.equals(playingSong); - querySongs(force); } } @@ -390,13 +395,6 @@ public final class CoverView extends View implements Handler.Callback { * obj must be the Song to generate a bitmap for. */ private static final int MSG_GENERATE_BITMAP = 0; - /** - * Tell PlaybackService to change the current song. - * - * arg1 should be the delta, -1 or 1, indicating the previous or next song, - * respectively. - */ - static final int MSG_SET_SONG = 1; /** * Perform a long click. * @@ -410,12 +408,6 @@ public final class CoverView extends View implements Handler.Callback { case MSG_GENERATE_BITMAP: generateBitmap((Song)message.obj); break; - case MSG_SET_SONG: - if (message.arg1 == 1) - ContextApplication.getService().nextSong(); - else - ContextApplication.getService().previousSong(); - break; case MSG_LONG_CLICK: mIgnoreNextUp = true; performLongClick(); diff --git a/src/org/kreed/vanilla/FourLongWidget.java b/src/org/kreed/vanilla/FourLongWidget.java index eb7ce33c..6dbffe34 100644 --- a/src/org/kreed/vanilla/FourLongWidget.java +++ b/src/org/kreed/vanilla/FourLongWidget.java @@ -74,7 +74,11 @@ public class FourLongWidget extends AppWidgetProvider { String action = intent.getAction(); if (PlaybackService.EVENT_CHANGED.equals(action) || PlaybackService.EVENT_REPLACE_SONG.equals(action)) { Context context = ContextApplication.getContext(); - Song song = intent.getParcelableExtra("song"); + Song song; + if (intent.hasExtra("song")) + song = intent.getParcelableExtra("song"); + else + song = ContextApplication.getService().getSong(0); int state = intent.getIntExtra("state", -1); AppWidgetManager manager = AppWidgetManager.getInstance(context); diff --git a/src/org/kreed/vanilla/FourSquareWidget.java b/src/org/kreed/vanilla/FourSquareWidget.java index 7448e434..9fc6e444 100644 --- a/src/org/kreed/vanilla/FourSquareWidget.java +++ b/src/org/kreed/vanilla/FourSquareWidget.java @@ -74,7 +74,11 @@ public class FourSquareWidget extends AppWidgetProvider { String action = intent.getAction(); if (PlaybackService.EVENT_CHANGED.equals(action) || PlaybackService.EVENT_REPLACE_SONG.equals(action)) { Context context = ContextApplication.getContext(); - Song song = intent.getParcelableExtra("song"); + Song song; + if (intent.hasExtra("song")) + song = intent.getParcelableExtra("song"); + else + song = ContextApplication.getService().getSong(0); int state = intent.getIntExtra("state", -1); AppWidgetManager manager = AppWidgetManager.getInstance(context); diff --git a/src/org/kreed/vanilla/FullPlaybackActivity.java b/src/org/kreed/vanilla/FullPlaybackActivity.java index b01f11aa..482483db 100644 --- a/src/org/kreed/vanilla/FullPlaybackActivity.java +++ b/src/org/kreed/vanilla/FullPlaybackActivity.java @@ -97,11 +97,9 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On setContentView(layout); CoverView coverView = (CoverView)findViewById(R.id.cover_view); - - coverView.setCoverStyle(coverStyle); + coverView.setup(mLooper, this, coverStyle); coverView.setOnClickListener(this); coverView.setOnLongClickListener(this); - coverView.setupHandler(mLooper); mCoverView = coverView; mControlsTop = findViewById(R.id.controls_top); @@ -189,34 +187,52 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On } @Override - protected void setState(int state) + protected void setState(final int state) { - if ((mState & PlaybackService.FLAG_NO_MEDIA) == 0 && (state & PlaybackService.FLAG_NO_MEDIA) != 0) - mUiHandler.sendEmptyMessage(MSG_SHOW_NO_MEDIA); - else if ((mState & PlaybackService.FLAG_NO_MEDIA) != 0 && (state & PlaybackService.FLAG_NO_MEDIA) == 0) - mUiHandler.sendEmptyMessage(MSG_HIDE_MESSAGE_OVERLAY); + int toggled = mState ^ state; + + if ((toggled & PlaybackService.FLAG_NO_MEDIA) != 0) { + runOnUiThread(new Runnable() { + @Override + public void run() + { + if ((state & PlaybackService.FLAG_NO_MEDIA) != 0) { + showMessageOverlay(); + setNoMediaOverlayMessage(); + } else { + hideMessageOverlay(); + } + } + }); + } super.setState(state); } @Override - protected void onServiceReady() + protected void onSongChange(final Song song) { - super.onServiceReady(); - - Song song = ContextApplication.getService().getSong(0); - mUiHandler.sendMessage(mUiHandler.obtainMessage(MSG_UPDATE_SONG, song)); - } - - @Override - public void receive(Intent intent) - { - super.receive(intent); - - if (PlaybackService.EVENT_CHANGED.equals(intent.getAction())) { - Song song = intent.getParcelableExtra("song"); - mUiHandler.sendMessage(mUiHandler.obtainMessage(MSG_UPDATE_SONG, song)); + mDuration = song == null ? 0 : song.duration; + if (mTitle != null) { + runOnUiThread(new Runnable() { + @Override + public void run() + { + if (song == null) { + mTitle.setText(null); + mAlbum.setText(null); + mArtist.setText(null); + } else { + mTitle.setText(song.title); + mAlbum.setText(song.album); + mArtist.setText(song.artist); + } + } + }); } + mUiHandler.sendEmptyMessage(MSG_UPDATE_PROGRESS); + + super.onSongChange(song); } @Override @@ -245,18 +261,17 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On return false; } + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_RIGHT: + nextSong(); findViewById(R.id.next).requestFocus(); - mHandler.sendMessage(mHandler.obtainMessage(PlaybackActivity.MSG_SET_SONG, 1, 0)); - mCoverView.go(1); return true; case KeyEvent.KEYCODE_DPAD_LEFT: + previousSong(); findViewById(R.id.previous).requestFocus(); - mHandler.sendMessage(mHandler.obtainMessage(PlaybackActivity.MSG_SET_SONG, -1, 0)); - mCoverView.go(-1); return true; } @@ -349,7 +364,7 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On public boolean onLongClick(View view) { if (view.getId() == R.id.cover_view) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_TOGGLE_FLAG, PlaybackService.FLAG_PLAYING, 0)); + setState(ContextApplication.getService().toggleFlag(PlaybackService.FLAG_PLAYING)); return true; } @@ -361,18 +376,6 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On * called on the UI Handler. */ private static final int MSG_UPDATE_PROGRESS = 10; - /** - * Update the metadata for the current song. - */ - private static final int MSG_UPDATE_SONG = 11; - /** - * The the no media overlay message. This must be called on the UI Handler. - */ - private static final int MSG_SHOW_NO_MEDIA = 12; - /** - * Hide any overlay messages. This must be called on the UI Handler. - */ - private static final int MSG_HIDE_MESSAGE_OVERLAY = 13; /** * Save the hidden_controls preference to storage. */ @@ -392,33 +395,6 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On case MSG_UPDATE_PROGRESS: updateProgress(); break; - case MSG_UPDATE_SONG: { - Song song = (Song)message.obj; - if (song == null) { - if (mTitle != null) { - mTitle.setText(null); - mAlbum.setText(null); - mArtist.setText(null); - } - mDuration = 0; - } else { - if (mTitle != null) { - mTitle.setText(song.title); - mAlbum.setText(song.album); - mArtist.setText(song.artist); - } - mDuration = song.duration; - } - updateProgress(); - break; - } - case MSG_SHOW_NO_MEDIA: - showMessageOverlay(); - setNoMediaOverlayMessage(); - break; - case MSG_HIDE_MESSAGE_OVERLAY: - hideMessageOverlay(); - break; default: return super.handleMessage(message); } diff --git a/src/org/kreed/vanilla/MiniPlaybackActivity.java b/src/org/kreed/vanilla/MiniPlaybackActivity.java index 850db3b5..a06f41aa 100644 --- a/src/org/kreed/vanilla/MiniPlaybackActivity.java +++ b/src/org/kreed/vanilla/MiniPlaybackActivity.java @@ -38,11 +38,11 @@ public class MiniPlaybackActivity extends PlaybackActivity implements View.OnCli { super.onCreate(state); - requestWindowFeature(Window.FEATURE_NO_TITLE); + requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.mini_playback); mCoverView = (CoverView)findViewById(R.id.cover_view); - mCoverView.setupHandler(mLooper); + mCoverView.setup(mLooper, this, CoverBitmap.STYLE_OVERLAPPING_BOX); View openButton = findViewById(R.id.open_button); openButton.setOnClickListener(this); diff --git a/src/org/kreed/vanilla/OneCellWidget.java b/src/org/kreed/vanilla/OneCellWidget.java index b6be20ca..aa277d99 100644 --- a/src/org/kreed/vanilla/OneCellWidget.java +++ b/src/org/kreed/vanilla/OneCellWidget.java @@ -73,7 +73,11 @@ public class OneCellWidget extends AppWidgetProvider { String action = intent.getAction(); if (PlaybackService.EVENT_CHANGED.equals(action) || PlaybackService.EVENT_REPLACE_SONG.equals(action)) { Context context = ContextApplication.getContext(); - Song song = intent.getParcelableExtra("song"); + Song song; + if (intent.hasExtra("song")) + song = intent.getParcelableExtra("song"); + else + song = ContextApplication.getService().getSong(0); int state = intent.getIntExtra("state", -1); AppWidgetManager manager = AppWidgetManager.getInstance(context); diff --git a/src/org/kreed/vanilla/PlaybackActivity.java b/src/org/kreed/vanilla/PlaybackActivity.java index ccfb2bc2..b318087b 100644 --- a/src/org/kreed/vanilla/PlaybackActivity.java +++ b/src/org/kreed/vanilla/PlaybackActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Christopher Eby + * 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 @@ -29,12 +29,13 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.os.SystemClock; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; -public class PlaybackActivity extends Activity implements Handler.Callback, View.OnClickListener { +public class PlaybackActivity extends Activity implements Handler.Callback, View.OnClickListener, CoverView.Callback { Handler mHandler; Looper mLooper; @@ -42,6 +43,9 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View ControlButton mPlayPauseButton; int mState; + private long mLastStateEvent; + private long mLastSongEvent; + @Override public void onCreate(Bundle state) { @@ -130,21 +134,27 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View return handleKeyLongPress(keyCode); } + public void nextSong() + { + onSongChange(ContextApplication.getService().nextSong()); + } + + public void previousSong() + { + onSongChange(ContextApplication.getService().previousSong()); + } + public void onClick(View view) { switch (view.getId()) { case R.id.next: - mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_SONG, 1, 0)); - if (mCoverView != null) - mCoverView.go(1); + nextSong(); break; case R.id.play_pause: - mHandler.sendMessage(mHandler.obtainMessage(MSG_TOGGLE_FLAG, PlaybackService.FLAG_PLAYING, 0)); + setState(ContextApplication.getService().toggleFlag(PlaybackService.FLAG_PLAYING)); break; case R.id.previous: - mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_SONG, -1, 0)); - if (mCoverView != null) - mCoverView.go(-1); + previousSong(); break; } } @@ -157,13 +167,15 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View */ protected void setState(int state) { + mLastStateEvent = SystemClock.uptimeMillis(); + if (mState == state) return; - mState = state; + int toggled = mState ^ state; - if (mPlayPauseButton != null) { - final int res = (mState & PlaybackService.FLAG_PLAYING) == 0 ? R.drawable.play : R.drawable.pause; + if ((toggled & PlaybackService.FLAG_PLAYING) != 0 && mPlayPauseButton != null) { + final int res = (state & PlaybackService.FLAG_PLAYING) == 0 ? R.drawable.play : R.drawable.pause; runOnUiThread(new Runnable() { public void run() { @@ -171,6 +183,8 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View } }); } + + mState = state; } /** @@ -179,9 +193,25 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View */ protected void onServiceReady() { - if (mCoverView != null) - mCoverView.initialize(); - setState(ContextApplication.getService().getState()); + PlaybackService service = ContextApplication.getService(); + onSongChange(service.getSong(0)); + setState(service.getState()); + } + + /** + * Called when the current song changes. + */ + protected void onSongChange(final Song song) + { + mLastSongEvent = SystemClock.uptimeMillis(); + if (mCoverView != null) { + runOnUiThread(new Runnable() { + public void run() + { + mCoverView.setCurrentSong(song); + } + }); + } } /** @@ -193,12 +223,21 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View { String action = intent.getAction(); - if (PlaybackService.EVENT_INITIALIZED.equals(action)) - onServiceReady(); + if (PlaybackService.EVENT_CHANGED.equals(action)) { + long time = intent.getLongExtra("time", -1); + assert(time != -1); + + int state = intent.getIntExtra("state", -1); + if (state != -1 && time > mLastStateEvent) + setState(state); + + if (intent.hasExtra("song") && time > mLastSongEvent) { + Song song = intent.getParcelableExtra("song"); + onSongChange(song); + } + } if (mCoverView != null) mCoverView.receive(intent); - if (PlaybackService.EVENT_CHANGED.equals(action)) - setState(intent.getIntExtra("state", 0)); } /** @@ -245,10 +284,10 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View ContextApplication.quit(); return true; case MENU_SHUFFLE: - mHandler.sendMessage(mHandler.obtainMessage(MSG_TOGGLE_FLAG, PlaybackService.FLAG_SHUFFLE, 0)); + setState(ContextApplication.getService().toggleFlag(PlaybackService.FLAG_SHUFFLE)); return true; case MENU_REPEAT: - mHandler.sendMessage(mHandler.obtainMessage(MSG_TOGGLE_FLAG, PlaybackService.FLAG_REPEAT, 0)); + setState(ContextApplication.getService().toggleFlag(PlaybackService.FLAG_REPEAT)); return true; case MENU_PREFS: startActivity(new Intent(this, PreferencesActivity.class)); @@ -258,35 +297,9 @@ public class PlaybackActivity extends Activity implements Handler.Callback, View } } - /** - * Tell PlaybackService to toggle a state flag (passed as arg1) and display - * a message notifying that the flag was toggled. - */ - static final int MSG_TOGGLE_FLAG = 0; - /** - * Tell PlaybackService to change the current song. - * - * arg1 should be the delta, -1 or 1, indicating the previous or next song, - * respectively. - */ - static final int MSG_SET_SONG = 1; - - public boolean handleMessage(Message message) + @Override + public boolean handleMessage(Message msg) { - switch (message.what) { - case MSG_TOGGLE_FLAG: - ContextApplication.getService().toggleFlag(message.arg1); - break; - case MSG_SET_SONG: - if (message.arg1 == 1) - ContextApplication.getService().nextSong(); - else - ContextApplication.getService().previousSong(); - break; - default: - return false; - } - - return true; + return false; } } diff --git a/src/org/kreed/vanilla/PlaybackService.java b/src/org/kreed/vanilla/PlaybackService.java index a69d604a..911a41dd 100644 --- a/src/org/kreed/vanilla/PlaybackService.java +++ b/src/org/kreed/vanilla/PlaybackService.java @@ -137,7 +137,6 @@ public final class PlaybackService extends Service implements Handler.Callback, public static final String EVENT_REPLACE_SONG = "org.kreed.vanilla.event.REPLACE_SONG"; public static final String EVENT_CHANGED = "org.kreed.vanilla.event.CHANGED"; - public static final String EVENT_INITIALIZED = "org.kreed.vanilla.event.INITIALIZED"; public static final int FLAG_NO_MEDIA = 0x2; public static final int FLAG_PLAYING = 0x1; @@ -391,8 +390,6 @@ public final class PlaybackService extends Service implements Handler.Callback, private void initialize() { - ContextApplication.broadcast(new Intent(EVENT_INITIALIZED)); - mMediaPlayer = new MediaPlayer(); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setWakeMode(this, PowerManager.PARTIAL_WAKE_LOCK); @@ -433,10 +430,10 @@ public final class PlaybackService extends Service implements Handler.Callback, mHeadsetPause = settings.getBoolean("headset_pause", true); } else if ("remote_player".equals(key)) { // the preference is loaded in SongNotification class - updateNotification(getSong(0)); + updateNotification(); } else if ("notification_mode".equals(key)){ mNotificationMode = Integer.parseInt(settings.getString("notification_mode", "1")); - updateNotification(getSong(0)); + updateNotification(); } else if ("scrobble".equals(key)) { mScrobble = settings.getBoolean("scrobble", false); } else if ("volume".equals(key)) { @@ -474,14 +471,14 @@ public final class PlaybackService extends Service implements Handler.Callback, private void setFlag(int flag) { synchronized (mStateLock) { - updateState(mState | flag, false); + updateState(mState | flag); } } private void unsetFlag(int flag) { synchronized (mStateLock) { - updateState(mState & ~flag, false); + updateState(mState & ~flag); } } @@ -489,10 +486,9 @@ public final class PlaybackService extends Service implements Handler.Callback, * Modify the service state. * * @param state Union of PlaybackService.STATE_* flags - * @param forceBroadcast Broadcast state/song update even if the state has - * not changed. + * @return The new state */ - private void updateState(int state, boolean forceBroadcast) + private int updateState(int state) { state &= ALL_FLAGS; @@ -508,33 +504,44 @@ public final class PlaybackService extends Service implements Handler.Callback, } if (song == null && (state & FLAG_PLAYING) != 0) - return; + return state; if (song == null) state &= ~FLAG_REPEAT; int oldState = mState; mState = state; - if (state != oldState || forceBroadcast) { - Intent intent = new Intent(EVENT_CHANGED); - intent.putExtra("state", state); - intent.putExtra("song", song); - intent.putExtra("pos", mTimeline.getCurrentPosition()); - mHandler.sendMessage(mHandler.obtainMessage(BROADCAST, intent)); - - if (mScrobble) { - intent = new Intent("net.jjc1138.android.scrobbler.action.MUSIC_STATUS"); - intent.putExtra("playing", (state & FLAG_PLAYING) != 0); - if (song != null) - intent.putExtra("id", (int)song.id); - sendBroadcast(intent); - } - - updateNotification(song); + if (state != oldState) { + mHandler.sendMessage(mHandler.obtainMessage(PROCESS_STATE, oldState, state)); + mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_CHANGE, state, 0)); } + return state; + } + + private void processNewState(int oldState, int state) + { int toggled = oldState ^ state; + if ((toggled & FLAG_PLAYING) != 0) { + if ((state & FLAG_PLAYING) != 0) { + if (mMediaPlayerInitialized) { + synchronized (mMediaPlayer) { + mMediaPlayer.start(); + } + } + if (mNotificationMode != NEVER) + startForegroundCompat(NOTIFICATION_ID, mNotification); + } else { + if (mMediaPlayerInitialized) { + synchronized (mMediaPlayer) { + mMediaPlayer.pause(); + } + } + stopForegroundCompat(false); + } + } + if ((toggled & FLAG_SHUFFLE) != 0) { if ((state & FLAG_SHUFFLE) != 0) { mTimeline.setShuffle(true); @@ -554,30 +561,42 @@ public final class PlaybackService extends Service implements Handler.Callback, Toast.makeText(this, R.string.repeat_disabling, Toast.LENGTH_SHORT).show(); } } - - if ((toggled & FLAG_PLAYING) != 0) { - if ((state & FLAG_PLAYING) != 0) { - if (mNotificationMode != NEVER) - startForegroundCompat(NOTIFICATION_ID, mNotification); - if (mMediaPlayerInitialized) { - synchronized (mMediaPlayer) { - mMediaPlayer.start(); - } - } - } else { - stopForegroundCompat(false); - if (mMediaPlayerInitialized) { - synchronized (mMediaPlayer) { - mMediaPlayer.pause(); - } - } - } - } } - private void updateNotification(Song song) + private void broadcastChange(int state, Song song, long uptime) + { + updateNotification(); + + Intent intent = new Intent(EVENT_CHANGED); + if (state != -1) + intent.putExtra("state", state); + if (song != null) + intent.putExtra("song", song); + intent.putExtra("time", uptime); + ContextApplication.broadcast(intent); + + if (mScrobble) + scrobble(); + } + + private void scrobble() + { + assert(mScrobble == true); + + Song song = getSong(0); + int state = mState; + + Intent intent = new Intent("net.jjc1138.android.scrobbler.action.MUSIC_STATUS"); + intent.putExtra("playing", (state & FLAG_PLAYING) != 0); + if (song != null) + intent.putExtra("id", (int)song.id); + sendBroadcast(intent); + } + + private void updateNotification() { boolean shouldNotify = mNotificationMode == ALWAYS || mNotificationMode == WHEN_PLAYING && (mState & FLAG_PLAYING) != 0; + Song song = getSong(0); if (song != null && shouldNotify) { mNotification = new SongNotification(song, (mState & FLAG_PLAYING) != 0); mNotificationManager.notify(NOTIFICATION_ID, mNotification); @@ -590,38 +609,56 @@ public final class PlaybackService extends Service implements Handler.Callback, * Toggle a flag in the state on or off * * @param flag The flag to be toggled + * @return The new state */ - public void toggleFlag(int flag) + public int toggleFlag(int flag) { + int state; synchronized (mStateLock) { - updateState(mState ^ flag, false); + state = updateState(mState ^ flag); } userActionTriggered(); + return state; } /** * Move delta places away from the current song. + * + * @return The new current song */ - private void setCurrentSong(int delta) + private Song setCurrentSong(int delta) { if (mMediaPlayer == null) - return; + return null; synchronized (mMediaPlayer) { - mMediaPlayer.stop(); + if (mMediaPlayer.isPlaying()) + mMediaPlayer.stop(); } Song song = mTimeline.shiftCurrentSong(delta); if (song == null) { - if (Song.isSongAvailable()) - setCurrentSong(+1); // we only encountered a bad song; skip it - else + if (Song.isSongAvailable()) { + return setCurrentSong(+1); // we only encountered a bad song; skip it + } else { setFlag(FLAG_NO_MEDIA); // we don't have any songs : / - return; + return null; + } } else if ((mState & FLAG_NO_MEDIA) != 0) { unsetFlag(FLAG_NO_MEDIA); } + mHandler.removeMessages(PROCESS_SONG); + + mHandler.sendMessage(mHandler.obtainMessage(PROCESS_SONG, song)); + Message msg = mHandler.obtainMessage(BROADCAST_CHANGE, -1, 0); + msg.obj = song; + mHandler.sendMessage(msg); + return song; + } + + private void processSong(Song song) + { try { synchronized (mMediaPlayer) { mMediaPlayer.reset(); @@ -632,15 +669,18 @@ public final class PlaybackService extends Service implements Handler.Callback, } if ((mState & FLAG_PLAYING) != 0) mMediaPlayer.start(); - updateState(mState & ~FLAG_ERROR, true); + updateState(mState & ~FLAG_ERROR); } catch (IOException e) { // FLAG_PLAYING is set to force showing the Toast. updateState() // will unset it. - updateState(mState | FLAG_PLAYING | FLAG_ERROR, true); + updateState(mState | FLAG_PLAYING | FLAG_ERROR); Log.e("VanillaMusic", "IOException", e); } - mHandler.sendEmptyMessage(PROCESS_SONG); + getSong(+2); + mTimeline.purge(); + mHandler.removeMessages(SAVE_STATE); + mHandler.sendEmptyMessageDelayed(SAVE_STATE, 5000); } public void onCompletion(MediaPlayer player) @@ -787,8 +827,10 @@ public final class PlaybackService extends Service implements Handler.Callback, * @see ContextApplication#broadcast(Intent) */ private static final int BROADCAST = 9; + private static final int BROADCAST_CHANGE = 10; private static final int SAVE_STATE = 12; private static final int PROCESS_SONG = 13; + private static final int PROCESS_STATE = 14; public boolean handleMessage(Message message) { @@ -817,10 +859,7 @@ public final class PlaybackService extends Service implements Handler.Callback, mTimeline.saveState(this, 0); break; case PROCESS_SONG: - getSong(+2); - mTimeline.purge(); - mHandler.removeMessages(SAVE_STATE); - mHandler.sendEmptyMessageDelayed(SAVE_STATE, 5000); + processSong((Song)message.obj); break; case CREATE: initialize(); @@ -854,9 +893,15 @@ public final class PlaybackService extends Service implements Handler.Callback, } } break; + case PROCESS_STATE: + processNewState(message.arg1, message.arg2); + break; case BROADCAST: ContextApplication.broadcast((Intent)message.obj); break; + case BROADCAST_CHANGE: + broadcastChange(message.arg1, (Song)message.obj, message.getWhen()); + break; default: return false; } @@ -943,19 +988,21 @@ public final class PlaybackService extends Service implements Handler.Callback, /** * Move to the next song in the queue. */ - public void nextSong() + public Song nextSong() { - setCurrentSong(+1); + Song song = setCurrentSong(+1); userActionTriggered(); + return song; } /** * Move to the previous song in the queue. */ - public void previousSong() + public Song previousSong() { - setCurrentSong(-1); + Song song = setCurrentSong(-1); userActionTriggered(); + return song; } /** diff --git a/src/org/kreed/vanilla/SongSelector.java b/src/org/kreed/vanilla/SongSelector.java index 755e1909..befbc3cf 100644 --- a/src/org/kreed/vanilla/SongSelector.java +++ b/src/org/kreed/vanilla/SongSelector.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Christopher Eby + * 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 @@ -192,15 +192,6 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem out.putSerializable("limiter_genres", mGenreAdapter.getLimiter()); } - @Override - protected void onServiceReady() - { - super.onServiceReady(); - - if (mStatusText != null) - onSongChange(ContextApplication.getService().getSong(0)); - } - @Override public boolean onKeyUp(int keyCode, KeyEvent event) { @@ -677,12 +668,14 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem private static int mCoverSize = -1; - /** - * Call to update the status text for a newly-playing song. Should only be - * called after the controls have been initialized. - */ - private void onSongChange(final Song song) + @Override + protected void onSongChange(final Song song) { + super.onSongChange(song); + + if (mStatusText == null) + return; + Resources res = getResources(); CharSequence text; if (song == null) { @@ -709,13 +702,4 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem } }); } - - @Override - public void receive(Intent intent) - { - super.receive(intent); - - if (mControls != null && PlaybackService.EVENT_CHANGED.equals(intent.getAction())) - onSongChange((Song)intent.getParcelableExtra("song")); - } }