Merge pull request #208 from jakobkukla/new-refactor-queuemanager

Refactor: Move queue management code into own class
This commit is contained in:
dkanada 2022-04-24 12:31:11 +09:00 committed by GitHub
commit 88ac1e0286
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 409 additions and 353 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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<Song> 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<Song> queue = new ArrayList<>();
queue.add(song);
@ -298,9 +299,9 @@ public class MusicPlayerRemote {
}
public static boolean playNext(@NonNull List<Song> 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<Song> queue = new ArrayList<>();
queue.add(song);
@ -331,9 +332,9 @@ public class MusicPlayerRemote {
}
public static boolean enqueue(@NonNull List<Song> 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;
}

View file

@ -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<Song> playingQueue = new ArrayList<>();
private List<Song> 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 This call will be removed in a subsequent PR
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<Song> 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 This will be refactored and partly moved to QueueManager in a subsequent PR
private synchronized void restoreQueuesAndPositionIfNecessary() {
if (!queuesRestored && playingQueue.isEmpty()) {
if (!queuesRestored && queueManager.getPlayingQueue().isEmpty()) {
List<Song> restoredQueue = App.getDatabase().queueSongDao().getQueue(0);
List<Song> 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<Song> 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 This will be refactored and partly moved to QueueManager in a subsequent PR
public void openQueue(@Nullable final List<Song> 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<Song> songs) {
playingQueue.addAll(position, songs);
originalPlayingQueue.addAll(position, songs);
notifyChange(QUEUE_CHANGED);
}
public void addSongs(List<Song> 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);

View file

@ -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<Song> playingQueue = new ArrayList<>();
List<Song> 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<Song> 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<Song> songs) {
playingQueue.addAll(position, songs);
originalPlayingQueue.addAll(position, songs);
callbacks.onQueueChanged();
}
public void addSongs(List<Song> 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 This will be refactored and removed in a subsequent PR
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();
}
}

View file

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

View file

@ -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

View file

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

View file

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

View file

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

View file

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