diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackActivity.java b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackActivity.java index c0d96f5b4..5ec7bd3a8 100644 --- a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackActivity.java +++ b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackActivity.java @@ -384,18 +384,19 @@ public void onTimelineChanged() static final int MENU_SONG_FAVORITE = 12; static final int MENU_SHOW_QUEUE = 13; static final int MENU_HIDE_QUEUE = 14; - static final int MENU_DELETE = 15; - static final int MENU_EMPTY_QUEUE = 16; - static final int MENU_ADD_TO_PLAYLIST = 17; - static final int MENU_SHARE = 18; - static final int MENU_GO_HOME = 19; - static final int MENU_PLUGINS = 20; // used in FullPlaybackActivity - static final int MENU_MORE = 21; // toplevel menu, has no own action - static final int MENU_MORE_ALBUM = 22; - static final int MENU_MORE_ARTIST = 23; - static final int MENU_MORE_GENRE = 24; - static final int MENU_MORE_FOLDER = 25; - static final int MENU_JUMP_TO_TIME = 26; + static final int MENU_PREV_QUEUE = 15; + static final int MENU_DELETE = 16; + static final int MENU_EMPTY_QUEUE = 17; + static final int MENU_ADD_TO_PLAYLIST = 18; + static final int MENU_SHARE = 19; + static final int MENU_GO_HOME = 20; + static final int MENU_PLUGINS = 21; // used in FullPlaybackActivity + static final int MENU_MORE = 22; // toplevel menu, has no own action + static final int MENU_MORE_ALBUM = 23; + static final int MENU_MORE_ARTIST = 24; + static final int MENU_MORE_GENRE = 25; + static final int MENU_MORE_FOLDER = 26; + static final int MENU_JUMP_TO_TIME = 27; @Override public boolean onCreateOptionsMenu(Menu menu) @@ -411,6 +412,9 @@ public boolean onOptionsItemSelected(MenuItem item) case MENU_PREFS: startActivity(new Intent(this, PreferencesActivity.class)); break; + case MENU_PREV_QUEUE: + PlaybackService.get(this).previousQueue(); + break; case MENU_CLEAR_QUEUE: PlaybackService.get(this).clearQueue(); break; diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java index 92095fe28..629f3bce0 100644 --- a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java +++ b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java @@ -1919,6 +1919,16 @@ public void enqueueFromSong(Song song, int type) addSongs(query); } + /** + * Jumps to a previously-created queue + * @author Markil 3 + * @since 1.0.86 + */ + public void previousQueue() + { + this.mTimeline.revertQueue(); + } + /** * Clear the song queue. */ diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/SlidingPlaybackActivity.java b/app/src/main/java/ch/blinkenlights/android/vanilla/SlidingPlaybackActivity.java index 395e94698..e37dd7664 100644 --- a/app/src/main/java/ch/blinkenlights/android/vanilla/SlidingPlaybackActivity.java +++ b/app/src/main/java/ch/blinkenlights/android/vanilla/SlidingPlaybackActivity.java @@ -125,6 +125,7 @@ public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, MENU_SHOW_QUEUE, 20, R.string.show_queue); menu.add(0, MENU_HIDE_QUEUE, 20, R.string.hide_queue); + menu.add(0, MENU_PREV_QUEUE, 20, R.string.prev_queue); menu.add(0, MENU_CLEAR_QUEUE, 20, R.string.dequeue_rest); menu.add(0, MENU_EMPTY_QUEUE, 20, R.string.empty_the_queue); menu.add(0, MENU_SAVE_QUEUE, 20, R.string.save_as_playlist); @@ -316,7 +317,7 @@ public void onSlideExpansionChanged(int expansion) { if (mMenu == null) return; // not initialized yet - final int[] slide_visible = {MENU_CLEAR_QUEUE, MENU_EMPTY_QUEUE, MENU_SAVE_QUEUE}; + final int[] slide_visible = {MENU_PREV_QUEUE, MENU_CLEAR_QUEUE, MENU_EMPTY_QUEUE, MENU_SAVE_QUEUE}; final int[] slide_hidden = {MENU_SORT, MENU_DELETE, MENU_ENQUEUE, MENU_MORE, MENU_ADD_TO_PLAYLIST, MENU_SHARE}; diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/SongTimeline.java b/app/src/main/java/ch/blinkenlights/android/vanilla/SongTimeline.java index 12f35052f..a3a4db90d 100644 --- a/app/src/main/java/ch/blinkenlights/android/vanilla/SongTimeline.java +++ b/app/src/main/java/ch/blinkenlights/android/vanilla/SongTimeline.java @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -220,6 +221,12 @@ public final class SongTimeline { * should be unique, even if it refers to the same media. */ private ArrayList mSongs = new ArrayList(12); + /** + * Contains a history of previous queues. + * + * @author Markil 3 + */ + private LinkedList> songHistory = new LinkedList<>(); /** * The position of the current song (i.e. the playing song). */ @@ -725,6 +732,64 @@ else if (delta == SHIFT_PREVIOUS_SONG || delta == SHIFT_NEXT_SONG) { return getSong(0); } + /** + * Jumps to a previously-created queue + * @author Markil 3 + * @since 1.0.86 + */ + public void revertQueue() + { + ArrayList timeline = mSongs; + synchronized (this) + { + if (this.songHistory.size() > 0) + { + /* + * Saves the queue for later + */ + if (timeline.size() > 0) + { + this.songHistory.addFirst(new ArrayList<>(timeline)); + } + timeline.clear(); + timeline.addAll(this.songHistory.removeLast()); + mCurrentPos = 0; + int start = + mCurrentPos + 1; // Position where our modification started + Song jumpSong = + null; // Jump to this song if `data' requested it + + /* Check if addAtPos is out-of-bounds OR if + * the request does not want to work at the current + * playlist position anyway + */ + if (start > timeline.size()) + { + start = timeline.size(); + } + + if (mShuffleMode != SHUFFLE_NONE) + MediaUtils.shuffle(timeline.subList(start, start), + mShuffleMode == SHUFFLE_ALBUMS); + + if (jumpSong != null) + { + int jumpPos = timeline.lastIndexOf(jumpSong); + if (jumpPos > start) + { + // Get the sublist twice to avoid a ConcurrentModificationException. + timeline.addAll(timeline.subList(start, jumpPos)); + timeline.subList(start, jumpPos).clear(); + } + } + + broadcastChangedSongs(); + } + } + + changed(); + } + /** * Run the given query and add the results to the song timeline. * @@ -736,6 +801,8 @@ else if (delta == SHIFT_PREVIOUS_SONG || delta == SHIFT_NEXT_SONG) { */ public int addSongs(Context context, QueryTask query) { + final int MAX_QUEUE_HISTORY = 1; + Cursor cursor = query.runQuery(context); if (cursor == null) { return 0; @@ -793,6 +860,14 @@ public int addSongs(Context context, QueryTask query) case MODE_PLAY: case MODE_PLAY_POS_FIRST: case MODE_PLAY_ID_FIRST: + /* + * Saves the queue for later + */ + this.songHistory.add(new ArrayList<>(timeline)); + if (this.songHistory.size() > MAX_QUEUE_HISTORY) + { + this.songHistory.removeFirst(); + } timeline.clear(); mCurrentPos = 0; break; diff --git a/app/src/main/res/values/translatable.xml b/app/src/main/res/values/translatable.xml index 9e6be0ec8..bb34e5224 100644 --- a/app/src/main/res/values/translatable.xml +++ b/app/src/main/res/values/translatable.xml @@ -322,6 +322,7 @@ THE SOFTWARE. Empty queue Show queue Hide queue + Play previous queue Dequeue rest Toggle controls Seek 10 seconds backward