Merge pull request #211 from jakobkukla/new-rewrite-playback
Cleanup and rework playback and shuffle logic
This commit is contained in:
commit
4f7f315ccd
8 changed files with 269 additions and 365 deletions
|
|
@ -45,4 +45,15 @@ public abstract class QueueSongDao {
|
||||||
|
|
||||||
insertQueueSongs(queueSongs);
|
insertQueueSongs(queueSongs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
public void updateQueues(List<Song> playingQueue, List<Song> shuffledQueue) {
|
||||||
|
// copy queues by value to avoid concurrent modification exceptions from database
|
||||||
|
App.getDatabase().songDao().deleteSongs();
|
||||||
|
App.getDatabase().songDao().insertSongs(new ArrayList<>(playingQueue));
|
||||||
|
|
||||||
|
deleteQueueSongs();
|
||||||
|
setQueue(new ArrayList<>(playingQueue), 0);
|
||||||
|
setQueue(new ArrayList<>(shuffledQueue), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,19 +111,19 @@ public class MusicPlayerRemote {
|
||||||
|
|
||||||
public static void playNextSong() {
|
public static void playNextSong() {
|
||||||
if (musicService != null) {
|
if (musicService != null) {
|
||||||
musicService.playNextSong(true);
|
musicService.playNextSong();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void playPreviousSong() {
|
public static void playPreviousSong() {
|
||||||
if (musicService != null) {
|
if (musicService != null) {
|
||||||
musicService.playPreviousSong(true);
|
musicService.playPreviousSong();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void back() {
|
public static void back() {
|
||||||
if (musicService != null) {
|
if (musicService != null) {
|
||||||
musicService.back(true);
|
musicService.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,10 +143,10 @@ public class MusicPlayerRemote {
|
||||||
|
|
||||||
public static void openQueue(final List<Song> queue, final int startPosition, final boolean startPlaying) {
|
public static void openQueue(final List<Song> queue, final int startPosition, final boolean startPlaying) {
|
||||||
if (!tryToHandleOpenPlayingQueue(queue, startPosition) && musicService != null) {
|
if (!tryToHandleOpenPlayingQueue(queue, startPosition) && musicService != null) {
|
||||||
musicService.openQueue(queue, startPosition, startPlaying);
|
|
||||||
if (!PreferenceUtil.getInstance(musicService).getRememberShuffle()) {
|
if (!PreferenceUtil.getInstance(musicService).getRememberShuffle()) {
|
||||||
setShuffleMode(QueueManager.SHUFFLE_MODE_NONE);
|
setShuffleMode(QueueManager.SHUFFLE_MODE_NONE);
|
||||||
}
|
}
|
||||||
|
musicService.openQueue(queue, startPosition, startPlaying);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,8 +157,8 @@ public class MusicPlayerRemote {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tryToHandleOpenPlayingQueue(queue, startPosition) && musicService != null) {
|
if (!tryToHandleOpenPlayingQueue(queue, startPosition) && musicService != null) {
|
||||||
openQueue(queue, startPosition, startPlaying);
|
|
||||||
setShuffleMode(QueueManager.SHUFFLE_MODE_SHUFFLE);
|
setShuffleMode(QueueManager.SHUFFLE_MODE_SHUFFLE);
|
||||||
|
openQueue(queue, startPosition, startPlaying);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
package com.dkanada.gramophone.helper;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.dkanada.gramophone.model.Song;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ShuffleHelper {
|
|
||||||
public static void makeShuffleList(@NonNull List<Song> listToShuffle, final int current) {
|
|
||||||
if (listToShuffle.isEmpty()) return;
|
|
||||||
|
|
||||||
if (current >= 0) {
|
|
||||||
Song song = listToShuffle.remove(current);
|
|
||||||
|
|
||||||
Collections.shuffle(listToShuffle);
|
|
||||||
listToShuffle.add(0, song);
|
|
||||||
} else {
|
|
||||||
Collections.shuffle(listToShuffle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -39,7 +39,6 @@ import com.dkanada.gramophone.BuildConfig;
|
||||||
import com.dkanada.gramophone.R;
|
import com.dkanada.gramophone.R;
|
||||||
import com.dkanada.gramophone.glide.BlurTransformation;
|
import com.dkanada.gramophone.glide.BlurTransformation;
|
||||||
import com.dkanada.gramophone.glide.CustomGlideRequest;
|
import com.dkanada.gramophone.glide.CustomGlideRequest;
|
||||||
import com.dkanada.gramophone.helper.ShuffleHelper;
|
|
||||||
import com.dkanada.gramophone.model.Playlist;
|
import com.dkanada.gramophone.model.Playlist;
|
||||||
import com.dkanada.gramophone.model.Song;
|
import com.dkanada.gramophone.model.Song;
|
||||||
import com.dkanada.gramophone.service.notifications.PlayingNotification;
|
import com.dkanada.gramophone.service.notifications.PlayingNotification;
|
||||||
|
|
@ -53,6 +52,7 @@ import com.dkanada.gramophone.util.Util;
|
||||||
import com.dkanada.gramophone.views.widgets.AppWidgetAlbum;
|
import com.dkanada.gramophone.views.widgets.AppWidgetAlbum;
|
||||||
import com.dkanada.gramophone.views.widgets.AppWidgetCard;
|
import com.dkanada.gramophone.views.widgets.AppWidgetCard;
|
||||||
import com.dkanada.gramophone.views.widgets.AppWidgetClassic;
|
import com.dkanada.gramophone.views.widgets.AppWidgetClassic;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
|
||||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||||
import org.jellyfin.apiclient.interaction.Response;
|
import org.jellyfin.apiclient.interaction.Response;
|
||||||
|
|
@ -103,9 +103,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
public static final int TRACK_CHANGED = 1;
|
public static final int TRACK_CHANGED = 1;
|
||||||
public static final int TRACK_ENDED = 2;
|
public static final int TRACK_ENDED = 2;
|
||||||
|
|
||||||
public static final int PLAY_SONG = 3;
|
|
||||||
public static final int PREPARE_NEXT = 4;
|
|
||||||
|
|
||||||
public static final int SAVE_QUEUE = 0;
|
public static final int SAVE_QUEUE = 0;
|
||||||
public static final int LOAD_QUEUE = 9;
|
public static final int LOAD_QUEUE = 9;
|
||||||
|
|
||||||
|
|
@ -127,27 +124,26 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
private MediaSessionCompat mediaSession;
|
private MediaSessionCompat mediaSession;
|
||||||
private PowerManager.WakeLock wakeLock;
|
private PowerManager.WakeLock wakeLock;
|
||||||
|
|
||||||
private PlaybackHandler playerHandler;
|
|
||||||
private Handler uiThreadHandler;
|
private Handler uiThreadHandler;
|
||||||
private ThrottledSeekHandler throttledSeekHandler;
|
private ThrottledSeekHandler throttledSeekHandler;
|
||||||
private QueueHandler queueHandler;
|
private QueueHandler queueHandler;
|
||||||
private ProgressHandler progressHandler;
|
private ProgressHandler progressHandler;
|
||||||
|
|
||||||
private HandlerThread playerHandlerThread;
|
|
||||||
private HandlerThread progressHandlerThread;
|
private HandlerThread progressHandlerThread;
|
||||||
private HandlerThread queueHandlerThread;
|
private HandlerThread queueHandlerThread;
|
||||||
|
|
||||||
public final QueueManager.QueueCallbacks queueCallbacks = new QueueManager.QueueCallbacks() {
|
public final QueueManager.QueueCallbacks queueCallbacks = new QueueManager.QueueCallbacks() {
|
||||||
@Override
|
@Override
|
||||||
public void onQueueChanged() {
|
public void onQueueChanged() {
|
||||||
|
playback.setQueue(queueManager.getPlayingQueue(), queueManager.getPosition(), queueManager.getRestoredProgress(), queueManager.isResetCurrentSong());
|
||||||
|
saveState();
|
||||||
notifyChange(QUEUE_CHANGED);
|
notifyChange(QUEUE_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRepeatModeChanged() {
|
public void onRepeatModeChanged() {
|
||||||
|
playback.setRepeatMode(queueManager.getRepeatMode());
|
||||||
notifyChange(REPEAT_MODE_CHANGED);
|
notifyChange(REPEAT_MODE_CHANGED);
|
||||||
// FIXME This call will be removed in a subsequent PR
|
|
||||||
prepareNext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -160,6 +156,11 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
@Override
|
@Override
|
||||||
public void onStateChanged(int state) {
|
public void onStateChanged(int state) {
|
||||||
notifyChange(STATE_CHANGED);
|
notifyChange(STATE_CHANGED);
|
||||||
|
|
||||||
|
if (state == Player.STATE_ENDED) {
|
||||||
|
playingNotification.stop();
|
||||||
|
releaseWakeLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -168,7 +169,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
|
|
||||||
if (ready) {
|
if (ready) {
|
||||||
progressHandler.sendEmptyMessage(TRACK_STARTED);
|
progressHandler.sendEmptyMessage(TRACK_STARTED);
|
||||||
prepareNext();
|
|
||||||
} else if (reason == PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM) {
|
} else if (reason == PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM) {
|
||||||
progressHandler.sendEmptyMessage(TRACK_ENDED);
|
progressHandler.sendEmptyMessage(TRACK_ENDED);
|
||||||
}
|
}
|
||||||
|
|
@ -179,11 +179,10 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
acquireWakeLock(30000);
|
acquireWakeLock(30000);
|
||||||
|
|
||||||
if (reason == MEDIA_ITEM_TRANSITION_REASON_AUTO) {
|
if (reason == MEDIA_ITEM_TRANSITION_REASON_AUTO) {
|
||||||
playerHandler.sendEmptyMessage(TRACK_CHANGED);
|
|
||||||
progressHandler.sendEmptyMessage(TRACK_CHANGED);
|
progressHandler.sendEmptyMessage(TRACK_CHANGED);
|
||||||
|
queueManager.setNextPosition();
|
||||||
} else if (reason == MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED) {
|
} else if (reason == MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED) {
|
||||||
progressHandler.sendEmptyMessage(TRACK_CHANGED);
|
progressHandler.sendEmptyMessage(TRACK_CHANGED);
|
||||||
prepareNext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyChange(STATE_CHANGED);
|
notifyChange(STATE_CHANGED);
|
||||||
|
|
@ -241,10 +240,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
|
|
||||||
queueManager = new QueueManager(this, queueCallbacks);
|
queueManager = new QueueManager(this, queueCallbacks);
|
||||||
|
|
||||||
playerHandlerThread = new HandlerThread(PlaybackHandler.class.getName());
|
|
||||||
playerHandlerThread.start();
|
|
||||||
playerHandler = new PlaybackHandler(this, playerHandlerThread.getLooper());
|
|
||||||
|
|
||||||
progressHandlerThread = new HandlerThread(ProgressHandler.class.getName());
|
progressHandlerThread = new HandlerThread(ProgressHandler.class.getName());
|
||||||
progressHandlerThread.start();
|
progressHandlerThread.start();
|
||||||
progressHandler = new ProgressHandler(this, progressHandlerThread.getLooper());
|
progressHandler = new ProgressHandler(this, progressHandlerThread.getLooper());
|
||||||
|
|
@ -290,12 +285,12 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSkipToNext() {
|
public void onSkipToNext() {
|
||||||
playNextSong(true);
|
playNextSong();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSkipToPrevious() {
|
public void onSkipToPrevious() {
|
||||||
back(true);
|
back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -357,10 +352,10 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ACTION_REWIND:
|
case ACTION_REWIND:
|
||||||
back(true);
|
back();
|
||||||
break;
|
break;
|
||||||
case ACTION_SKIP:
|
case ACTION_SKIP:
|
||||||
playNextSong(true);
|
playNextSong();
|
||||||
break;
|
break;
|
||||||
case ACTION_STOP:
|
case ACTION_STOP:
|
||||||
case ACTION_QUIT:
|
case ACTION_QUIT:
|
||||||
|
|
@ -422,42 +417,17 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
queueHandler.removeMessages(SAVE_QUEUE);
|
queueHandler.removeMessages(SAVE_QUEUE);
|
||||||
queueHandler.sendEmptyMessage(SAVE_QUEUE);
|
queueHandler.sendEmptyMessage(SAVE_QUEUE);
|
||||||
|
|
||||||
PreferenceUtil.getInstance(this).setPosition(queueManager.position);
|
|
||||||
PreferenceUtil.getInstance(this).setProgress(getSongProgressMillis());
|
PreferenceUtil.getInstance(this).setProgress(getSongProgressMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restoreState() {
|
private void restoreState() {
|
||||||
queueManager.shuffleMode = PreferenceUtil.getInstance(this).getShuffle();
|
|
||||||
queueManager.repeatMode = PreferenceUtil.getInstance(this).getRepeat();
|
|
||||||
|
|
||||||
notifyChange(SHUFFLE_MODE_CHANGED);
|
|
||||||
notifyChange(REPEAT_MODE_CHANGED);
|
|
||||||
|
|
||||||
queueHandler.removeMessages(LOAD_QUEUE);
|
queueHandler.removeMessages(LOAD_QUEUE);
|
||||||
queueHandler.sendEmptyMessage(LOAD_QUEUE);
|
queueHandler.sendEmptyMessage(LOAD_QUEUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME This will be refactored and partly moved to QueueManager in a subsequent PR
|
|
||||||
private synchronized void restoreQueuesAndPositionIfNecessary() {
|
private synchronized void restoreQueuesAndPositionIfNecessary() {
|
||||||
if (!queuesRestored && queueManager.getPlayingQueue().isEmpty()) {
|
if (!queuesRestored && queueManager.getPlayingQueue().isEmpty()) {
|
||||||
List<Song> restoredQueue = App.getDatabase().queueSongDao().getQueue(0);
|
queueManager.restoreQueue();
|
||||||
List<Song> restoredOriginalQueue = App.getDatabase().queueSongDao().getQueue(1);
|
|
||||||
|
|
||||||
int restoredPosition = PreferenceUtil.getInstance(this).getPosition();
|
|
||||||
int restoredProgress = PreferenceUtil.getInstance(this).getProgress();
|
|
||||||
|
|
||||||
if (restoredQueue.size() > 0 && restoredQueue.size() == restoredOriginalQueue.size() && restoredPosition != -1) {
|
|
||||||
queueManager.originalPlayingQueue = restoredOriginalQueue;
|
|
||||||
queueManager.playingQueue = restoredQueue;
|
|
||||||
|
|
||||||
queueManager.position = restoredPosition;
|
|
||||||
openCurrent();
|
|
||||||
|
|
||||||
if (restoredProgress > 0) seek(restoredProgress);
|
|
||||||
|
|
||||||
handleChangeInternal(META_CHANGED);
|
|
||||||
handleChangeInternal(QUEUE_CHANGED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
queuesRestored = true;
|
queuesRestored = true;
|
||||||
|
|
@ -471,9 +441,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseResources() {
|
private void releaseResources() {
|
||||||
playerHandler.removeCallbacksAndMessages(null);
|
|
||||||
playerHandlerThread.quitSafely();
|
|
||||||
|
|
||||||
progressHandler.removeCallbacksAndMessages(null);
|
progressHandler.removeCallbacksAndMessages(null);
|
||||||
progressHandlerThread.quitSafely();
|
progressHandlerThread.quitSafely();
|
||||||
|
|
||||||
|
|
@ -492,34 +459,14 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
return playback != null && playback.isLoading();
|
return playback != null && playback.isLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void playPreviousSong() {
|
||||||
public void playNextSong(boolean force) {
|
queueManager.setPreviousPosition();
|
||||||
playSongAt(queueManager.getNextPosition(force));
|
playback.previous();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void openTrackAndPrepareNextAt(int position) {
|
public void playNextSong() {
|
||||||
queueManager.position = position;
|
queueManager.setNextPosition();
|
||||||
|
playback.next();
|
||||||
openCurrent();
|
|
||||||
playback.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void openCurrent() {
|
|
||||||
if (queueManager.getCurrentSong() == null) return;
|
|
||||||
|
|
||||||
playback.setDataSource(queueManager.getCurrentSong());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepareNext() {
|
|
||||||
playerHandler.removeMessages(PREPARE_NEXT);
|
|
||||||
playerHandler.obtainMessage(PREPARE_NEXT).sendToTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void prepareNextImpl() {
|
|
||||||
if (queueManager.getCurrentSong() == null) return;
|
|
||||||
|
|
||||||
queueManager.nextPosition = queueManager.getNextPosition(false);
|
|
||||||
playback.queueDataSource(queueManager.getSongAt(queueManager.nextPosition));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initNotification() {
|
public void initNotification() {
|
||||||
|
|
@ -561,7 +508,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.albumName)
|
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.albumName)
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title)
|
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title)
|
||||||
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration)
|
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration)
|
||||||
.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, queueManager.position + 1)
|
.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, queueManager.getPosition() + 1)
|
||||||
.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.year)
|
.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, song.year)
|
||||||
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null);
|
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, null);
|
||||||
|
|
||||||
|
|
@ -615,29 +562,17 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
uiThreadHandler.post(runnable);
|
uiThreadHandler.post(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
public void openQueue(@Nullable final List<Song> playingQueue, final int startPosition, final boolean startPlaying) {
|
||||||
if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size()) {
|
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
|
queueManager.setPlayingQueueAndPosition(playingQueue, startPosition);
|
||||||
queueManager.originalPlayingQueue = new ArrayList<>(playingQueue);
|
if (startPlaying) play();
|
||||||
queueManager.playingQueue = new ArrayList<>(queueManager.originalPlayingQueue);
|
|
||||||
|
|
||||||
int position = startPosition;
|
|
||||||
if (queueManager.shuffleMode == QueueManager.SHUFFLE_MODE_SHUFFLE) {
|
|
||||||
ShuffleHelper.makeShuffleList(queueManager.playingQueue, startPosition);
|
|
||||||
position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
playSongAt(position);
|
|
||||||
|
|
||||||
notifyChange(QUEUE_CHANGED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playSongAt(final int position) {
|
public void playSongAt(final int position) {
|
||||||
// handle this on the handlers thread to avoid blocking the ui thread
|
queueManager.setPosition(position);
|
||||||
playerHandler.removeMessages(PLAY_SONG);
|
playback.playSongAt(position);
|
||||||
playerHandler.obtainMessage(PLAY_SONG, position, 0).sendToTarget();
|
play();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pause() {
|
public void pause() {
|
||||||
|
|
@ -646,25 +581,17 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void play() {
|
public void play() {
|
||||||
if (!playback.isPlaying()) {
|
if (!playback.isPlaying()) {
|
||||||
if (!playback.isReady()) {
|
|
||||||
playSongAt(queueManager.position);
|
|
||||||
} else {
|
|
||||||
playback.start();
|
playback.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void playPreviousSong(boolean force) {
|
public void back() {
|
||||||
playSongAt(queueManager.getPreviousPosition(force));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void back(boolean force) {
|
|
||||||
if (getSongProgressMillis() > 5000) {
|
if (getSongProgressMillis() > 5000) {
|
||||||
seek(0);
|
seek(0);
|
||||||
} else {
|
} else {
|
||||||
playPreviousSong(force);
|
playPreviousSong();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -709,19 +636,11 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
updateNotification();
|
updateNotification();
|
||||||
updateMediaSessionMetadata();
|
updateMediaSessionMetadata();
|
||||||
updateMediaSessionState();
|
updateMediaSessionState();
|
||||||
PreferenceUtil.getInstance(this).setPosition(queueManager.position);
|
PreferenceUtil.getInstance(this).setPosition(queueManager.getPosition());
|
||||||
PreferenceUtil.getInstance(this).setProgress(getSongProgressMillis());
|
PreferenceUtil.getInstance(this).setProgress(getSongProgressMillis());
|
||||||
break;
|
break;
|
||||||
case QUEUE_CHANGED:
|
case QUEUE_CHANGED:
|
||||||
// because playing queue size might have changed
|
|
||||||
updateMediaSessionMetadata();
|
updateMediaSessionMetadata();
|
||||||
saveState();
|
|
||||||
if (queueManager.getPlayingQueue().size() > 0) {
|
|
||||||
prepareNext();
|
|
||||||
} else {
|
|
||||||
playback.pause();
|
|
||||||
playingNotification.stop();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -757,63 +676,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class PlaybackHandler extends Handler {
|
|
||||||
private final WeakReference<MusicService> mService;
|
|
||||||
|
|
||||||
public PlaybackHandler(final MusicService service, @NonNull final Looper looper) {
|
|
||||||
super(looper);
|
|
||||||
mService = new WeakReference<>(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(@NonNull final Message msg) {
|
|
||||||
final MusicService service = mService.get();
|
|
||||||
if (service == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (msg.what) {
|
|
||||||
case TRACK_CHANGED:
|
|
||||||
if (service.queueManager.getRepeatMode() == QueueManager.REPEAT_MODE_NONE && service.queueManager.isLastTrack()) {
|
|
||||||
service.pause();
|
|
||||||
service.seek(0);
|
|
||||||
} else {
|
|
||||||
service.queueManager.position = service.queueManager.nextPosition;
|
|
||||||
service.prepareNextImpl();
|
|
||||||
service.notifyChange(QUEUE_CHANGED);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TRACK_ENDED:
|
|
||||||
// FIXME This isn't used anywhere. This means releaseWakeLock() is never called
|
|
||||||
|
|
||||||
// if there is a timer finished, don't continue
|
|
||||||
if (service.pendingQuit || service.queueManager.getRepeatMode() == QueueManager.REPEAT_MODE_NONE && service.queueManager.isLastTrack()) {
|
|
||||||
service.notifyChange(STATE_CHANGED);
|
|
||||||
service.seek(0);
|
|
||||||
if (service.pendingQuit) {
|
|
||||||
service.pendingQuit = false;
|
|
||||||
service.quit();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
service.playNextSong(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
service.releaseWakeLock();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PLAY_SONG:
|
|
||||||
service.openTrackAndPrepareNextAt(msg.arg1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PREPARE_NEXT:
|
|
||||||
service.prepareNextImpl();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MusicBinder extends Binder {
|
public class MusicBinder extends Binder {
|
||||||
@NonNull
|
@NonNull
|
||||||
public MusicService getService() {
|
public MusicService getService() {
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,19 @@ package com.dkanada.gramophone.service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.dkanada.gramophone.App;
|
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.model.Song;
|
||||||
import com.dkanada.gramophone.util.PreferenceUtil;
|
import com.dkanada.gramophone.util.PreferenceUtil;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class QueueManager {
|
public class QueueManager {
|
||||||
public static final int REPEAT_MODE_NONE = 0;
|
public static final int REPEAT_MODE_NONE = 0;
|
||||||
public static final int REPEAT_MODE_ALL = 1;
|
public static final int REPEAT_MODE_THIS = 1;
|
||||||
public static final int REPEAT_MODE_THIS = 2;
|
public static final int REPEAT_MODE_ALL = 2;
|
||||||
|
|
||||||
public static final int SHUFFLE_MODE_NONE = 0;
|
public static final int SHUFFLE_MODE_NONE = 0;
|
||||||
public static final int SHUFFLE_MODE_SHUFFLE = 1;
|
public static final int SHUFFLE_MODE_SHUFFLE = 1;
|
||||||
|
|
@ -22,20 +23,34 @@ public class QueueManager {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final QueueCallbacks callbacks;
|
private final QueueCallbacks callbacks;
|
||||||
|
|
||||||
List<Song> playingQueue = new ArrayList<>();
|
private List<Song> playingQueue = new ArrayList<>();
|
||||||
List<Song> originalPlayingQueue = new ArrayList<>();
|
private List<Song> shuffledQueue = new ArrayList<>();
|
||||||
|
|
||||||
int position = -1;
|
private int position = 0;
|
||||||
int nextPosition = -1;
|
private int restoredProgress = 0;
|
||||||
|
private boolean resetCurrentSong = true;
|
||||||
|
|
||||||
int shuffleMode;
|
private int shuffleMode;
|
||||||
int repeatMode;
|
private @Player.RepeatMode int repeatMode;
|
||||||
|
|
||||||
public QueueManager(Context context, QueueCallbacks callbacks) {
|
public QueueManager(Context context, QueueCallbacks callbacks) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.callbacks = callbacks;
|
this.callbacks = callbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPlayingQueueAndPosition(List<Song> queue, int position) {
|
||||||
|
this.position = position;
|
||||||
|
this.playingQueue = new ArrayList<>(queue);
|
||||||
|
this.shuffledQueue = new ArrayList<>(queue);
|
||||||
|
shuffleQueue();
|
||||||
|
|
||||||
|
callbacks.onQueueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Song> getPlayingQueue() {
|
||||||
|
return shuffleMode == SHUFFLE_MODE_SHUFFLE ? shuffledQueue : playingQueue;
|
||||||
|
}
|
||||||
|
|
||||||
public int getPosition() {
|
public int getPosition() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
@ -52,68 +67,32 @@ public class QueueManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNextPosition(boolean force) {
|
public void setPosition(int position) {
|
||||||
int position = getPosition() + 1;
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextPosition() {
|
||||||
switch (getRepeatMode()) {
|
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:
|
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:
|
case REPEAT_MODE_THIS:
|
||||||
if (force) {
|
position = Math.min(position + 1, playingQueue.size() - 1);
|
||||||
if (newPosition < 0) {
|
|
||||||
newPosition = getPlayingQueue().size() - 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newPosition = getPosition();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
case REPEAT_MODE_ALL:
|
||||||
|
position = (position + 1) % playingQueue.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreviousPosition() {
|
||||||
|
switch (getRepeatMode()) {
|
||||||
case REPEAT_MODE_NONE:
|
case REPEAT_MODE_NONE:
|
||||||
if (newPosition < 0) {
|
case REPEAT_MODE_THIS:
|
||||||
newPosition = 0;
|
position = Math.max(position - 1, 0);
|
||||||
}
|
break;
|
||||||
|
case REPEAT_MODE_ALL:
|
||||||
|
position = (position - 1 + playingQueue.size()) % playingQueue.size();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return newPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLastTrack() {
|
|
||||||
return getPosition() == getPlayingQueue().size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Song> getPlayingQueue() {
|
|
||||||
return playingQueue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getRepeatMode() {
|
public int getRepeatMode() {
|
||||||
|
|
@ -150,87 +129,110 @@ public class QueueManager {
|
||||||
switch (shuffleMode) {
|
switch (shuffleMode) {
|
||||||
case SHUFFLE_MODE_SHUFFLE:
|
case SHUFFLE_MODE_SHUFFLE:
|
||||||
this.shuffleMode = shuffleMode;
|
this.shuffleMode = shuffleMode;
|
||||||
ShuffleHelper.makeShuffleList(this.getPlayingQueue(), getPosition());
|
shuffleQueue();
|
||||||
position = 0;
|
|
||||||
break;
|
break;
|
||||||
case SHUFFLE_MODE_NONE:
|
case SHUFFLE_MODE_NONE:
|
||||||
this.shuffleMode = shuffleMode;
|
|
||||||
String currentSongId = getCurrentSong().id;
|
String currentSongId = getCurrentSong().id;
|
||||||
playingQueue = new ArrayList<>(originalPlayingQueue);
|
|
||||||
int newPosition = 0;
|
int newPosition = 0;
|
||||||
for (Song song : getPlayingQueue()) {
|
|
||||||
if (song.id == currentSongId) {
|
Optional<Song> currentSong = playingQueue.stream()
|
||||||
newPosition = getPlayingQueue().indexOf(song);
|
.filter(song -> song.id.equals(currentSongId))
|
||||||
}
|
.findFirst();
|
||||||
|
|
||||||
|
if (currentSong.isPresent()) {
|
||||||
|
newPosition = playingQueue.indexOf(currentSong.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shuffledQueue = new ArrayList<>(playingQueue);
|
||||||
|
|
||||||
position = newPosition;
|
position = newPosition;
|
||||||
|
this.shuffleMode = shuffleMode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetCurrentSong = false;
|
||||||
callbacks.onShuffleModeChanged();
|
callbacks.onShuffleModeChanged();
|
||||||
callbacks.onQueueChanged();
|
callbacks.onQueueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void shuffleQueue() {
|
||||||
|
if (shuffleMode == SHUFFLE_MODE_SHUFFLE) {
|
||||||
|
this.shuffledQueue = new ArrayList<>(playingQueue);
|
||||||
|
|
||||||
|
if (!shuffledQueue.isEmpty()) {
|
||||||
|
if (getPosition() >= 0) {
|
||||||
|
Song song = shuffledQueue.remove(getPosition());
|
||||||
|
|
||||||
|
Collections.shuffle(shuffledQueue);
|
||||||
|
shuffledQueue.add(0, song);
|
||||||
|
} else {
|
||||||
|
Collections.shuffle(shuffledQueue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
position = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addSong(int position, Song song) {
|
public void addSong(int position, Song song) {
|
||||||
playingQueue.add(position, song);
|
playingQueue.add(position, song);
|
||||||
originalPlayingQueue.add(position, song);
|
shuffledQueue.add(position, song);
|
||||||
|
|
||||||
|
resetCurrentSong = false;
|
||||||
callbacks.onQueueChanged();
|
callbacks.onQueueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSong(Song song) {
|
public void addSong(Song song) {
|
||||||
playingQueue.add(song);
|
playingQueue.add(song);
|
||||||
originalPlayingQueue.add(song);
|
shuffledQueue.add(song);
|
||||||
|
|
||||||
|
resetCurrentSong = false;
|
||||||
callbacks.onQueueChanged();
|
callbacks.onQueueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSongs(int position, List<Song> songs) {
|
public void addSongs(int position, List<Song> songs) {
|
||||||
playingQueue.addAll(position, songs);
|
playingQueue.addAll(position, songs);
|
||||||
originalPlayingQueue.addAll(position, songs);
|
shuffledQueue.addAll(position, songs);
|
||||||
|
|
||||||
|
resetCurrentSong = false;
|
||||||
callbacks.onQueueChanged();
|
callbacks.onQueueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSongs(List<Song> songs) {
|
public void addSongs(List<Song> songs) {
|
||||||
playingQueue.addAll(songs);
|
playingQueue.addAll(songs);
|
||||||
originalPlayingQueue.addAll(songs);
|
shuffledQueue.addAll(songs);
|
||||||
|
|
||||||
|
resetCurrentSong = false;
|
||||||
callbacks.onQueueChanged();
|
callbacks.onQueueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeSong(int position) {
|
public void removeSong(int position) {
|
||||||
if (getShuffleMode() == SHUFFLE_MODE_NONE) {
|
if (getShuffleMode() == SHUFFLE_MODE_NONE) {
|
||||||
playingQueue.remove(position);
|
playingQueue.remove(position);
|
||||||
originalPlayingQueue.remove(position);
|
shuffledQueue.remove(position);
|
||||||
} else {
|
} else {
|
||||||
originalPlayingQueue.remove(playingQueue.remove(position));
|
playingQueue.remove(shuffledQueue.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();
|
int currentPosition = getPosition();
|
||||||
if (deletedPosition < currentPosition) {
|
if (position != currentPosition) {
|
||||||
position = currentPosition - 1;
|
resetCurrentSong = false;
|
||||||
} else if (deletedPosition == currentPosition) {
|
|
||||||
if (playingQueue.size() > deletedPosition) {
|
|
||||||
MusicPlayerRemote.playSongAt(position);
|
|
||||||
} else {
|
|
||||||
MusicPlayerRemote.playSongAt(position - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (position < currentPosition) {
|
||||||
|
this.position = currentPosition - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callbacks.onQueueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void moveSong(int from, int to) {
|
public void moveSong(int from, int to) {
|
||||||
if (from == to) return;
|
if (from == to) return;
|
||||||
|
|
||||||
final int currentPosition = getPosition();
|
final int currentPosition = getPosition();
|
||||||
Song songToMove = playingQueue.remove(from);
|
Song songToMove = getPlayingQueue().remove(from);
|
||||||
playingQueue.add(to, songToMove);
|
getPlayingQueue().add(to, songToMove);
|
||||||
if (getShuffleMode() == SHUFFLE_MODE_NONE) {
|
|
||||||
Song tmpSong = originalPlayingQueue.remove(from);
|
|
||||||
originalPlayingQueue.add(to, tmpSong);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (from > currentPosition && to <= currentPosition) {
|
if (from > currentPosition && to <= currentPosition) {
|
||||||
position = currentPosition + 1;
|
position = currentPosition + 1;
|
||||||
|
|
@ -240,14 +242,15 @@ public class QueueManager {
|
||||||
position = to;
|
position = to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetCurrentSong = false;
|
||||||
callbacks.onQueueChanged();
|
callbacks.onQueueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearQueue() {
|
public void clearQueue() {
|
||||||
playingQueue.clear();
|
playingQueue.clear();
|
||||||
originalPlayingQueue.clear();
|
shuffledQueue.clear();
|
||||||
|
|
||||||
position = -1;
|
position = 0;
|
||||||
callbacks.onQueueChanged();
|
callbacks.onQueueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,14 +277,38 @@ public class QueueManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveQueue() {
|
public void restoreQueue() {
|
||||||
// copy queues by value to avoid concurrent modification exceptions from database
|
position = PreferenceUtil.getInstance(context).getPosition();
|
||||||
App.getDatabase().songDao().deleteSongs();
|
restoredProgress = PreferenceUtil.getInstance(context).getProgress();
|
||||||
App.getDatabase().songDao().insertSongs(new ArrayList<>(playingQueue));
|
|
||||||
|
|
||||||
App.getDatabase().queueSongDao().deleteQueueSongs();
|
playingQueue = new ArrayList<>(App.getDatabase().queueSongDao().getQueue(0));
|
||||||
App.getDatabase().queueSongDao().setQueue(new ArrayList<>(playingQueue), 0);
|
shuffledQueue = new ArrayList<>(App.getDatabase().queueSongDao().getQueue(1));
|
||||||
App.getDatabase().queueSongDao().setQueue(new ArrayList<>(originalPlayingQueue), 1);
|
|
||||||
|
shuffleMode = PreferenceUtil.getInstance(context).getShuffle();
|
||||||
|
repeatMode = PreferenceUtil.getInstance(context).getRepeat();
|
||||||
|
|
||||||
|
callbacks.onQueueChanged();
|
||||||
|
callbacks.onRepeatModeChanged();
|
||||||
|
callbacks.onShuffleModeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveQueue() {
|
||||||
|
PreferenceUtil.getInstance(context).setPosition(position);
|
||||||
|
App.getDatabase().queueSongDao().updateQueues(playingQueue, shuffledQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRestoredProgress() {
|
||||||
|
int progress = restoredProgress;
|
||||||
|
restoredProgress = 0;
|
||||||
|
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isResetCurrentSong() {
|
||||||
|
boolean reset = resetCurrentSong;
|
||||||
|
resetCurrentSong = true;
|
||||||
|
|
||||||
|
return reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface QueueCallbacks {
|
interface QueueCallbacks {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class LocalPlayer implements Playback {
|
public class LocalPlayer implements Playback {
|
||||||
|
|
@ -41,6 +43,8 @@ public class LocalPlayer implements Playback {
|
||||||
|
|
||||||
private PlaybackCallbacks callbacks;
|
private PlaybackCallbacks callbacks;
|
||||||
|
|
||||||
|
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
private final EventListener eventListener = new EventListener() {
|
private final EventListener eventListener = new EventListener() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -64,14 +68,7 @@ public class LocalPlayer implements Playback {
|
||||||
@Override
|
@Override
|
||||||
public void onMediaItemTransition(MediaItem mediaItem, int reason) {
|
public void onMediaItemTransition(MediaItem mediaItem, int reason) {
|
||||||
Log.i(TAG, String.format("onMediaItemTransition: %s %d", mediaItem, reason));
|
Log.i(TAG, String.format("onMediaItemTransition: %s %d", mediaItem, reason));
|
||||||
|
if (callbacks != null) callbacks.onTrackChanged(reason);
|
||||||
if (exoPlayer.getMediaItemCount() > 1) {
|
|
||||||
exoPlayer.removeMediaItem(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callbacks != null) {
|
|
||||||
callbacks.onTrackChanged(reason);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -115,28 +112,33 @@ public class LocalPlayer implements Playback {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDataSource(Song song) {
|
public void setQueue(List<Song> queue, int position, int progress, boolean resetCurrentSong) {
|
||||||
MediaItem mediaItem = exoPlayer.getCurrentMediaItem();
|
executorService.submit(() -> {
|
||||||
|
List<MediaItem> mediaItems = createMediaItems(queue);
|
||||||
|
|
||||||
if (mediaItem != null && mediaItem.mediaId.equals(song.id)) {
|
// TODO: Call this on main thread
|
||||||
|
if (resetCurrentSong) {
|
||||||
|
exoPlayer.setMediaItems(mediaItems, position, progress);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
exoPlayer.clearMediaItems();
|
int currentPosition = exoPlayer.getCurrentWindowIndex();
|
||||||
appendDataSource(song);
|
exoPlayer.removeMediaItems(0, currentPosition);
|
||||||
exoPlayer.seekTo(0, 0);
|
|
||||||
|
if (exoPlayer.getMediaItemCount() > 1) {
|
||||||
|
exoPlayer.removeMediaItems(1, exoPlayer.getMediaItemCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (position + 1 < mediaItems.size()) {
|
||||||
public void queueDataSource(Song song) {
|
exoPlayer.addMediaItems(1, mediaItems.subList(position + 1, mediaItems.size()));
|
||||||
while (exoPlayer.getMediaItemCount() > 1) {
|
|
||||||
exoPlayer.removeMediaItem(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
appendDataSource(song);
|
exoPlayer.addMediaItems(0, mediaItems.subList(0, position));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendDataSource(Song song) {
|
private List<MediaItem> createMediaItems(List<Song> queue) {
|
||||||
|
return queue.stream().map(song -> {
|
||||||
File audio = new File(MusicUtil.getFileUri(song));
|
File audio = new File(MusicUtil.getFileUri(song));
|
||||||
Uri uri = Uri.fromFile(audio);
|
Uri uri = Uri.fromFile(audio);
|
||||||
|
|
||||||
|
|
@ -147,21 +149,14 @@ public class LocalPlayer implements Playback {
|
||||||
List<String> containers = PreferenceUtil.getInstance(context).getDirectPlayCodecs().stream()
|
List<String> containers = PreferenceUtil.getInstance(context).getDirectPlayCodecs().stream()
|
||||||
.map(codec -> codec.container.toLowerCase(Locale.ROOT))
|
.map(codec -> codec.container.toLowerCase(Locale.ROOT))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
List<String> codecs = PreferenceUtil.getInstance(context).getDirectPlayCodecs().stream()
|
List<String> codecs = PreferenceUtil.getInstance(context).getDirectPlayCodecs().stream()
|
||||||
.map(codec -> codec.codec.toLowerCase(Locale.ROOT))
|
.map(codec -> codec.codec.toLowerCase(Locale.ROOT))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
String maxBitrate = PreferenceUtil.getInstance(context).getMaximumBitrate();
|
String maxBitrate = PreferenceUtil.getInstance(context).getMaximumBitrate();
|
||||||
|
|
||||||
MediaItem mediaItem;
|
MediaItem mediaItem;
|
||||||
|
|
||||||
boolean shouldDirectPlay =
|
if (uri.toString().contains("file://") || (containers.contains(song.container.toLowerCase(Locale.ROOT)) && codecs.contains(song.codec.toLowerCase(Locale.ROOT)) && song.bitRate <= Integer.parseInt(maxBitrate))) {
|
||||||
containers.contains(song.container.toLowerCase(Locale.ROOT)) &&
|
|
||||||
codecs.contains(song.codec.toLowerCase(Locale.ROOT)) &&
|
|
||||||
song.bitRate <= Integer.parseInt(maxBitrate);
|
|
||||||
|
|
||||||
if (uri.toString().contains("file://") || shouldDirectPlay || !song.supportsTranscoding) {
|
|
||||||
mediaItem = new MediaItem.Builder()
|
mediaItem = new MediaItem.Builder()
|
||||||
.setUri(uri)
|
.setUri(uri)
|
||||||
.setMediaId(song.id)
|
.setMediaId(song.id)
|
||||||
|
|
@ -174,7 +169,15 @@ public class LocalPlayer implements Playback {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
exoPlayer.addMediaItem(mediaItem);
|
return mediaItem;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playSongAt(int position) {
|
||||||
|
if (exoPlayer.getMediaItemCount() > 0) {
|
||||||
|
exoPlayer.seekTo(Math.max(0, position) % exoPlayer.getMediaItemCount(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataSource.Factory buildDataSourceFactory() {
|
private DataSource.Factory buildDataSourceFactory() {
|
||||||
|
|
@ -230,6 +233,21 @@ public class LocalPlayer implements Playback {
|
||||||
exoPlayer.release();
|
exoPlayer.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void previous() {
|
||||||
|
exoPlayer.previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void next() {
|
||||||
|
exoPlayer.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRepeatMode(@Player.RepeatMode int repeatMode) {
|
||||||
|
exoPlayer.setRepeatMode(repeatMode);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getProgress() {
|
public int getProgress() {
|
||||||
return (int) exoPlayer.getCurrentPosition();
|
return (int) exoPlayer.getCurrentPosition();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package com.dkanada.gramophone.service.playback;
|
package com.dkanada.gramophone.service.playback;
|
||||||
|
|
||||||
import com.dkanada.gramophone.model.Song;
|
import com.dkanada.gramophone.model.Song;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface Playback {
|
public interface Playback {
|
||||||
void setDataSource(Song song);
|
void setQueue(List<Song> queue, int position, int progress, boolean resetCurrentSong);
|
||||||
|
|
||||||
void queueDataSource(Song song);
|
void playSongAt(int position);
|
||||||
|
|
||||||
void setCallbacks(PlaybackCallbacks callbacks);
|
void setCallbacks(PlaybackCallbacks callbacks);
|
||||||
|
|
||||||
|
|
@ -21,6 +24,12 @@ public interface Playback {
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
void previous();
|
||||||
|
|
||||||
|
void next();
|
||||||
|
|
||||||
|
void setRepeatMode(@Player.RepeatMode int repeatMode);
|
||||||
|
|
||||||
int getProgress();
|
int getProgress();
|
||||||
|
|
||||||
int getDuration();
|
int getDuration();
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ public final class PreferenceUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPosition() {
|
public int getPosition() {
|
||||||
return mPreferences.getInt(POSITION, -1);
|
return mPreferences.getInt(POSITION, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPosition(int position) {
|
public void setPosition(int position) {
|
||||||
|
|
@ -178,7 +178,7 @@ public final class PreferenceUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getProgress() {
|
public int getProgress() {
|
||||||
return mPreferences.getInt(PROGRESS, -1);
|
return mPreferences.getInt(PROGRESS, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProgress(int progress) {
|
public void setProgress(int progress) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue