Rewrite of shuffle function

- shuffle function should work now
This commit is contained in:
Karim Abou Zeid 2015-01-25 21:36:50 +01:00
commit ecbf5fc292
8 changed files with 134 additions and 152 deletions

View file

@ -30,6 +30,7 @@ public class MusicPlayerRemote implements OnMusicRemoteEventListener {
private int position = -1; private int position = -1;
private List<Song> playingQueue; private List<Song> playingQueue;
private List<Song> restoredOriginalQueue;
private List<OnMusicRemoteEventListener> onMusicRemoteEventListeners; private List<OnMusicRemoteEventListener> onMusicRemoteEventListeners;
private MusicService musicService; private MusicService musicService;
@ -39,6 +40,7 @@ public class MusicPlayerRemote implements OnMusicRemoteEventListener {
public MusicPlayerRemote(Context context) { public MusicPlayerRemote(Context context) {
app = (App) context.getApplicationContext(); app = (App) context.getApplicationContext();
playingQueue = new ArrayList<>(); playingQueue = new ArrayList<>();
restoredOriginalQueue = new ArrayList<>();
onMusicRemoteEventListeners = new ArrayList<>(); onMusicRemoteEventListeners = new ArrayList<>();
startAndBindService(); startAndBindService();
} }
@ -56,10 +58,9 @@ public class MusicPlayerRemote implements OnMusicRemoteEventListener {
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service; MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
musicService = binder.getService(); musicService = binder.getService();
musicService.setPosition(position);
musicBound = true; musicBound = true;
musicService.restorePreviousState(restoredOriginalQueue, playingQueue, position);
musicService.addOnMusicRemoteEventListener(MusicPlayerRemote.this); musicService.addOnMusicRemoteEventListener(MusicPlayerRemote.this);
setPlayingQueue(playingQueue);
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.SERVICE_CONNECTED); notifyOnMusicRemoteEventListeners(MusicRemoteEvent.SERVICE_CONNECTED);
} }
@ -70,16 +71,9 @@ public class MusicPlayerRemote implements OnMusicRemoteEventListener {
} }
}; };
public boolean playSongAt(int position) { public boolean playSongAt(final int position) {
if (musicBound) { if (musicBound) {
if (position < getPlayingQueue().size() && position >= 0) { musicService.playSongAt(position);
this.position = position;
musicService.setPosition(position);
musicService.playSong();
return true;
} else {
Log.e(TAG, "No song in queue at given index!");
}
} }
return false; return false;
} }
@ -139,10 +133,17 @@ public class MusicPlayerRemote implements OnMusicRemoteEventListener {
return position; return position;
} }
public void setPlayingQueue(List<Song> songs) { private void setPosition(int position) {
playingQueue = songs; this.position = position;
if (musicBound) { if (musicBound) {
musicService.setPlayingQueue(playingQueue); musicService.setPosition(position);
}
}
public void openQueue(final List<Song> playingQueue, final int startPosition, final boolean startPlaying) {
this.playingQueue = playingQueue;
if (musicBound) {
musicService.openQueue(this.playingQueue, startPosition, startPlaying);
} }
} }
@ -254,12 +255,17 @@ public class MusicPlayerRemote implements OnMusicRemoteEventListener {
public void restorePreviousState() { public void restorePreviousState() {
try { try {
List restoredQueue = (ArrayList<Song>) InternalStorageUtil.readObject(app, AppKeys.IS_PLAYING_QUEUE); List restoredQueue = (ArrayList<Song>) InternalStorageUtil.readObject(app, AppKeys.IS_PLAYING_QUEUE);
List restoredOriginalQueue = (ArrayList<Song>) InternalStorageUtil.readObject(app, AppKeys.IS_ORIGINAL_PLAYING_QUEUE);
int restoredPosition = (int) InternalStorageUtil.readObject(app, AppKeys.IS_POSITION_IN_QUEUE); int restoredPosition = (int) InternalStorageUtil.readObject(app, AppKeys.IS_POSITION_IN_QUEUE);
setPlayingQueue(restoredQueue);
position = restoredPosition;
if (musicBound) { if (musicBound) {
musicService.setPosition(restoredPosition); musicService.restorePreviousState(restoredOriginalQueue, restoredQueue, restoredPosition);
} }
playingQueue = restoredQueue;
this.restoredOriginalQueue = restoredOriginalQueue;
position = restoredPosition;
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.STATE_RESTORED); notifyOnMusicRemoteEventListeners(MusicRemoteEvent.STATE_RESTORED);
Log.i(TAG, "restored last state"); Log.i(TAG, "restored last state");
} catch (IOException | ClassNotFoundException | ClassCastException e) { } catch (IOException | ClassNotFoundException | ClassCastException e) {

View file

@ -0,0 +1,21 @@
package com.kabouzeid.materialmusic.helper;
import com.kabouzeid.materialmusic.model.Song;
import java.util.Collections;
import java.util.List;
/**
* Created by karim on 24.01.15.
*/
public class ShuffleHelper {
public static void makeShuffleList(List<Song> listToShuffle, final int current) {
if (current >= 0) {
Song song = listToShuffle.remove(current);
Collections.shuffle(listToShuffle);
listToShuffle.add(0, song);
} else {
Collections.shuffle(listToShuffle);
}
}
}

View file

@ -1,52 +0,0 @@
package com.kabouzeid.materialmusic.helper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by karim on 24.01.15.
*/
public class Shuffler {
private static int MAX_HISTORY_SIZE = 250;
private List<Integer> order = new ArrayList<>();
private int position;
private int interval;
public Shuffler(final int interval) {
order = getShuffledOrderList(interval);
this.interval = interval;
}
public int nextInt(boolean infinite) {
position = position + 1;
if (position > order.size() - 1) {
if (infinite) {
order.addAll(getShuffledOrderList(interval));
if (order.size() > Math.max(interval, MAX_HISTORY_SIZE)) {
order = order.subList(order.size() / 2 - 1, order.size() - 1);
}
} else {
return order.get(order.size() - 1);
}
}
return order.get(position);
}
public int previousInt() {
position = position - 1;
if (position < 0) {
position = 0;
}
return order.get(position);
}
private List<Integer> getShuffledOrderList(int interval) {
final List<Integer> newList = new ArrayList<>();
for (int i = 0; i < interval; i++) {
newList.add(i);
}
Collections.shuffle(newList);
return newList;
}
}

View file

@ -11,6 +11,7 @@ public final class AppKeys {
public static final String SP_SHUFFLE_MODE = "com.kabouzeid.materialmusic.SHUFFLE_MODE"; public static final String SP_SHUFFLE_MODE = "com.kabouzeid.materialmusic.SHUFFLE_MODE";
public static final String SP_REPEAT_MODE = "com.kabouzeid.materialmusic.REPEAT_MODE"; public static final String SP_REPEAT_MODE = "com.kabouzeid.materialmusic.REPEAT_MODE";
public static final String IS_ORIGINAL_PLAYING_QUEUE = "com.kabouzeid.materialmusic.ORIGINAL_PLAYING_QUEUE";
public static final String IS_PLAYING_QUEUE = "com.kabouzeid.materialmusic.PLAYING_QUEUE"; public static final String IS_PLAYING_QUEUE = "com.kabouzeid.materialmusic.PLAYING_QUEUE";
public static final String IS_POSITION_IN_QUEUE = "com.kabouzeid.materialmusic.POSITION_IN_QUEUE"; public static final String IS_POSITION_IN_QUEUE = "com.kabouzeid.materialmusic.POSITION_IN_QUEUE";
public static final String IS_ARTIST_JSON_INFO_CACHE = "com.kabouzeid.materialmusic.ARTIST_JSON_INFO_CACHE"; public static final String IS_ARTIST_JSON_INFO_CACHE = "com.kabouzeid.materialmusic.ARTIST_JSON_INFO_CACHE";

View file

@ -23,7 +23,7 @@ import android.widget.Toast;
import com.kabouzeid.materialmusic.R; import com.kabouzeid.materialmusic.R;
import com.kabouzeid.materialmusic.helper.NotificationHelper; import com.kabouzeid.materialmusic.helper.NotificationHelper;
import com.kabouzeid.materialmusic.helper.Shuffler; import com.kabouzeid.materialmusic.helper.ShuffleHelper;
import com.kabouzeid.materialmusic.interfaces.OnMusicRemoteEventListener; import com.kabouzeid.materialmusic.interfaces.OnMusicRemoteEventListener;
import com.kabouzeid.materialmusic.misc.AppKeys; import com.kabouzeid.materialmusic.misc.AppKeys;
import com.kabouzeid.materialmusic.model.MusicRemoteEvent; import com.kabouzeid.materialmusic.model.MusicRemoteEvent;
@ -34,7 +34,6 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; import java.util.List;
public class MusicService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, AudioManager.OnAudioFocusChangeListener { public class MusicService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, AudioManager.OnAudioFocusChangeListener {
@ -57,7 +56,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
private MediaPlayer player; private MediaPlayer player;
private List<Song> playingQueue; private List<Song> playingQueue;
private LinkedList<Song> playingHistory; private List<Song> originalPlayingQueue;
private List<OnMusicRemoteEventListener> onMusicRemoteEventListeners; private List<OnMusicRemoteEventListener> onMusicRemoteEventListeners;
private int currentSongId = -1; private int currentSongId = -1;
private int position = -1; private int position = -1;
@ -70,7 +69,6 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
private NotificationHelper notificationHelper; private NotificationHelper notificationHelper;
private AudioManager audioManager; private AudioManager audioManager;
private RemoteControlClient remoteControlClient; private RemoteControlClient remoteControlClient;
private Shuffler shuffler;
private final BroadcastReceiver receiver = new BroadcastReceiver() { private final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override @Override
@ -96,7 +94,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
super.onCreate(); super.onCreate();
isPlayerPrepared = false; isPlayerPrepared = false;
playingQueue = new ArrayList<>(); playingQueue = new ArrayList<>();
playingHistory = new LinkedList<>(); originalPlayingQueue = new ArrayList<>();
onMusicRemoteEventListeners = new ArrayList<>(); onMusicRemoteEventListeners = new ArrayList<>();
notificationHelper = new NotificationHelper(this); notificationHelper = new NotificationHelper(this);
@ -106,13 +104,6 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
registerEverything(); registerEverything();
} }
private Shuffler getShuffler() {
if (shuffler == null) {
shuffler = new Shuffler(playingQueue.size());
}
return shuffler;
}
private boolean requestFocus() { private boolean requestFocus() {
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN); AudioManager.AUDIOFOCUS_GAIN);
@ -253,7 +244,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
private void killEverythingAndReleaseResources() { private void killEverythingAndReleaseResources() {
savePosition(); savePosition();
saveQueue(); saveQueues();
stopPlaying(); stopPlaying();
notificationHelper.killNotification(); notificationHelper.killNotification();
stopSelf(); stopSelf();
@ -276,12 +267,27 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
notificationHelper.buildNotification(playingQueue.get(position), isPlaying()); notificationHelper.buildNotification(playingQueue.get(position), isPlaying());
} }
public void setPlayingQueue(List<Song> songs) { public void openQueue(final List<Song> playingQueue, final int startPosition, final boolean startPlaying) {
if (!playingQueue.equals(songs)) { if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size()) {
this.playingQueue = songs; originalPlayingQueue = playingQueue;
shuffler = new Shuffler(playingQueue.size()); this.playingQueue = new ArrayList<>(originalPlayingQueue);
saveQueue(); setPosition(startPosition);
if (shuffleMode == SHUFFLE_MODE_SHUFFLE) {
ShuffleHelper.makeShuffleList(this.playingQueue, startPosition);
setPosition(0);
} }
if (startPlaying) {
playSong();
}
saveState();
}
}
public void restorePreviousState(final List<Song> originalPlayingQueue, final List<Song> playingQueue, int position) {
this.originalPlayingQueue = originalPlayingQueue;
this.playingQueue = playingQueue;
this.position = position;
saveState();
} }
public List<Song> getPlayingQueue() { public List<Song> getPlayingQueue() {
@ -309,9 +315,9 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
player.setVolume(1.0f, 1.0f); player.setVolume(1.0f, 1.0f);
if (wasPlayingBeforeFocusLoss) { if (wasPlayingBeforeFocusLoss) {
resumePlaying(); resumePlaying();
updateRemoteControlClient(getPlayingQueue().get(position)); updateRemoteControlClient(getPlayingQueue().get(getPosition()));
} }
updateRemoteControlClient(getPlayingQueue().get(position)); updateRemoteControlClient(getPlayingQueue().get(getPosition()));
break; break;
case AudioManager.AUDIOFOCUS_LOSS: case AudioManager.AUDIOFOCUS_LOSS:
@ -344,6 +350,15 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
} }
} }
public void playSongAt(final int position) {
if (position < getPlayingQueue().size() && position >= 0) {
setPosition(position);
playSong();
} else {
Log.e(TAG, "No song in queue at given index!");
}
}
public void playSong() { public void playSong() {
if (requestFocus()) { if (requestFocus()) {
setUpMediaPlayerIfNeeded(); setUpMediaPlayerIfNeeded();
@ -353,9 +368,9 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
Uri trackUri = getCurrentPositionTrackUri(); Uri trackUri = getCurrentPositionTrackUri();
try { try {
player.setDataSource(getApplicationContext(), trackUri); player.setDataSource(getApplicationContext(), trackUri);
currentSongId = playingQueue.get(position).id; currentSongId = getPlayingQueue().get(getPosition()).id;
updateNotification(); updateNotification();
updateRemoteControlClient(getPlayingQueue().get(position)); updateRemoteControlClient(getPlayingQueue().get(getPosition()));
player.prepareAsync(); player.prepareAsync();
} catch (Exception e) { } catch (Exception e) {
Log.e("MUSIC SERVICE", "Error setting data source", e); Log.e("MUSIC SERVICE", "Error setting data source", e);
@ -407,7 +422,6 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
public void playNextSong() { public void playNextSong() {
if (position != -1) { if (position != -1) {
if (isPlayerPrepared) { if (isPlayerPrepared) {
setPosition(getNextPosition()); setPosition(getNextPosition());
playSong(); playSong();
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.NEXT); notifyOnMusicRemoteEventListeners(MusicRemoteEvent.NEXT);
@ -437,31 +451,17 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
int position = 0; int position = 0;
switch (repeatMode) { switch (repeatMode) {
case REPEAT_MODE_NONE: case REPEAT_MODE_NONE:
switch (shuffleMode) {
case SHUFFLE_MODE_NONE:
position = getPosition() + 1; position = getPosition() + 1;
if (isLastTrack()) { if (isLastTrack()) {
position -= 1; position -= 1;
} }
break; break;
case SHUFFLE_MODE_SHUFFLE:
position = getShuffler().nextInt(false);
break;
}
break;
case REPEAT_MODE_ALL: case REPEAT_MODE_ALL:
switch (shuffleMode) {
case SHUFFLE_MODE_NONE:
position = getPosition() + 1; position = getPosition() + 1;
if (isLastTrack()) { if (isLastTrack()) {
position = 0; position = 0;
} }
break; break;
case SHUFFLE_MODE_SHUFFLE:
position = getShuffler().nextInt(true);
break;
}
break;
case REPEAT_MODE_THIS: case REPEAT_MODE_THIS:
position = getPosition(); position = getPosition();
break; break;
@ -473,31 +473,17 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
int position = 0; int position = 0;
switch (repeatMode) { switch (repeatMode) {
case REPEAT_MODE_NONE: case REPEAT_MODE_NONE:
switch (shuffleMode) {
case SHUFFLE_MODE_NONE:
position = getPosition() - 1; position = getPosition() - 1;
if (position < 0) { if (position < 0) {
position = 0; position = 0;
} }
break; break;
case SHUFFLE_MODE_SHUFFLE:
position = getShuffler().previousInt();
break;
}
break;
case REPEAT_MODE_ALL: case REPEAT_MODE_ALL:
switch (shuffleMode) {
case SHUFFLE_MODE_NONE:
position = getPosition() - 1; position = getPosition() - 1;
if (position < 0) { if (position < 0) {
position = getPlayingQueue().size() - 1; position = getPlayingQueue().size() - 1;
} }
break; break;
case SHUFFLE_MODE_SHUFFLE:
position = getShuffler().previousInt();
break;
}
break;
case REPEAT_MODE_THIS: case REPEAT_MODE_THIS:
position = getPosition(); position = getPosition();
break; break;
@ -526,10 +512,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
} }
public boolean isPlayerPrepared() { public boolean isPlayerPrepared() {
if (player == null) { return player != null && isPlayerPrepared;
return false;
}
return isPlayerPrepared;
} }
private boolean isLastTrack() { private boolean isLastTrack() {
@ -547,14 +530,25 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
onMusicRemoteEventListeners.add(onMusicRemoteEventListener); onMusicRemoteEventListeners.add(onMusicRemoteEventListener);
} }
public void saveQueue() { public void saveState() {
saveQueues();
savePosition();
}
public void saveQueues() {
new Thread(new Runnable() {
@Override
public void run() {
try { try {
InternalStorageUtil.writeObject(MusicService.this, AppKeys.IS_PLAYING_QUEUE, getPlayingQueue()); InternalStorageUtil.writeObject(MusicService.this, AppKeys.IS_PLAYING_QUEUE, playingQueue);
InternalStorageUtil.writeObject(MusicService.this, AppKeys.IS_ORIGINAL_PLAYING_QUEUE, originalPlayingQueue);
Log.i(TAG, "saved current queue state"); Log.i(TAG, "saved current queue state");
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "error while saving music service queue state", e); Log.e(TAG, "error while saving music service queue state", e);
} }
} }
}).start();
}
public void savePosition() { public void savePosition() {
new Thread(new Runnable() { new Thread(new Runnable() {
@ -573,12 +567,27 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
public void setShuffleMode(final int shuffleMode) { public void setShuffleMode(final int shuffleMode) {
switch (shuffleMode) { switch (shuffleMode) {
case SHUFFLE_MODE_SHUFFLE: case SHUFFLE_MODE_SHUFFLE:
shuffler = new Shuffler(getPlayingQueue().size()); this.shuffleMode = shuffleMode;
PreferenceManager.getDefaultSharedPreferences(this).edit()
.putInt(AppKeys.SP_SHUFFLE_MODE, shuffleMode)
.apply();
ShuffleHelper.makeShuffleList(this.playingQueue, getPosition());
setPosition(0);
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.SHUFFLE_MODE_CHANGED);
break;
case SHUFFLE_MODE_NONE: case SHUFFLE_MODE_NONE:
this.shuffleMode = shuffleMode; this.shuffleMode = shuffleMode;
PreferenceManager.getDefaultSharedPreferences(this).edit() PreferenceManager.getDefaultSharedPreferences(this).edit()
.putInt(AppKeys.SP_SHUFFLE_MODE, shuffleMode) .putInt(AppKeys.SP_SHUFFLE_MODE, shuffleMode)
.apply(); .apply();
playingQueue = new ArrayList<>(originalPlayingQueue);
int newPosition = 0;
for (Song song : playingQueue) {
if (song.id == currentSongId) {
newPosition = playingQueue.indexOf(song);
}
}
setPosition(newPosition);
notifyOnMusicRemoteEventListeners(MusicRemoteEvent.SHUFFLE_MODE_CHANGED); notifyOnMusicRemoteEventListeners(MusicRemoteEvent.SHUFFLE_MODE_CHANGED);
break; break;
} }
@ -599,7 +608,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
} }
public void cycleRepeatMode() { public void cycleRepeatMode() {
switch (repeatMode) { switch (getRepeatMode()) {
case REPEAT_MODE_NONE: case REPEAT_MODE_NONE:
setRepeatMode(REPEAT_MODE_ALL); setRepeatMode(REPEAT_MODE_ALL);
break; break;
@ -613,7 +622,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
} }
public void toggleShuffle() { public void toggleShuffle() {
if (shuffleMode == SHUFFLE_MODE_NONE) { if (getShuffleMode() == SHUFFLE_MODE_NONE) {
setShuffleMode(SHUFFLE_MODE_SHUFFLE); setShuffleMode(SHUFFLE_MODE_SHUFFLE);
} else { } else {
setShuffleMode(SHUFFLE_MODE_NONE); setShuffleMode(SHUFFLE_MODE_NONE);

View file

@ -236,8 +236,7 @@ public class AlbumDetailActivity extends AbsFabActivity implements OnMusicRemote
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position > 0) { if (position > 0) {
app.getMusicPlayerRemote().setPlayingQueue(songs); app.getMusicPlayerRemote().openQueue(songs, position - 1, true);
app.getMusicPlayerRemote().playSongAt(position - 1);
} }
} }
}); });

View file

@ -29,8 +29,7 @@ public class ViewPagerTabArtistSongListFragment extends AbsViewPagerTabArtistLis
setOnItemClickListener(new AdapterView.OnItemClickListener() { setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
app.getMusicPlayerRemote().setPlayingQueue(songs); app.getMusicPlayerRemote().openQueue(songs, position, true);
app.getMusicPlayerRemote().playSongAt(position);
} }
}); });
return adapter; return adapter;

View file

@ -82,8 +82,7 @@ public class SongViewFragment extends Fragment implements KabSearchAbleFragment
absListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { absListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
app.getMusicPlayerRemote().setPlayingQueue(songs); app.getMusicPlayerRemote().openQueue(songs, position, true);
app.getMusicPlayerRemote().playSongAt(position);
} }
}); });