Add play all and enqueue all library actions

Similar to the play all and enqueue all headers, but plays selected row
first. This is similar to the default behavior of most music player's libraries
This commit is contained in:
Christopher Eby 2011-10-26 03:09:20 -05:00
parent bc1f587730
commit d43dcc14ae
6 changed files with 167 additions and 39 deletions

View File

@ -42,6 +42,7 @@ THE SOFTWARE.
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</string-array>
<string-array name="swipe_action_values">
<!-- This must match the order of swipe_action_entries exactly and
@ -78,6 +79,8 @@ THE SOFTWARE.
<item>@string/play</item>
<item>@string/enqueue</item>
<item>@string/last_used_action</item>
<item>@string/play_all</item>
<item>@string/enqueue_all</item>
</string-array>
<string-array name="notification_mode_entries">
<item>@string/never_show</item>

View File

@ -68,8 +68,11 @@ public class LibraryActivity
private static final int ACTION_PLAY = 0;
private static final int ACTION_ENQUEUE = 1;
private static final int ACTION_LAST_USED = 2;
private static final int ACTION_PLAY_ALL = 3;
private static final int ACTION_ENQUEUE_ALL = 4;
private static final int[] modeForAction =
{ SongTimeline.MODE_PLAY, SongTimeline.MODE_ENQUEUE };
{ SongTimeline.MODE_PLAY, SongTimeline.MODE_ENQUEUE, -1,
SongTimeline.MODE_PLAY_ID_FIRST, SongTimeline.MODE_ENQUEUE_ID_FIRST };
private TabHost mTabHost;
@ -248,7 +251,7 @@ public class LibraryActivity
if (action == ACTION_LAST_USED)
action = mLastAction;
int res = action == ACTION_ENQUEUE ? R.string.enqueue_all : R.string.play_all;
int res = action == ACTION_ENQUEUE || action == ACTION_ENQUEUE_ALL ? R.string.enqueue_all : R.string.play_all;
String text = getString(res);
mArtistAdapter.setHeaderText(text);
mAlbumAdapter.setHeaderText(text);
@ -267,11 +270,28 @@ public class LibraryActivity
if (action == ACTION_LAST_USED)
action = mLastAction;
int mode = modeForAction[action];
QueryTask query = buildQueryFromIntent(intent, false);
PlaybackService.get(this).addSongs(mode, query, 0);
long id = intent.getLongExtra("id", -1);
mLastActedId = intent.getLongExtra("id", -1);
boolean all = false;
int mode = action;
if (action == ACTION_PLAY_ALL || action == ACTION_ENQUEUE_ALL) {
MediaAdapter adapter = mCurrentAdapter;
boolean notPlayAllAdapter = (adapter != mSongAdapter && adapter != mAlbumAdapter
&& adapter != mArtistAdapter) || id == MediaView.HEADER_ID;
if (mode == ACTION_ENQUEUE_ALL && notPlayAllAdapter) {
mode = ACTION_ENQUEUE;
} else if (mode == ACTION_PLAY_ALL && notPlayAllAdapter) {
mode = ACTION_PLAY;
} else {
all = true;
}
}
mode = modeForAction[mode];
QueryTask query = buildQueryFromIntent(intent, false, all);
PlaybackService.get(this).addSongs(mode, query, intent.getIntExtra("type", -1));
mLastActedId = id;
if (mDefaultAction == ACTION_LAST_USED && mLastAction != action) {
mLastAction = action;
@ -333,6 +353,7 @@ public class LibraryActivity
return tab;
}
@Override
public void onItemClick(AdapterView<?> list, View view, int pos, long id)
{
MediaView mediaView = (MediaView)view;
@ -349,14 +370,17 @@ public class LibraryActivity
}
}
@Override
public void afterTextChanged(Editable editable)
{
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
@Override
public void onTextChanged(CharSequence text, int start, int before, int count)
{
String filter = text.toString();
@ -463,8 +487,10 @@ public class LibraryActivity
* @param intent An intent created with
* {@link LibraryActivity#createClickIntent(MediaAdapter,MediaView)}.
* @param empty If true, use the empty projection (only query id).
* @param all If true query all songs in the adapter; otherwise query based
* on the row selected.
*/
private QueryTask buildQueryFromIntent(Intent intent, boolean empty)
private QueryTask buildQueryFromIntent(Intent intent, boolean empty, boolean all)
{
int type = intent.getIntExtra("type", 1);
@ -476,10 +502,12 @@ public class LibraryActivity
long id = intent.getLongExtra("id", -1);
QueryTask query;
if (id == MediaView.HEADER_ID)
if (all || id == MediaView.HEADER_ID) {
query = mAdapters[type - 1].buildSongQuery(projection);
else
query.setExtra(id);
} else {
query = MediaUtils.buildQuery(type, id, projection, null);
}
return query;
}
@ -493,6 +521,8 @@ public class LibraryActivity
private static final int MENU_EDIT = 6;
private static final int MENU_RENAME_PLAYLIST = 7;
private static final int MENU_SELECT_PLAYLIST = 8;
private static final int MENU_PLAY_ALL = 9;
private static final int MENU_ENQUEUE_ALL = 10;
@Override
public void onCreateContextMenu(ContextMenu menu, View listView, ContextMenu.ContextMenuInfo absInfo)
@ -505,6 +535,7 @@ public class LibraryActivity
Intent intent = createClickIntent(adapter, view);
boolean isHeader = view.getMediaId() == MediaView.HEADER_ID;
boolean isAllAdapter = adapter == mArtistAdapter || adapter == mAlbumAdapter || adapter == mSongAdapter;
if (isHeader)
menu.setHeaderTitle(getString(R.string.all_songs));
@ -512,7 +543,11 @@ public class LibraryActivity
menu.setHeaderTitle(view.getTitle());
menu.add(0, MENU_PLAY, 0, R.string.play).setIntent(intent);
if (isAllAdapter)
menu.add(0, MENU_PLAY_ALL, 0, R.string.play_all).setIntent(intent);
menu.add(0, MENU_ENQUEUE, 0, R.string.enqueue).setIntent(intent);
if (isAllAdapter)
menu.add(0, MENU_ENQUEUE_ALL, 0, R.string.enqueue_all).setIntent(intent);
if (adapter == mPlaylistAdapter) {
menu.add(0, MENU_RENAME_PLAYLIST, 0, R.string.rename).setIntent(intent);
menu.add(0, MENU_EDIT, 0, R.string.edit).setIntent(intent);
@ -534,7 +569,7 @@ public class LibraryActivity
*/
private void addToPlaylist(long playlistId, Intent intent)
{
QueryTask query = buildQueryFromIntent(intent, true);
QueryTask query = buildQueryFromIntent(intent, true, false);
int count = Playlist.addToPlaylist(getContentResolver(), playlistId, query);
String message = getResources().getQuantityString(R.plurals.added_to_playlist, count, count, intent.getStringExtra("playlistName"));
@ -590,6 +625,12 @@ public class LibraryActivity
case MENU_PLAY:
pickSongs(intent, ACTION_PLAY);
break;
case MENU_PLAY_ALL:
pickSongs(intent, ACTION_PLAY_ALL);
break;
case MENU_ENQUEUE_ALL:
pickSongs(intent, ACTION_ENQUEUE_ALL);
break;
case MENU_NEW_PLAYLIST: {
NewPlaylistDialog dialog = new NewPlaylistDialog(this, null, R.string.create, intent);
dialog.setDismissMessage(mHandler.obtainMessage(MSG_NEW_PLAYLIST, dialog));

View File

@ -1127,33 +1127,34 @@ public final class PlaybackService extends Service implements Handler.Callback,
* worker thread.
*
* @param mode How to add songs. Passed to
* {@link SongTimeline#addSongs(int, android.database.Cursor, int)}
* {@link SongTimeline#addSongs(int, android.database.Cursor, int, long)}
* @param query The query to run.
* @param jumpTo Passed to
* {@link SongTimeline#addSongs(int, android.database.Cursor, int)}
* {@link SongTimeline#addSongs(int, android.database.Cursor, int, long)}
*/
public void runQuery(int mode, QueryTask query, int jumpTo)
{
int count = mTimeline.addSongs(mode, query.runQuery(getContentResolver()), jumpTo);
int count = mTimeline.addSongs(mode, query.runQuery(getContentResolver()), jumpTo, query.getExtra());
int text;
switch (mode) {
case SongTimeline.MODE_PLAY:
case SongTimeline.MODE_PLAY_JUMP_TO:
case SongTimeline.MODE_PLAY_POS_FIRST:
case SongTimeline.MODE_PLAY_ID_FIRST:
text = R.plurals.playing;
if (count != 0 && (mState & FLAG_PLAYING) == 0)
setFlag(FLAG_PLAYING);
break;
case SongTimeline.MODE_PLAY_NEXT:
case SongTimeline.MODE_ENQUEUE:
case SongTimeline.MODE_ENQUEUE_ID_FIRST:
text = R.plurals.enqueued;
break;
default:
return;
throw new IllegalArgumentException("Invalid add mode: " + mode);
}
if ((mode == SongTimeline.MODE_PLAY || mode == SongTimeline.MODE_PLAY_JUMP_TO) && count != 0 && (mState & FLAG_PLAYING) == 0)
setFlag(FLAG_PLAYING);
Toast.makeText(this, getResources().getQuantityString(text, count, count), Toast.LENGTH_SHORT).show();
}
@ -1164,7 +1165,7 @@ public final class PlaybackService extends Service implements Handler.Callback,
* immediately or enqueue them for later.
* @param query The query.
* @param jumpTo Passed to
* {@link SongTimeline#addSongs(int, android.database.Cursor, int)}
* {@link SongTimeline#addSongs(int, android.database.Cursor, int, long)}
*/
public void addSongs(int mode, QueryTask query, int jumpTo)
{

View File

@ -139,7 +139,7 @@ public class PlaylistActivity extends Activity
mAdapter.remove(id);
} else if (!mEditing) {
QueryTask query = MediaUtils.buildPlaylistQuery(mPlaylistId, Song.FILLED_PLAYLIST_PROJECTION, null);
PlaybackService.get(this).addSongs(SongTimeline.MODE_PLAY_JUMP_TO, query, position - mListView.getHeaderViewsCount());
PlaybackService.get(this).addSongs(SongTimeline.MODE_PLAY_POS_FIRST, query, position - mListView.getHeaderViewsCount());
}
}
}

View File

@ -35,6 +35,7 @@ public class QueryTask {
private final String mSelection;
private final String[] mSelectionArgs;
private String mSortOrder;
private long mExtra;
/**
* Create the tasks. All arguments are passed directly to
@ -69,6 +70,27 @@ public class QueryTask {
mSortOrder = sortOrder;
}
/**
* Store some extra data with this query. This data is not used at all by
* when running the query.
*
* @param extra The extra data
*/
public void setExtra(long extra)
{
mExtra = extra;
}
/**
* Retrieve the extra data stored by {@link QueryTask#setExtra(long)}
*
* @return The extra data
*/
public long getExtra()
{
return mExtra;
}
/**
* Run the query. Should be called on a background thread.
*

View File

@ -74,28 +74,57 @@ public final class SongTimeline {
/**
* Clear the timeline and use only the provided songs.
*
* @see SongTimeline#addSongs(int,Cursor,int)
* @see SongTimeline#addSongs(int, android.database.Cursor, int, long)
*/
public static final int MODE_PLAY = 0;
/**
* Clear the queue and add the songs after the current song.
*
* @see SongTimeline#addSongs(int,Cursor,int)
* @see SongTimeline#addSongs(int, android.database.Cursor, int, long)
*/
public static final int MODE_PLAY_NEXT = 1;
/**
* Add the songs at the end of the timeline, clearing random songs.
*
* @see SongTimeline#addSongs(int,Cursor,int)
* @see SongTimeline#addSongs(int, android.database.Cursor, int, long)
*/
public static final int MODE_ENQUEUE = 2;
/**
* Like play mode, but make the current position point to the song at
* the given position.
* Like play mode, but make the song at the given position play first by
* removing the songs before the given position in the query and appending
* them to the end of the queue.
*
* @see SongTimeline#addSongs(int,Cursor,int)
* Pass the position in the integer argument.
*
* @see SongTimeline#addSongs(int, android.database.Cursor, int, long)
*/
public static final int MODE_PLAY_JUMP_TO = 3;
public static final int MODE_PLAY_POS_FIRST = 3;
/**
* Like play mode, but make the song with the given id play first by
* removing the songs before the song in the query and appending
* them to the end of the queue. If there are multiple songs with
* the given id, picks the first song with that id.
*
* Pass the id in the long argument and the type of the id (one of
* MediaUtils.TYPE_ARTIST, TYPE_ALBUM or TYPE_SONG) in the integer
* argument.
*
* @see SongTimeline#addSongs(int, android.database.Cursor, int, long)
*/
public static final int MODE_PLAY_ID_FIRST = 4;
/**
* Like enqueue mode, but make the song with the given id play first by
* removing the songs before the song in the query and appending
* them to the end of the queue. If there are multiple songs with
* the given id, picks the first song with that id.
*
* Pass the id in the long argument and the type of the id (one of
* MediaUtils.TYPE_ARTIST, TYPE_ALBUM or TYPE_SONG) in the integer
* argument.
*
* @see SongTimeline#addSongs(int, android.database.Cursor, int, long)
*/
public static final int MODE_ENQUEUE_ID_FIRST = 5;
/**
* Disable shuffle.
@ -486,10 +515,13 @@ public final class SongTimeline {
*
* @param mode How to add the songs. One of SongTimeline.MODE_*.
* @param cursor The cursor to fill from.
* @param jumpTo The position to jump to for MODE_PLAY_JUMP_TO.
* @param i The integer argument. See individual mode documentation for
* usage.
* @param l The long argument. See individual mode documentation for
* usage.
* @return The number of songs that were added.
*/
public int addSongs(int mode, Cursor cursor, int jumpTo)
public int addSongs(int mode, Cursor cursor, int i, long l)
{
if (cursor == null)
return 0;
@ -502,11 +534,12 @@ public final class SongTimeline {
saveActiveSongs();
switch (mode) {
case MODE_ENQUEUE: {
int i = timeline.size();
while (--i > mCurrentPos) {
if (timeline.get(i).isRandom())
timeline.remove(i);
case MODE_ENQUEUE:
case MODE_ENQUEUE_ID_FIRST: {
int j = timeline.size();
while (--j > mCurrentPos) {
if (timeline.get(j).isRandom())
timeline.remove(j);
}
break;
}
@ -514,7 +547,8 @@ public final class SongTimeline {
timeline.subList(mCurrentPos + 1, timeline.size()).clear();
break;
case MODE_PLAY:
case MODE_PLAY_JUMP_TO:
case MODE_PLAY_POS_FIRST:
case MODE_PLAY_ID_FIRST:
timeline.clear();
mCurrentPos = 0;
break;
@ -530,15 +564,42 @@ public final class SongTimeline {
Song song = new Song(-1);
song.populate(cursor);
timeline.add(song);
if (j == jumpTo)
if (jumpSong == null) {
if (mode == MODE_PLAY_POS_FIRST && j == i) {
jumpSong = song;
} else if (mode == MODE_PLAY_ID_FIRST || mode == MODE_ENQUEUE_ID_FIRST) {
long id;
switch (i) {
case MediaUtils.TYPE_ARTIST:
id = song.artistId;
break;
case MediaUtils.TYPE_ALBUM:
id = song.albumId;
break;
case MediaUtils.TYPE_SONG:
id = song.id;
break;
default:
throw new IllegalArgumentException("Unsupported id type: " + i);
}
if (id == l)
jumpSong = song;
}
}
}
if (mShuffleMode != SHUFFLE_NONE)
MediaUtils.shuffle(timeline.subList(start, timeline.size()), mShuffleMode == SHUFFLE_ALBUMS);
if (mode == MODE_PLAY_JUMP_TO && jumpSong != null)
mCurrentPos = timeline.indexOf(jumpSong);
if (jumpSong != null) {
int jumpPos = timeline.indexOf(jumpSong);
if (jumpPos != start) {
// Get the sublist twice to avoid a ConcurrentModificationException.
timeline.addAll(timeline.subList(start, jumpPos));
timeline.subList(start, jumpPos).clear();
}
}
broadcastChangedSongs();
}