diff --git a/app/src/main/java/com/dkanada/gramophone/fragments/player/card/CardPlayerPlaybackControlsFragment.java b/app/src/main/java/com/dkanada/gramophone/fragments/player/card/CardPlayerPlaybackControlsFragment.java index 43982422..fb865b6d 100644 --- a/app/src/main/java/com/dkanada/gramophone/fragments/player/card/CardPlayerPlaybackControlsFragment.java +++ b/app/src/main/java/com/dkanada/gramophone/fragments/player/card/CardPlayerPlaybackControlsFragment.java @@ -13,13 +13,13 @@ import android.widget.SeekBar; import androidx.annotation.NonNull; import com.dkanada.gramophone.util.ThemeUtil; +import com.dkanada.gramophone.service.QueueManager; import com.dkanada.gramophone.R; import com.dkanada.gramophone.helper.MusicPlayerRemote; import com.dkanada.gramophone.databinding.FragmentCardPlayerPlaybackControlsBinding; import com.dkanada.gramophone.helper.MusicProgressViewUpdateHelper; import com.dkanada.gramophone.helper.PlayPauseButtonOnClickHandler; import com.dkanada.gramophone.interfaces.base.SimpleOnSeekbarChangeListener; -import com.dkanada.gramophone.service.MusicService; import com.dkanada.gramophone.fragments.AbsMusicServiceFragment; import com.dkanada.gramophone.util.MusicUtil; import com.dkanada.gramophone.views.PlayPauseDrawable; @@ -165,10 +165,10 @@ public class CardPlayerPlaybackControlsFragment extends AbsMusicServiceFragment private void updateShuffleState() { switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: + case QueueManager.SHUFFLE_MODE_SHUFFLE: binding.playerShuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; - case MusicService.SHUFFLE_MODE_NONE: + case QueueManager.SHUFFLE_MODE_NONE: default: binding.playerShuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; @@ -181,15 +181,15 @@ public class CardPlayerPlaybackControlsFragment extends AbsMusicServiceFragment private void updateRepeatState() { switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: + case QueueManager.REPEAT_MODE_NONE: binding.playerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); binding.playerRepeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; - case MusicService.REPEAT_MODE_ALL: + case QueueManager.REPEAT_MODE_ALL: binding.playerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); binding.playerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; - case MusicService.REPEAT_MODE_THIS: + case QueueManager.REPEAT_MODE_THIS: binding.playerRepeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); binding.playerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; diff --git a/app/src/main/java/com/dkanada/gramophone/fragments/player/flat/FlatPlayerPlaybackControlsFragment.java b/app/src/main/java/com/dkanada/gramophone/fragments/player/flat/FlatPlayerPlaybackControlsFragment.java index 21c9f4e6..206026f1 100644 --- a/app/src/main/java/com/dkanada/gramophone/fragments/player/flat/FlatPlayerPlaybackControlsFragment.java +++ b/app/src/main/java/com/dkanada/gramophone/fragments/player/flat/FlatPlayerPlaybackControlsFragment.java @@ -18,12 +18,12 @@ import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import com.dkanada.gramophone.databinding.FragmentFlatPlayerPlaybackControlsBinding; import com.dkanada.gramophone.util.ThemeUtil; +import com.dkanada.gramophone.service.QueueManager; import com.dkanada.gramophone.R; import com.dkanada.gramophone.helper.MusicPlayerRemote; import com.dkanada.gramophone.helper.MusicProgressViewUpdateHelper; import com.dkanada.gramophone.helper.PlayPauseButtonOnClickHandler; import com.dkanada.gramophone.interfaces.base.SimpleOnSeekbarChangeListener; -import com.dkanada.gramophone.service.MusicService; import com.dkanada.gramophone.fragments.AbsMusicServiceFragment; import com.dkanada.gramophone.util.MusicUtil; import com.dkanada.gramophone.views.PlayPauseDrawable; @@ -179,10 +179,10 @@ public class FlatPlayerPlaybackControlsFragment extends AbsMusicServiceFragment private void updateShuffleState() { switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: + case QueueManager.SHUFFLE_MODE_SHUFFLE: binding.playerShuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; - case MusicService.SHUFFLE_MODE_NONE: + case QueueManager.SHUFFLE_MODE_NONE: default: binding.playerShuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; @@ -195,15 +195,15 @@ public class FlatPlayerPlaybackControlsFragment extends AbsMusicServiceFragment private void updateRepeatState() { switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: + case QueueManager.REPEAT_MODE_NONE: binding.playerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); binding.playerRepeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; - case MusicService.REPEAT_MODE_ALL: + case QueueManager.REPEAT_MODE_ALL: binding.playerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); binding.playerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; - case MusicService.REPEAT_MODE_THIS: + case QueueManager.REPEAT_MODE_THIS: binding.playerRepeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); binding.playerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); break; diff --git a/app/src/main/java/com/dkanada/gramophone/helper/MusicPlayerRemote.java b/app/src/main/java/com/dkanada/gramophone/helper/MusicPlayerRemote.java index 1c4a010d..f6e19356 100644 --- a/app/src/main/java/com/dkanada/gramophone/helper/MusicPlayerRemote.java +++ b/app/src/main/java/com/dkanada/gramophone/helper/MusicPlayerRemote.java @@ -14,6 +14,7 @@ import android.widget.Toast; import com.dkanada.gramophone.R; import com.dkanada.gramophone.model.Song; import com.dkanada.gramophone.service.MusicService; +import com.dkanada.gramophone.service.QueueManager; import com.dkanada.gramophone.util.PreferenceUtil; import java.util.ArrayList; @@ -150,7 +151,7 @@ public class MusicPlayerRemote { if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) { musicService.openQueue(queue, startPosition, startPlaying); if (!PreferenceUtil.getInstance(musicService).getRememberShuffle()){ - setShuffleMode(MusicService.SHUFFLE_MODE_NONE); + setShuffleMode(QueueManager.SHUFFLE_MODE_NONE); } } } @@ -163,7 +164,7 @@ public class MusicPlayerRemote { if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) { openQueue(queue, startPosition, startPlaying); - setShuffleMode(MusicService.SHUFFLE_MODE_SHUFFLE); + setShuffleMode(QueueManager.SHUFFLE_MODE_SHUFFLE); } } @@ -182,24 +183,24 @@ public class MusicPlayerRemote { } public static Song getCurrentSong() { - if (musicService != null) { - return musicService.getCurrentSong(); + if (musicService != null && musicService.queueManager != null) { + return musicService.queueManager.getCurrentSong(); } return null; } public static int getPosition() { - if (musicService != null) { - return musicService.getPosition(); + if (musicService != null && musicService.queueManager != null) { + return musicService.queueManager.getPosition(); } return -1; } public static List getPlayingQueue() { - if (musicService != null) { - return musicService.getPlayingQueue(); + if (musicService != null && musicService.queueManager != null) { + return musicService.queueManager.getPlayingQueue(); } return new ArrayList<>(); @@ -222,8 +223,8 @@ public class MusicPlayerRemote { } public static long getQueueDurationMillis(int position) { - if (musicService != null) { - return musicService.getQueueDurationMillis(position); + if (musicService != null && musicService.queueManager != null) { + return musicService.queueManager.getQueueDurationMillis(position); } return -1; @@ -238,24 +239,24 @@ public class MusicPlayerRemote { } public static int getRepeatMode() { - if (musicService != null) { - return musicService.getRepeatMode(); + if (musicService != null && musicService.queueManager != null) { + return musicService.queueManager.getRepeatMode(); } - return MusicService.REPEAT_MODE_NONE; + return QueueManager.REPEAT_MODE_NONE; } public static int getShuffleMode() { - if (musicService != null) { - return musicService.getShuffleMode(); + if (musicService != null && musicService.queueManager != null) { + return musicService.queueManager.getShuffleMode(); } - return MusicService.SHUFFLE_MODE_NONE; + return QueueManager.SHUFFLE_MODE_NONE; } public static boolean cycleRepeatMode() { - if (musicService != null) { - musicService.cycleRepeatMode(); + if (musicService != null && musicService.queueManager != null) { + musicService.queueManager.cycleRepeatMode(); return true; } @@ -263,8 +264,8 @@ public class MusicPlayerRemote { } public static boolean toggleShuffleMode() { - if (musicService != null) { - musicService.toggleShuffle(); + if (musicService != null && musicService.queueManager != null) { + musicService.queueManager.toggleShuffle(); return true; } @@ -272,8 +273,8 @@ public class MusicPlayerRemote { } public static boolean setShuffleMode(final int shuffleMode) { - if (musicService != null) { - musicService.setShuffleMode(shuffleMode); + if (musicService != null && musicService.queueManager != null) { + musicService.queueManager.setShuffleMode(shuffleMode); return true; } @@ -281,9 +282,9 @@ public class MusicPlayerRemote { } public static boolean playNext(Song song) { - if (musicService != null) { + if (musicService != null && musicService.queueManager != null) { if (getPlayingQueue().size() > 0) { - musicService.addSong(getPosition() + 1, song); + musicService.queueManager.addSong(getPosition() + 1, song); } else { List queue = new ArrayList<>(); queue.add(song); @@ -298,9 +299,9 @@ public class MusicPlayerRemote { } public static boolean playNext(@NonNull List songs) { - if (musicService != null) { + if (musicService != null && musicService.queueManager != null) { if (getPlayingQueue().size() > 0) { - musicService.addSongs(getPosition() + 1, songs); + musicService.queueManager.addSongs(getPosition() + 1, songs); } else { openQueue(songs, 0, false); } @@ -314,9 +315,9 @@ public class MusicPlayerRemote { } public static boolean enqueue(Song song) { - if (musicService != null) { + if (musicService != null && musicService.queueManager != null) { if (getPlayingQueue().size() > 0) { - musicService.addSong(song); + musicService.queueManager.addSong(song); } else { List queue = new ArrayList<>(); queue.add(song); @@ -331,9 +332,9 @@ public class MusicPlayerRemote { } public static boolean enqueue(@NonNull List songs) { - if (musicService != null) { + if (musicService != null && musicService.queueManager != null) { if (getPlayingQueue().size() > 0) { - musicService.addSongs(songs); + musicService.queueManager.addSongs(songs); } else { openQueue(songs, 0, false); } @@ -347,8 +348,8 @@ public class MusicPlayerRemote { } public static boolean removeFromQueue(int position) { - if (musicService != null && position >= 0 && position < getPlayingQueue().size()) { - musicService.removeSong(position); + if (musicService != null && musicService.queueManager != null && position >= 0 && position < getPlayingQueue().size()) { + musicService.queueManager.removeSong(position); return true; } @@ -356,8 +357,8 @@ public class MusicPlayerRemote { } public static boolean moveSong(int from, int to) { - if (musicService != null && from >= 0 && to >= 0 && from < getPlayingQueue().size() && to < getPlayingQueue().size()) { - musicService.moveSong(from, to); + if (musicService != null && musicService.queueManager != null && from >= 0 && to >= 0 && from < getPlayingQueue().size() && to < getPlayingQueue().size()) { + musicService.queueManager.moveSong(from, to); return true; } @@ -365,8 +366,8 @@ public class MusicPlayerRemote { } public static boolean clearQueue() { - if (musicService != null) { - musicService.clearQueue(); + if (musicService != null && musicService.queueManager != null) { + musicService.queueManager.clearQueue(); return true; } diff --git a/app/src/main/java/com/dkanada/gramophone/service/MusicService.java b/app/src/main/java/com/dkanada/gramophone/service/MusicService.java index f09f2a8f..e86e6c7c 100644 --- a/app/src/main/java/com/dkanada/gramophone/service/MusicService.java +++ b/app/src/main/java/com/dkanada/gramophone/service/MusicService.java @@ -108,13 +108,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP public static final int PREPARE_NEXT = 4; public static final int SET_POSITION = 5; - public static final int SHUFFLE_MODE_NONE = 0; - public static final int SHUFFLE_MODE_SHUFFLE = 1; - - public static final int REPEAT_MODE_NONE = 0; - public static final int REPEAT_MODE_ALL = 1; - public static final int REPEAT_MODE_THIS = 2; - public static final int SAVE_QUEUE = 0; public static final int LOAD_QUEUE = 9; @@ -128,14 +121,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private Playback playback; - private List playingQueue = new ArrayList<>(); - private List originalPlayingQueue = new ArrayList<>(); - - private int position = -1; - private int nextPosition = -1; - - private int shuffleMode; - private int repeatMode; + public QueueManager queueManager; private boolean notHandledMetaChangedForCurrentTrack; private boolean queuesRestored; @@ -154,6 +140,25 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private HandlerThread progressHandlerThread; private HandlerThread queueHandlerThread; + public final QueueManager.QueueCallbacks queueCallbacks = new QueueManager.QueueCallbacks() { + @Override + public void onQueueChanged() { + notifyChange(QUEUE_CHANGED); + } + + @Override + public void onRepeatModeChanged() { + notifyChange(REPEAT_MODE_CHANGED); + // FixMe: Not sure about this: + prepareNext(); + } + + @Override + public void onShuffleModeChanged() { + notifyChange(SHUFFLE_MODE_CHANGED); + } + }; + private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, @NonNull Intent intent) { @@ -202,6 +207,8 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP playback = new LocalPlayer(this); playback.setCallbacks(this); + queueManager = new QueueManager(this, queueCallbacks); + playerHandlerThread = new HandlerThread(PlaybackHandler.class.getName()); playerHandlerThread.start(); playerHandler = new PlaybackHandler(this, playerHandlerThread.getLooper()); @@ -299,14 +306,14 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP break; case ACTION_PLAY_PLAYLIST: Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST); - int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE, getShuffleMode()); + int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE, queueManager.getShuffleMode()); if (playlist != null) { List playlistSongs = new ArrayList<>(); if (!playlistSongs.isEmpty()) { - if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { + if (shuffleMode == QueueManager.SHUFFLE_MODE_SHUFFLE) { int startPosition = new Random().nextInt(playlistSongs.size()); openQueue(playlistSongs, startPosition, true); - setShuffleMode(shuffleMode); + queueManager.setShuffleMode(shuffleMode); } else { openQueue(playlistSongs, 0, true); } @@ -373,33 +380,23 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP service.restoreQueuesAndPositionIfNecessary(); break; case SAVE_QUEUE: - service.saveQueue(); + service.queueManager.saveQueue(); break; } } } - private void saveQueue() { - // copy queues by value to avoid concurrent modification exceptions from database - App.getDatabase().songDao().deleteSongs(); - App.getDatabase().songDao().insertSongs(new ArrayList<>(playingQueue)); - - App.getDatabase().queueSongDao().deleteQueueSongs(); - App.getDatabase().queueSongDao().setQueue(new ArrayList<>(playingQueue), 0); - App.getDatabase().queueSongDao().setQueue(new ArrayList<>(originalPlayingQueue), 1); - } - public void saveState() { queueHandler.removeMessages(SAVE_QUEUE); queueHandler.sendEmptyMessage(SAVE_QUEUE); - PreferenceUtil.getInstance(this).setPosition(getPosition()); + PreferenceUtil.getInstance(this).setPosition(queueManager.position); PreferenceUtil.getInstance(this).setProgress(getSongProgressMillis()); } private void restoreState() { - shuffleMode = PreferenceUtil.getInstance(this).getShuffle(); - repeatMode = PreferenceUtil.getInstance(this).getRepeat(); + queueManager.shuffleMode = PreferenceUtil.getInstance(this).getShuffle(); + queueManager.repeatMode = PreferenceUtil.getInstance(this).getRepeat(); notifyChange(SHUFFLE_MODE_CHANGED); notifyChange(REPEAT_MODE_CHANGED); @@ -408,8 +405,9 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP queueHandler.sendEmptyMessage(LOAD_QUEUE); } + // FixMe: Move to QueueManager or not? private synchronized void restoreQueuesAndPositionIfNecessary() { - if (!queuesRestored && playingQueue.isEmpty()) { + if (!queuesRestored && queueManager.getPlayingQueue().isEmpty()) { List restoredQueue = App.getDatabase().queueSongDao().getQueue(0); List restoredOriginalQueue = App.getDatabase().queueSongDao().getQueue(1); @@ -417,10 +415,10 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP int restoredProgress = PreferenceUtil.getInstance(this).getProgress(); if (restoredQueue.size() > 0 && restoredQueue.size() == restoredOriginalQueue.size() && restoredPosition != -1) { - this.originalPlayingQueue = restoredOriginalQueue; - this.playingQueue = restoredQueue; + queueManager.originalPlayingQueue = restoredOriginalQueue; + queueManager.playingQueue = restoredQueue; - position = restoredPosition; + queueManager.position = restoredPosition; openCurrent(); if (restoredProgress > 0) seek(restoredProgress); @@ -463,17 +461,14 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP return playback != null && playback.isLoading(); } - public int getPosition() { - return position; - } public void playNextSong(boolean force) { - playSongAt(getNextPosition(force)); + playSongAt(queueManager.getNextPosition(force)); } private void openTrackAndPrepareNextAt(int position) { synchronized (this) { - this.position = position; + queueManager.position = position; openCurrent(); playback.start(); @@ -485,9 +480,9 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private void openCurrent() { synchronized (this) { - if (getCurrentSong() == null) return; + if (queueManager.getCurrentSong() == null) return; - playback.setDataSource(getCurrentSong()); + playback.setDataSource(queueManager.getCurrentSong()); } } @@ -498,10 +493,10 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private void prepareNextImpl() { synchronized (this) { - if (getCurrentSong() == null) return; + if (queueManager.getCurrentSong() == null) return; - nextPosition = getNextPosition(false); - playback.queueDataSource(getSongAt(nextPosition)); + queueManager.nextPosition = queueManager.getNextPosition(false); + playback.queueDataSource(queueManager.getSongAt(queueManager.nextPosition)); } } @@ -516,7 +511,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP } public void updateNotification() { - if (playingNotification != null && getCurrentSong() != null) { + if (playingNotification != null && queueManager.getCurrentSong() != null) { playingNotification.update(); } } @@ -531,7 +526,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP @SuppressLint("CheckResult") private void updateMediaSessionMetadata() { - final Song song = getCurrentSong(); + final Song song = queueManager.getCurrentSong(); if (song == null) { mediaSession.setMetadata(null); @@ -544,12 +539,12 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.albumName) .putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title) .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration) - .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, getPosition() + 1) + .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, queueManager.position + 1) .putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.year) .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getPlayingQueue().size()); + metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, queueManager.getPlayingQueue().size()); } if (PreferenceUtil.getInstance(this).getShowAlbumCover()) { @@ -598,80 +593,16 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP uiThreadHandler.post(runnable); } - public Song getCurrentSong() { - return getSongAt(getPosition()); - } - - public Song getSongAt(int position) { - if (position >= 0 && position < getPlayingQueue().size()) { - return getPlayingQueue().get(position); - } - - return null; - } - - public int getNextPosition(boolean force) { - int position = getPosition() + 1; - switch (getRepeatMode()) { - case REPEAT_MODE_ALL: - if (isLastTrack()) { - position = 0; - } - break; - case REPEAT_MODE_THIS: - if (force) { - if (isLastTrack()) { - position = 0; - } - } else { - position -= 1; - } - break; - default: - case REPEAT_MODE_NONE: - if (isLastTrack()) { - position -= 1; - } - break; - } - - return position; - } - - private boolean isLastTrack() { - return getPosition() == getPlayingQueue().size() - 1; - } - - public List getPlayingQueue() { - return playingQueue; - } - - public int getRepeatMode() { - return repeatMode; - } - - public void setRepeatMode(final int repeatMode) { - switch (repeatMode) { - case REPEAT_MODE_NONE: - case REPEAT_MODE_ALL: - case REPEAT_MODE_THIS: - this.repeatMode = repeatMode; - PreferenceUtil.getInstance(this).setRepeat(repeatMode); - prepareNext(); - notifyChange(REPEAT_MODE_CHANGED); - break; - } - } - + // FixMe: Move to QueueManager or not? public void openQueue(@Nullable final List playingQueue, final int startPosition, final boolean startPlaying) { if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size()) { // it is important to copy the playing queue here first as we might add or remove songs later - originalPlayingQueue = new ArrayList<>(playingQueue); - this.playingQueue = new ArrayList<>(originalPlayingQueue); + queueManager.originalPlayingQueue = new ArrayList<>(playingQueue); + queueManager.playingQueue = new ArrayList<>(queueManager.originalPlayingQueue); int position = startPosition; - if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { - ShuffleHelper.makeShuffleList(this.playingQueue, startPosition); + if (queueManager.shuffleMode == QueueManager.SHUFFLE_MODE_SHUFFLE) { + ShuffleHelper.makeShuffleList(queueManager.playingQueue, startPosition); position = 0; } @@ -685,84 +616,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP } } - public void addSong(int position, Song song) { - playingQueue.add(position, song); - originalPlayingQueue.add(position, song); - notifyChange(QUEUE_CHANGED); - } - - public void addSong(Song song) { - playingQueue.add(song); - originalPlayingQueue.add(song); - notifyChange(QUEUE_CHANGED); - } - - public void addSongs(int position, List songs) { - playingQueue.addAll(position, songs); - originalPlayingQueue.addAll(position, songs); - notifyChange(QUEUE_CHANGED); - } - - public void addSongs(List songs) { - playingQueue.addAll(songs); - originalPlayingQueue.addAll(songs); - notifyChange(QUEUE_CHANGED); - } - - public void removeSong(int position) { - if (getShuffleMode() == SHUFFLE_MODE_NONE) { - playingQueue.remove(position); - originalPlayingQueue.remove(position); - } else { - originalPlayingQueue.remove(playingQueue.remove(position)); - } - - reposition(position); - notifyChange(QUEUE_CHANGED); - } - - private void reposition(int deletedPosition) { - int currentPosition = getPosition(); - if (deletedPosition < currentPosition) { - position = currentPosition - 1; - } else if (deletedPosition == currentPosition) { - if (playingQueue.size() > deletedPosition) { - setPosition(position); - } else { - setPosition(position - 1); - } - } - } - - public void moveSong(int from, int to) { - if (from == to) return; - final int currentPosition = getPosition(); - Song songToMove = playingQueue.remove(from); - playingQueue.add(to, songToMove); - if (getShuffleMode() == SHUFFLE_MODE_NONE) { - Song tmpSong = originalPlayingQueue.remove(from); - originalPlayingQueue.add(to, tmpSong); - } - - if (from > currentPosition && to <= currentPosition) { - position = currentPosition + 1; - } else if (from < currentPosition && to >= currentPosition) { - position = currentPosition - 1; - } else if (from == currentPosition) { - position = to; - } - - notifyChange(QUEUE_CHANGED); - } - - public void clearQueue() { - playingQueue.clear(); - originalPlayingQueue.clear(); - - setPosition(-1); - notifyChange(QUEUE_CHANGED); - } - public void playSongAt(final int position) { // handle this on the handlers thread to avoid blocking the ui thread playerHandler.removeMessages(PLAY_SONG); @@ -790,7 +643,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP synchronized (this) { if (!playback.isPlaying()) { if (!playback.isReady()) { - playSongAt(getPosition()); + playSongAt(queueManager.position); } else { playback.start(); if (notHandledMetaChangedForCurrentTrack) { @@ -805,7 +658,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP } public void playPreviousSong(boolean force) { - playSongAt(getPreviousPosition(force)); + playSongAt(queueManager.getPreviousPosition(force)); } public void back(boolean force) { @@ -816,34 +669,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP } } - public int getPreviousPosition(boolean force) { - int newPosition = getPosition() - 1; - switch (repeatMode) { - case REPEAT_MODE_ALL: - if (newPosition < 0) { - newPosition = getPlayingQueue().size() - 1; - } - break; - case REPEAT_MODE_THIS: - if (force) { - if (newPosition < 0) { - newPosition = getPlayingQueue().size() - 1; - } - } else { - newPosition = getPosition(); - } - break; - default: - case REPEAT_MODE_NONE: - if (newPosition < 0) { - newPosition = 0; - } - break; - } - - return newPosition; - } - public int getSongProgressMillis() { return playback.getProgress(); } @@ -852,15 +677,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP return playback.getDuration(); } - public long getQueueDurationMillis(int position) { - long duration = 0; - for (int i = position + 1; i < playingQueue.size(); i++) { - duration += playingQueue.get(i).duration; - } - - return duration; - } - public int seek(int millis) { synchronized (this) { playback.setProgress(millis); @@ -869,60 +685,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP } } - public void cycleRepeatMode() { - switch (getRepeatMode()) { - case REPEAT_MODE_NONE: - setRepeatMode(REPEAT_MODE_ALL); - break; - case REPEAT_MODE_ALL: - setRepeatMode(REPEAT_MODE_THIS); - break; - default: - setRepeatMode(REPEAT_MODE_NONE); - break; - } - } - - public void toggleShuffle() { - if (getShuffleMode() == SHUFFLE_MODE_NONE) { - setShuffleMode(SHUFFLE_MODE_SHUFFLE); - } else { - setShuffleMode(SHUFFLE_MODE_NONE); - } - } - - public int getShuffleMode() { - return shuffleMode; - } - - public void setShuffleMode(final int shuffleMode) { - PreferenceUtil.getInstance(this).setShuffle(shuffleMode); - - switch (shuffleMode) { - case SHUFFLE_MODE_SHUFFLE: - this.shuffleMode = shuffleMode; - ShuffleHelper.makeShuffleList(this.getPlayingQueue(), getPosition()); - position = 0; - break; - case SHUFFLE_MODE_NONE: - this.shuffleMode = shuffleMode; - String currentSongId = getCurrentSong().id; - playingQueue = new ArrayList<>(originalPlayingQueue); - int newPosition = 0; - for (Song song : getPlayingQueue()) { - if (song.id == currentSongId) { - newPosition = getPlayingQueue().indexOf(song); - } - } - - position = newPosition; - break; - } - - notifyChange(SHUFFLE_MODE_CHANGED); - notifyChange(QUEUE_CHANGED); - } - private void notifyChange(@NonNull final String what) { handleChangeInternal(what); sendChangeInternal(what); @@ -950,14 +712,14 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP updateNotification(); updateMediaSessionMetadata(); updateMediaSessionState(); - PreferenceUtil.getInstance(this).setPosition(getPosition()); + PreferenceUtil.getInstance(this).setPosition(queueManager.position); PreferenceUtil.getInstance(this).setProgress(getSongProgressMillis()); break; case QUEUE_CHANGED: // because playing queue size might have changed updateMediaSessionMetadata(); saveState(); - if (playingQueue.size() > 0) { + if (queueManager.getPlayingQueue().size() > 0) { prepareNext(); } else { playback.pause(); @@ -1045,12 +807,12 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP switch (msg.what) { case TRACK_CHANGED: - if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { + if (service.queueManager.getRepeatMode() == QueueManager.REPEAT_MODE_NONE && service.queueManager.isLastTrack()) { service.pause(); service.seek(0); service.notifyChange(STATE_CHANGED); } else { - service.position = service.nextPosition; + service.queueManager.position = service.queueManager.nextPosition; service.prepareNextImpl(); service.notifyChange(META_CHANGED); service.notifyChange(QUEUE_CHANGED); @@ -1059,7 +821,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP case TRACK_ENDED: // if there is a timer finished, don't continue - if (service.pendingQuit || service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { + if (service.pendingQuit || service.queueManager.getRepeatMode() == QueueManager.REPEAT_MODE_NONE && service.queueManager.isLastTrack()) { service.notifyChange(STATE_CHANGED); service.seek(0); if (service.pendingQuit) { @@ -1136,7 +898,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP @Override public void handleMessage(@NonNull final Message msg) { - Song song = mService.get().getCurrentSong(); + Song song = mService.get().queueManager.getCurrentSong(); if (song == null) return; switch (msg.what) { @@ -1160,7 +922,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP public void onNext() { PlaybackStartInfo startInfo = new PlaybackStartInfo(); - startInfo.setItemId(mService.get().getCurrentSong().id); + startInfo.setItemId(mService.get().queueManager.getCurrentSong().id); startInfo.setVolumeLevel(mService.get().playback.getVolume()); startInfo.setCanSeek(true); startInfo.setIsPaused(false); @@ -1171,7 +933,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP public void onProgress() { PlaybackProgressInfo progressInfo = new PlaybackProgressInfo(); - Song current = mService.get().getCurrentSong(); + Song current = mService.get().queueManager.getCurrentSong(); String user = App.getApiClient().getCurrentUserId(); Date time = new Date(System.currentTimeMillis()); @@ -1199,7 +961,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP PlaybackStopInfo info = new PlaybackStopInfo(); long progress = mService.get().getSongProgressMillis(); - info.setItemId(mService.get().getCurrentSong().id); + info.setItemId(mService.get().queueManager.getCurrentSong().id); info.setPositionTicks(progress * 10000); if (task != null) task.cancel(true); diff --git a/app/src/main/java/com/dkanada/gramophone/service/QueueManager.java b/app/src/main/java/com/dkanada/gramophone/service/QueueManager.java new file mode 100644 index 00000000..5604e20e --- /dev/null +++ b/app/src/main/java/com/dkanada/gramophone/service/QueueManager.java @@ -0,0 +1,292 @@ +package com.dkanada.gramophone.service; + +import android.content.Context; + +import com.dkanada.gramophone.App; +import com.dkanada.gramophone.helper.MusicPlayerRemote; +import com.dkanada.gramophone.helper.ShuffleHelper; +import com.dkanada.gramophone.model.Song; +import com.dkanada.gramophone.util.PreferenceUtil; + +import java.util.ArrayList; +import java.util.List; + +public class QueueManager { + public static final int REPEAT_MODE_NONE = 0; + public static final int REPEAT_MODE_ALL = 1; + public static final int REPEAT_MODE_THIS = 2; + + public static final int SHUFFLE_MODE_NONE = 0; + public static final int SHUFFLE_MODE_SHUFFLE = 1; + + private final Context context; + private final QueueCallbacks callbacks; + + List playingQueue = new ArrayList<>(); + List originalPlayingQueue = new ArrayList<>(); + + int position = -1; + int nextPosition = -1; + + int shuffleMode; + int repeatMode; + + public QueueManager(Context context, QueueCallbacks callbacks) { + this.context = context; + this.callbacks = callbacks; + } + + public int getPosition() { + return position; + } + + public Song getCurrentSong() { + return getSongAt(getPosition()); + } + + public Song getSongAt(int position) { + if (position >= 0 && position < getPlayingQueue().size()) { + return getPlayingQueue().get(position); + } + + return null; + } + + public int getNextPosition(boolean force) { + int position = getPosition() + 1; + switch (getRepeatMode()) { + case REPEAT_MODE_ALL: + if (isLastTrack()) { + position = 0; + } + break; + case REPEAT_MODE_THIS: + if (force) { + if (isLastTrack()) { + position = 0; + } + } else { + position -= 1; + } + break; + default: + case REPEAT_MODE_NONE: + if (isLastTrack()) { + position -= 1; + } + break; + } + + return position; + } + + public int getPreviousPosition(boolean force) { + int newPosition = getPosition() - 1; + switch (repeatMode) { + case REPEAT_MODE_ALL: + if (newPosition < 0) { + newPosition = getPlayingQueue().size() - 1; + } + break; + case REPEAT_MODE_THIS: + if (force) { + if (newPosition < 0) { + newPosition = getPlayingQueue().size() - 1; + } + } else { + newPosition = getPosition(); + } + break; + default: + case REPEAT_MODE_NONE: + if (newPosition < 0) { + newPosition = 0; + } + break; + } + + return newPosition; + } + + public boolean isLastTrack() { + return getPosition() == getPlayingQueue().size() - 1; + } + + public List getPlayingQueue() { + return playingQueue; + } + + public int getRepeatMode() { + return repeatMode; + } + + public int getShuffleMode() { + return shuffleMode; + } + + public void toggleShuffle() { + if (getShuffleMode() == SHUFFLE_MODE_NONE) { + setShuffleMode(SHUFFLE_MODE_SHUFFLE); + } else { + setShuffleMode(SHUFFLE_MODE_NONE); + } + } + + public void setRepeatMode(final int repeatMode) { + switch (repeatMode) { + case REPEAT_MODE_NONE: + case REPEAT_MODE_ALL: + case REPEAT_MODE_THIS: + this.repeatMode = repeatMode; + PreferenceUtil.getInstance(context).setRepeat(repeatMode); + callbacks.onRepeatModeChanged(); + break; + } + } + + public void setShuffleMode(final int shuffleMode) { + PreferenceUtil.getInstance(context).setShuffle(shuffleMode); + + switch (shuffleMode) { + case SHUFFLE_MODE_SHUFFLE: + this.shuffleMode = shuffleMode; + ShuffleHelper.makeShuffleList(this.getPlayingQueue(), getPosition()); + position = 0; + break; + case SHUFFLE_MODE_NONE: + this.shuffleMode = shuffleMode; + String currentSongId = getCurrentSong().id; + playingQueue = new ArrayList<>(originalPlayingQueue); + int newPosition = 0; + for (Song song : getPlayingQueue()) { + if (song.id == currentSongId) { + newPosition = getPlayingQueue().indexOf(song); + } + } + + position = newPosition; + break; + } + + callbacks.onShuffleModeChanged(); + callbacks.onQueueChanged(); + } + + public void addSong(int position, Song song) { + playingQueue.add(position, song); + originalPlayingQueue.add(position, song); + callbacks.onQueueChanged(); + } + + public void addSong(Song song) { + playingQueue.add(song); + originalPlayingQueue.add(song); + callbacks.onQueueChanged(); + } + + public void addSongs(int position, List songs) { + playingQueue.addAll(position, songs); + originalPlayingQueue.addAll(position, songs); + callbacks.onQueueChanged(); + } + + public void addSongs(List songs) { + playingQueue.addAll(songs); + originalPlayingQueue.addAll(songs); + callbacks.onQueueChanged(); + } + + public void removeSong(int position) { + if (getShuffleMode() == SHUFFLE_MODE_NONE) { + playingQueue.remove(position); + originalPlayingQueue.remove(position); + } else { + originalPlayingQueue.remove(playingQueue.remove(position)); + } + + reposition(position); + callbacks.onQueueChanged(); + } + + // FixMe: Remove static MusicPlayerRemote call, idk how... + private void reposition(int deletedPosition) { + int currentPosition = getPosition(); + if (deletedPosition < currentPosition) { + position = currentPosition - 1; + } else if (deletedPosition == currentPosition) { + if (playingQueue.size() > deletedPosition) { + MusicPlayerRemote.setPosition(position); + } else { + MusicPlayerRemote.setPosition(position - 1); + } + } + } + + public void moveSong(int from, int to) { + if (from == to) return; + final int currentPosition = getPosition(); + Song songToMove = playingQueue.remove(from); + playingQueue.add(to, songToMove); + if (getShuffleMode() == SHUFFLE_MODE_NONE) { + Song tmpSong = originalPlayingQueue.remove(from); + originalPlayingQueue.add(to, tmpSong); + } + + if (from > currentPosition && to <= currentPosition) { + position = currentPosition + 1; + } else if (from < currentPosition && to >= currentPosition) { + position = currentPosition - 1; + } else if (from == currentPosition) { + position = to; + } + + callbacks.onQueueChanged(); + } + + public void clearQueue() { + playingQueue.clear(); + originalPlayingQueue.clear(); + + position = -1; + callbacks.onQueueChanged(); + } + + public long getQueueDurationMillis(int position) { + long duration = 0; + for (int i = position + 1; i < playingQueue.size(); i++) { + duration += playingQueue.get(i).duration; + } + + return duration; + } + + public void cycleRepeatMode() { + switch (getRepeatMode()) { + case REPEAT_MODE_NONE: + setRepeatMode(REPEAT_MODE_ALL); + break; + case REPEAT_MODE_ALL: + setRepeatMode(REPEAT_MODE_THIS); + break; + default: + setRepeatMode(REPEAT_MODE_NONE); + break; + } + } + + public void saveQueue() { + // copy queues by value to avoid concurrent modification exceptions from database + App.getDatabase().songDao().deleteSongs(); + App.getDatabase().songDao().insertSongs(new ArrayList<>(playingQueue)); + + App.getDatabase().queueSongDao().deleteQueueSongs(); + App.getDatabase().queueSongDao().setQueue(new ArrayList<>(playingQueue), 0); + App.getDatabase().queueSongDao().setQueue(new ArrayList<>(originalPlayingQueue), 1); + } + + interface QueueCallbacks { + void onQueueChanged(); + void onRepeatModeChanged(); + void onShuffleModeChanged(); + } +} diff --git a/app/src/main/java/com/dkanada/gramophone/service/notifications/PlayingNotificationMarshmallow.java b/app/src/main/java/com/dkanada/gramophone/service/notifications/PlayingNotificationMarshmallow.java index d8b385df..494ffc80 100644 --- a/app/src/main/java/com/dkanada/gramophone/service/notifications/PlayingNotificationMarshmallow.java +++ b/app/src/main/java/com/dkanada/gramophone/service/notifications/PlayingNotificationMarshmallow.java @@ -39,7 +39,7 @@ public class PlayingNotificationMarshmallow extends PlayingNotification { public synchronized void update() { stopped = false; - final Song song = service.getCurrentSong(); + final Song song = service.queueManager.getCurrentSong(); final boolean isPlaying = service.isPlaying(); final RemoteViews notificationLayout = new RemoteViews(service.getPackageName(), R.layout.notification); diff --git a/app/src/main/java/com/dkanada/gramophone/service/notifications/PlayingNotificationNougat.java b/app/src/main/java/com/dkanada/gramophone/service/notifications/PlayingNotificationNougat.java index 0a0d1980..3ce80842 100644 --- a/app/src/main/java/com/dkanada/gramophone/service/notifications/PlayingNotificationNougat.java +++ b/app/src/main/java/com/dkanada/gramophone/service/notifications/PlayingNotificationNougat.java @@ -33,7 +33,7 @@ public class PlayingNotificationNougat extends PlayingNotification { public synchronized void update() { stopped = false; - final Song song = service.getCurrentSong(); + final Song song = service.queueManager.getCurrentSong(); final boolean isPlaying = service.isPlaying(); final int playButtonResId = isPlaying diff --git a/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetAlbum.java b/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetAlbum.java index 8f91b26d..13959a8a 100644 --- a/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetAlbum.java +++ b/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetAlbum.java @@ -68,7 +68,7 @@ public class AppWidgetAlbum extends BaseAppWidget { final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(), R.layout.app_widget_album); final boolean isPlaying = service.isPlaying(); - final Song song = service.getCurrentSong(); + final Song song = service.queueManager.getCurrentSong(); if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); diff --git a/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetCard.java b/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetCard.java index 66a57393..84a31478 100644 --- a/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetCard.java +++ b/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetCard.java @@ -66,7 +66,7 @@ public class AppWidgetCard extends BaseAppWidget { final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(), R.layout.app_widget_card); final boolean isPlaying = service.isPlaying(); - final Song song = service.getCurrentSong(); + final Song song = service.queueManager.getCurrentSong(); if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); diff --git a/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetClassic.java b/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetClassic.java index 95b6fa62..b09aaf9e 100644 --- a/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetClassic.java +++ b/app/src/main/java/com/dkanada/gramophone/views/widgets/AppWidgetClassic.java @@ -66,7 +66,7 @@ public class AppWidgetClassic extends BaseAppWidget { final RemoteViews appWidgetView = new RemoteViews(service.getPackageName(), R.layout.app_widget_classic); final boolean isPlaying = service.isPlaying(); - final Song song = service.getCurrentSong(); + final Song song = service.queueManager.getCurrentSong(); if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); diff --git a/app/src/main/java/com/dkanada/gramophone/views/widgets/BaseAppWidget.java b/app/src/main/java/com/dkanada/gramophone/views/widgets/BaseAppWidget.java index 63ead5f7..38246f75 100644 --- a/app/src/main/java/com/dkanada/gramophone/views/widgets/BaseAppWidget.java +++ b/app/src/main/java/com/dkanada/gramophone/views/widgets/BaseAppWidget.java @@ -25,6 +25,7 @@ import com.dkanada.gramophone.R; import com.dkanada.gramophone.activities.MainActivity; import com.dkanada.gramophone.model.Song; import com.dkanada.gramophone.service.MusicService; +import com.dkanada.gramophone.service.QueueManager; import com.dkanada.gramophone.util.MusicUtil; import java.util.Arrays; @@ -62,7 +63,7 @@ public abstract class BaseAppWidget extends AppWidgetProvider { appWidgetIds = appWidgetManager.getAppWidgetIds(componentName); } - Song song = service.getCurrentSong(); + Song song = service.queueManager.getCurrentSong(); if (song != null && (what.equals(MusicService.STATE_CHANGED) || what.equals(MusicService.META_CHANGED))) { updateMeta(service, appWidgetIds); }