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