diff --git a/res/values/translatable.xml b/res/values/translatable.xml index 09485ce2..c310d5cb 100644 --- a/res/values/translatable.xml +++ b/res/values/translatable.xml @@ -194,6 +194,8 @@ THE SOFTWARE. Play/Pause Next song Previous song + Next album + Previous album Cycle repeat mode Cycle shuffle mode Enqueue current album diff --git a/res/values/untranslatable.xml b/res/values/untranslatable.xml index f3521b54..2d558eb2 100644 --- a/res/values/untranslatable.xml +++ b/res/values/untranslatable.xml @@ -55,6 +55,8 @@ THE SOFTWARE. PlayPause NextSong PreviousSong + NextAlbum + PreviousAlbum Repeat Shuffle EnqueueAlbum @@ -70,6 +72,8 @@ THE SOFTWARE. @string/play_pause @string/next_song @string/previous_song + @string/next_album + @string/previous_album @string/cycle_repeat_mode @string/cycle_shuffle_mode @string/enqueue_current_album diff --git a/src/org/kreed/vanilla/CoverView.java b/src/org/kreed/vanilla/CoverView.java index 25b19a7e..5fbb1dd9 100644 --- a/src/org/kreed/vanilla/CoverView.java +++ b/src/org/kreed/vanilla/CoverView.java @@ -65,13 +65,11 @@ public final class CoverView extends View implements Handler.Callback { */ public interface Callback { /** - * Called after the view has scrolled to the next (right) cover. + * Called after the view has scrolled to the next or previous cover. + * + * @param delta -1 for the previous cover, 1 for the next. */ - public void nextSong(); - /** - * Called after the view has scrolled to the previous (left) cover. - */ - public void previousSong(); + public void shiftCurrentSong(int delta); /** * Called when the user has swiped up on the view. */ @@ -422,6 +420,7 @@ public final class CoverView extends View implements Handler.Callback { */ private static final int MSG_SCROLL = 3; + @Override public boolean handleMessage(Message message) { switch (message.what) { @@ -446,12 +445,8 @@ public final class CoverView extends View implements Handler.Callback { invalidateCovers(); mUiHandler.sendEmptyMessage(MSG_SCROLL); } else if (mTentativeCover != -1) { - int delta = mTentativeCover - 1; + mCallback.shiftCurrentSong(mTentativeCover - 1); mTentativeCover = -1; - if (delta == 1) - mCallback.nextSong(); - else - mCallback.previousSong(); resetScroll(); } break; diff --git a/src/org/kreed/vanilla/FullPlaybackActivity.java b/src/org/kreed/vanilla/FullPlaybackActivity.java index 979d60ba..c69abc3e 100644 --- a/src/org/kreed/vanilla/FullPlaybackActivity.java +++ b/src/org/kreed/vanilla/FullPlaybackActivity.java @@ -314,11 +314,11 @@ public class FullPlaybackActivity extends PlaybackActivity { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_RIGHT: - nextSong(); + shiftCurrentSong(SongTimeline.SHIFT_NEXT_SONG); findViewById(R.id.next).requestFocus(); return true; case KeyEvent.KEYCODE_DPAD_LEFT: - previousSong(); + shiftCurrentSong(SongTimeline.SHIFT_PREVIOUS_SONG); findViewById(R.id.previous).requestFocus(); return true; } diff --git a/src/org/kreed/vanilla/PlaybackActivity.java b/src/org/kreed/vanilla/PlaybackActivity.java index b1a2b853..4d4c72ee 100644 --- a/src/org/kreed/vanilla/PlaybackActivity.java +++ b/src/org/kreed/vanilla/PlaybackActivity.java @@ -58,6 +58,8 @@ public class PlaybackActivity extends Activity PlayPause, NextSong, PreviousSong, + NextAlbum, + PreviousAlbum, Repeat, Shuffle, EnqueueAlbum, @@ -199,15 +201,9 @@ public class PlaybackActivity extends Activity } @Override - public void nextSong() + public void shiftCurrentSong(int delta) { - setSong(PlaybackService.get(this).nextSong()); - } - - @Override - public void previousSong() - { - setSong(PlaybackService.get(this).previousSong()); + setSong(PlaybackService.get(this).shiftCurrentSong(delta)); } public void playPause() @@ -224,13 +220,13 @@ public class PlaybackActivity extends Activity { switch (view.getId()) { case R.id.next: - nextSong(); + shiftCurrentSong(SongTimeline.SHIFT_NEXT_SONG); break; case R.id.play_pause: playPause(); break; case R.id.previous: - previousSong(); + shiftCurrentSong(SongTimeline.SHIFT_PREVIOUS_SONG); break; case R.id.end_action: cycleFinishAction(); @@ -427,10 +423,16 @@ public class PlaybackActivity extends Activity playPause(); break; case NextSong: - nextSong(); + shiftCurrentSong(SongTimeline.SHIFT_NEXT_SONG); break; case PreviousSong: - previousSong(); + shiftCurrentSong(SongTimeline.SHIFT_PREVIOUS_SONG); + break; + case NextAlbum: + shiftCurrentSong(SongTimeline.SHIFT_NEXT_ALBUM); + break; + case PreviousAlbum: + shiftCurrentSong(SongTimeline.SHIFT_PREVIOUS_ALBUM); break; case Repeat: cycleFinishAction(); diff --git a/src/org/kreed/vanilla/PlaybackService.java b/src/org/kreed/vanilla/PlaybackService.java index 98818c39..5b167e35 100644 --- a/src/org/kreed/vanilla/PlaybackService.java +++ b/src/org/kreed/vanilla/PlaybackService.java @@ -748,8 +748,11 @@ public final class PlaybackService extends Service } /** - * Move delta places away from the current song. + * Move to the next or previous song or album in the timeline. * + * @param delta One of SongTimeline.SHIFT_*. 0 can also be passed to + * initialize the current song with media player, notification, + * broadcasts, etc. * @return The new current song */ private Song setCurrentSong(int delta) @@ -760,7 +763,11 @@ public final class PlaybackService extends Service if (mMediaPlayer.isPlaying()) mMediaPlayer.stop(); - Song song = mTimeline.shiftCurrentSong(delta); + Song song; + if (delta == 0) + song = mTimeline.getSong(0); + else + song = mTimeline.shiftCurrentSong(delta); mCurrentSong = song; if (song == null || song.id == -1 || song.path == null) { if (MediaUtils.isSongAvailable(getContentResolver())) { @@ -1115,21 +1122,14 @@ public final class PlaybackService extends Service } /** - * Move to the next song in the queue. + * Move to next or previous song or album in the queue. + * + * @param delta One of SongTimeline.SHIFT_*. + * @return The new current song. */ - public Song nextSong() + public Song shiftCurrentSong(int delta) { - Song song = setCurrentSong(+1); - userActionTriggered(); - return song; - } - - /** - * Move to the previous song in the queue. - */ - public Song previousSong() - { - Song song = setCurrentSong(-1); + Song song = setCurrentSong(delta); userActionTriggered(); return song; } diff --git a/src/org/kreed/vanilla/SongTimeline.java b/src/org/kreed/vanilla/SongTimeline.java index 6dc38f0b..11cc5f21 100644 --- a/src/org/kreed/vanilla/SongTimeline.java +++ b/src/org/kreed/vanilla/SongTimeline.java @@ -176,6 +176,30 @@ public final class SongTimeline { public static final int[] SHUFFLE_ICONS = { R.drawable.shuffle_inactive, R.drawable.shuffle_active, R.drawable.shuffle_album_active }; + /** + * Move current position to the previous album. + * + * @see SongTimeline#shiftCurrentSong(int) + */ + public static final int SHIFT_PREVIOUS_ALBUM = -2; + /** + * Move current position to the previous song. + * + * @see SongTimeline#shiftCurrentSong(int) + */ + public static final int SHIFT_PREVIOUS_SONG = -1; + /** + * Move current position to the next song. + * + * @see SongTimeline#shiftCurrentSong(int) + */ + public static final int SHIFT_NEXT_SONG = 1; + /** + * Move current position to the next album. + * + * @see SongTimeline#shiftCurrentSong(int) + */ + public static final int SHIFT_NEXT_ALBUM = 2; private final Context mContext; /** @@ -473,11 +497,12 @@ public final class SongTimeline { } else if (pos > size) { return null; } else if (pos == size) { - switch (mFinishAction) { - case FINISH_STOP: - case FINISH_REPEAT: - case FINISH_REPEAT_CURRENT: - case FINISH_STOP_CURRENT: + if (mFinishAction == FINISH_RANDOM) { + song = MediaUtils.randomSong(mContext.getContentResolver()); + if (song == null) + return null; + timeline.add(song); + } else { if (size == 0) // empty queue return null; @@ -485,15 +510,6 @@ public final class SongTimeline { song = shuffleAll(); else song = timeline.get(0); - break; - case FINISH_RANDOM: - song = MediaUtils.randomSong(mContext.getContentResolver()); - if (song == null) - return null; - timeline.add(song); - break; - default: - throw new IllegalStateException("Invalid finish action: " + mFinishAction); } } else { song = timeline.get(pos); @@ -508,40 +524,58 @@ public final class SongTimeline { } /** - * Shift the current song by delta places. + * Internal implementation for shiftCurrentSong. Does all the work except + * broadcasting the timeline change: updates mCurrentPos and handles + * shuffling, repeating, and random mode. * - * @param delta The delta. Must be -1, 0, 1. + * @param delta -1 to move to the previous song or 1 for the next. + */ + private void shiftCurrentSongInternal(int delta) + { + int pos = mCurrentPos + delta; + + if (mFinishAction != FINISH_RANDOM && pos == mSongs.size()) { + if (mShuffleMode != SHUFFLE_NONE && !mSongs.isEmpty()) { + if (mShuffledSongs == null) + shuffleAll(); + mSongs = mShuffledSongs; + } + + pos = 0; + } else if (pos < 0) { + if (mFinishAction == FINISH_RANDOM) + pos = 0; + else + pos = Math.max(0, mSongs.size() - 1); + } + + mCurrentPos = pos; + mShuffledSongs = null; + } + + /** + * Move to the next or previous song or album. + * + * @param delta One of SongTimeline.SHIFT_*. * @return The Song at the new position */ public Song shiftCurrentSong(int delta) { - Assert.assertTrue(delta >= -1 && delta <= 1); - synchronized (this) { - int pos = mCurrentPos + delta; - - if (mFinishAction != FINISH_RANDOM && pos == mSongs.size()) { - if (mShuffleMode != SHUFFLE_NONE && !mSongs.isEmpty()) { - if (mShuffledSongs == null) - shuffleAll(); - mSongs = mShuffledSongs; - } - - pos = 0; - } else if (pos < 0) { - if (mFinishAction == FINISH_RANDOM) - pos = 0; - else - pos = Math.max(0, mSongs.size() - 1); + if (delta == SHIFT_PREVIOUS_SONG || delta == SHIFT_NEXT_SONG) { + shiftCurrentSongInternal(delta); + } else { + Song song = getSong(0); + long currentAlbum = song.albumId; + long currentSong = song.id; + delta = delta > 0 ? 1 : -1; + do { + shiftCurrentSongInternal(delta); + song = getSong(0); + } while (currentAlbum == song.albumId && currentSong != song.id); } - - mCurrentPos = pos; - mShuffledSongs = null; } - - if (delta != 0) - changed(); - + changed(); return getSong(0); }