Allow a specific shuffle/repeat mode to be set rather than force cycling

This is just plumbing for a future UI
This commit is contained in:
Christopher Eby 2011-10-21 16:38:43 -05:00
parent 601074c9cc
commit 61bb610ce3
5 changed files with 105 additions and 90 deletions

View File

@ -29,6 +29,7 @@ THE SOFTWARE.
<string name="settings">Settings</string>
<string name="library">Library</string>
<string name="display_mode">Display Mode</string>
<string name="random_enabling">Random enabled</string>
<string name="song_load_failed">Failed to load song %s. It may be corrupt or missing.</string>
<string name="queue_cleared">Queue cleared.</string>

View File

@ -236,15 +236,21 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On
if ((state & PlaybackService.FLAG_PLAYING) != 0)
updateProgress();
if ((toggled & (PlaybackService.FLAG_REPEAT|PlaybackService.FLAG_REPEAT_CURRENT|PlaybackService.FLAG_RANDOM)) != 0) {
if ((state & PlaybackService.FLAG_REPEAT) != 0)
mEndButton.setImageResource(R.drawable.repeat_active);
else if ((state & PlaybackService.FLAG_REPEAT_CURRENT) != 0)
mEndButton.setImageResource(R.drawable.repeat_current_active);
else if ((state & PlaybackService.FLAG_RANDOM) != 0)
mEndButton.setImageResource(R.drawable.random_active);
else
if ((toggled & PlaybackService.MASK_FINISH) != 0) {
switch (PlaybackService.finishAction(state)) {
case SongTimeline.FINISH_STOP:
mEndButton.setImageResource(R.drawable.repeat_inactive);
break;
case SongTimeline.FINISH_REPEAT:
mEndButton.setImageResource(R.drawable.repeat_active);
break;
case SongTimeline.FINISH_REPEAT_CURRENT:
mEndButton.setImageResource(R.drawable.repeat_current_active);
break;
case SongTimeline.FINISH_RANDOM:
mEndButton.setImageResource(R.drawable.random_active);
break;
}
}
if ((toggled & PlaybackService.MASK_SHUFFLE) != 0) {
@ -455,10 +461,7 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On
{
switch (view.getId()) {
case R.id.end_action:
if ((mState & (PlaybackService.FLAG_REPEAT_CURRENT|PlaybackService.FLAG_RANDOM)) != 0)
toggleRandom();
else
cycleRepeat();
cycleFinishAction();
break;
case R.id.shuffle:
cycleShuffle();

View File

@ -54,7 +54,6 @@ public class PlaybackActivity extends Activity
public static final int ACTION_PREVIOUS_SONG = 4;
public static final int ACTION_REPEAT = 5;
public static final int ACTION_SHUFFLE = 6;
public static final int ACTION_RANDOM = 7;
public static final int ACTION_ENQUEUE_ALBUM = 8;
public static final int ACTION_ENQUEUE_ARTIST = 9;
public static final int ACTION_ENQUEUE_GENRE = 10;
@ -347,19 +346,11 @@ public class PlaybackActivity extends Activity
}
/**
* Cycle repeat mode.
* Cycle the finish action.
*/
public void cycleRepeat()
public void cycleFinishAction()
{
setState(PlaybackService.get(this).cycleRepeat());
}
/**
* Toggle random mode on/off
*/
public void toggleRandom()
{
setState(PlaybackService.get(this).toggleRandom());
setState(PlaybackService.get(this).cycleFinishAction());
}
/**
@ -393,14 +384,11 @@ public class PlaybackActivity extends Activity
previousSong();
break;
case ACTION_REPEAT:
cycleRepeat();
cycleFinishAction();
break;
case ACTION_SHUFFLE:
cycleShuffle();
break;
case ACTION_RANDOM:
toggleRandom();
break;
case ACTION_ENQUEUE_ALBUM:
enqueue(MediaUtils.TYPE_ALBUM);
break;

View File

@ -73,7 +73,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
/**
* State file version that indicates data order.
*/
private static final int STATE_VERSION = 3;
private static final int STATE_VERSION = 4;
private static final int NOTIFICATION_ID = 2;
@ -138,33 +138,40 @@ public final class PlaybackService extends Service implements Handler.Callback,
* Set when there is no media available on the device.
*/
public static final int FLAG_NO_MEDIA = 0x2;
/**
* If set, will loop back to the beginning of the timeline when its end is
* reached.
*/
public static final int FLAG_REPEAT = 0x8;
/**
* Set when the current song is unplayable.
*/
public static final int FLAG_ERROR = 0x10;
/**
* If set, random songs will be added to the timeline when its end is
* reached. Overrides FLAG_REPEAT.
*/
public static final int FLAG_RANDOM = 0x20;
public static final int FLAG_ERROR = 0x4;
/**
* Set when the user needs to select songs to play.
*/
public static final int FLAG_EMPTY_QUEUE = 0x40;
public static final int FLAG_EMPTY_QUEUE = 0x8;
public static final int SHIFT_FINISH = 4;
/**
* If set, replay the current song when the end of the song is reached
* instead of advancing to the next song.
* These two bits will be one of SongTimeline.FINISH_*.
*/
public static final int FLAG_REPEAT_CURRENT = 0x80;
public static final int MASK_FINISH = 0x3 << SHIFT_FINISH;
public static final int SHIFT_SHUFFLE = 6;
/**
* These two bits will be one of SongTimeline.SHUFFLE_*.
*/
public static final int MASK_SHUFFLE = 0x100 | 0x200;
public static final int MASK_SHUFFLE = 0x3 << SHIFT_SHUFFLE;
/**
* The PlaybackService state, indicating if the service is playing,
* repeating, etc.
*
* The format of this is 0b00000000_00000000_00000000_ffeedcba,
* where each bit is:
* a: {@link PlaybackService#FLAG_PLAYING}
* b: {@link PlaybackService#FLAG_NO_MEDIA}
* c: {@link PlaybackService#FLAG_ERROR}
* d: {@link PlaybackService#FLAG_EMPTY_QUEUE}
* ee: {@link PlaybackService#MASK_FINISH}
* ff: {@link PlaybackService#MASK_SHUFFLE}
*/
int mState;
private final Object mStateLock = new Object[0];
public static final int NEVER = 0;
public static final int WHEN_PLAYING = 1;
@ -235,10 +242,8 @@ public final class PlaybackService extends Service implements Handler.Callback,
private AudioManager mAudioManager;
SongTimeline mTimeline;
int mState;
private Song mCurrentSong;
private final Object mStateLock = new Object();
boolean mPlayingBeforeCall;
private int mPendingSeek;
public Receiver mReceiver;
@ -524,16 +529,8 @@ public final class PlaybackService extends Service implements Handler.Callback,
if ((toggled & MASK_SHUFFLE) != 0)
mTimeline.setShuffleMode(shuffleMode(state));
if ((toggled & (FLAG_REPEAT | FLAG_RANDOM)) != 0) {
int action;
if ((state & FLAG_RANDOM) != 0)
action = SongTimeline.FINISH_RANDOM;
else if ((state & FLAG_REPEAT) != 0)
action = SongTimeline.FINISH_REPEAT;
else
action = SongTimeline.FINISH_STOP;
mTimeline.setFinishAction(action);
}
if ((toggled & MASK_FINISH) != 0)
mTimeline.setFinishAction(finishAction(state));
}
private void broadcastChange(int state, Song song, long uptime)
@ -627,8 +624,9 @@ public final class PlaybackService extends Service implements Handler.Callback,
{
synchronized (mStateLock) {
if ((mState & FLAG_EMPTY_QUEUE) != 0) {
updateState((mState | FLAG_RANDOM) & ~FLAG_REPEAT);
setFinishAction(SongTimeline.FINISH_RANDOM);
setCurrentSong(0);
Toast.makeText(this, R.string.random_enabling, Toast.LENGTH_SHORT).show();
}
int state = updateState(mState | FLAG_PLAYING);
@ -666,15 +664,17 @@ public final class PlaybackService extends Service implements Handler.Callback,
}
}
/**
* Toggle random mode. Disables repeat mode.
* Change the end action (e.g. repeat, random).
*
* @param action The new action. One of SongTimeline.FINISH_*.
* @return The new state after this is called.
*/
public int toggleRandom()
public int setFinishAction(int action)
{
synchronized (mStateLock) {
return updateState((mState ^ FLAG_RANDOM) & ~(FLAG_REPEAT|FLAG_REPEAT_CURRENT));
return updateState((mState & ~MASK_FINISH) | (action << SHIFT_FINISH));
}
}
@ -683,17 +683,26 @@ public final class PlaybackService extends Service implements Handler.Callback,
*
* @return The new state after this is called.
*/
public int cycleRepeat()
public int cycleFinishAction()
{
synchronized (mStateLock) {
int state = mState & ~FLAG_RANDOM;
if ((state & FLAG_REPEAT_CURRENT) != 0)
state &= ~(FLAG_REPEAT|FLAG_REPEAT_CURRENT);
else if ((state & FLAG_REPEAT) == 0)
state |= FLAG_REPEAT;
else if ((state & FLAG_REPEAT) != 0)
state = (state | FLAG_REPEAT_CURRENT) & ~FLAG_REPEAT;
return updateState(state);
int mode = finishAction(mState) + 1;
if (mode > SongTimeline.FINISH_RANDOM)
mode = SongTimeline.FINISH_STOP;
return setFinishAction(mode);
}
}
/**
* Change the shuffle mode.
*
* @param mode The new mode. One of SongTimeline.SHUFFLE_*.
* @return The new state after this is called.
*/
public int setShuffleMode(int mode)
{
synchronized (mStateLock) {
return updateState((mState & ~MASK_SHUFFLE) | (mode << SHIFT_SHUFFLE));
}
}
@ -705,10 +714,10 @@ public final class PlaybackService extends Service implements Handler.Callback,
public int cycleShuffle()
{
synchronized (mStateLock) {
int state = mState;
int step = (state & MASK_SHUFFLE) == 0x200 ? 0x200 : 0x100;
state = (state & ~MASK_SHUFFLE) | ((state + step) & MASK_SHUFFLE);
return updateState(state);
int mode = shuffleMode(mState) + 1;
if (mode > SongTimeline.SHUFFLE_ALBUMS)
mode = SongTimeline.SHUFFLE_NONE;
return setShuffleMode(mode);
}
}
@ -729,7 +738,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
mCurrentSong = song;
if (song == null || song.id == -1 || song.path == null) {
if (MediaUtils.isSongAvailable(getContentResolver())) {
int flag = (mState & FLAG_RANDOM) == 0 ? FLAG_EMPTY_QUEUE : FLAG_ERROR;
int flag = finishAction(mState) == SongTimeline.FINISH_RANDOM ? FLAG_ERROR : FLAG_EMPTY_QUEUE;
synchronized (mStateLock) {
updateState((mState | flag) & ~FLAG_NO_MEDIA);
}
@ -790,10 +799,10 @@ public final class PlaybackService extends Service implements Handler.Callback,
@Override
public void onCompletion(MediaPlayer player)
{
if (mTimeline.isEndOfQueue())
unsetFlag(FLAG_PLAYING);
else if ((mState & FLAG_REPEAT_CURRENT) != 0)
if (finishAction(mState) == SongTimeline.FINISH_REPEAT_CURRENT)
setCurrentSong(0);
else if (mTimeline.isEndOfQueue())
unsetFlag(FLAG_PLAYING);
else
setCurrentSong(+1);
}
@ -1284,16 +1293,9 @@ public final class PlaybackService extends Service implements Handler.Callback,
if (in.readLong() == STATE_FILE_MAGIC && in.readInt() == STATE_VERSION) {
mPendingSeek = in.readInt();
int savedState = in.readInt();
mTimeline.readState(in);
int finishAction = mTimeline.getFinishAction();
if (finishAction == SongTimeline.FINISH_RANDOM)
state |= FLAG_RANDOM;
else if (finishAction == SongTimeline.FINISH_REPEAT)
state |= FLAG_REPEAT;
state |= mTimeline.getShuffleMode() << 8;
state |= savedState & FLAG_REPEAT_CURRENT;
state |= mTimeline.getShuffleMode() << SHIFT_SHUFFLE;
state |= mTimeline.getFinishAction() << SHIFT_FINISH;
}
in.close();
@ -1319,7 +1321,6 @@ public final class PlaybackService extends Service implements Handler.Callback,
out.writeLong(STATE_FILE_MAGIC);
out.writeInt(STATE_VERSION);
out.writeInt(pendingSeek);
out.writeInt(mState);
mTimeline.writeState(out);
out.close();
} catch (IOException e) {
@ -1330,11 +1331,23 @@ public final class PlaybackService extends Service implements Handler.Callback,
/**
* Returns the shuffle mode for the given state.
*
* @param state The PlaybackService state to process.
* @return The shuffle mode. One of SongTimeline.SHUFFLE_*.
*/
public static int shuffleMode(int state)
{
return (state & MASK_SHUFFLE) >> 8;
return (state & MASK_SHUFFLE) >> SHIFT_SHUFFLE;
}
/**
* Returns the finish action for the given state.
*
* @param state The PlaybackService state to process.
* @return The finish action. One of SongTimeline.FINISH_*.
*/
public static int finishAction(int state)
{
return (state & MASK_FINISH) >> SHIFT_FINISH;
}
/**

View File

@ -55,12 +55,21 @@ public final class SongTimeline {
* @see SongTimeline#setFinishAction(int)
*/
public static final int FINISH_REPEAT = 1;
/**
* Repeat the current song. This behavior is implemented entirely in
* {@link PlaybackService#onCompletion(android.media.MediaPlayer)};
* pressing the next or previous buttons will advance the song as normal;
* only allowing the song to play until the end will repeat it.
*
* @see SongTimeline#setFinishAction(int)
*/
public static final int FINISH_REPEAT_CURRENT = 2;
/**
* Add random songs to the playlist.
*
* @see SongTimeline#setFinishAction(int)
*/
public static final int FINISH_RANDOM = 2;
public static final int FINISH_RANDOM = 3;
/**
* Clear the timeline and use only the provided songs.
@ -397,6 +406,7 @@ public final class SongTimeline {
switch (mFinishAction) {
case FINISH_STOP:
case FINISH_REPEAT:
case FINISH_REPEAT_CURRENT:
if (size == 0)
// empty queue
return null;