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>1</item>
<item>2</item> <item>2</item>
<item>3</item> <item>3</item>
<item>4</item>
</string-array> </string-array>
<string-array name="swipe_action_values"> <string-array name="swipe_action_values">
<!-- This must match the order of swipe_action_entries exactly and <!-- This must match the order of swipe_action_entries exactly and
@ -78,6 +79,8 @@ THE SOFTWARE.
<item>@string/play</item> <item>@string/play</item>
<item>@string/enqueue</item> <item>@string/enqueue</item>
<item>@string/last_used_action</item> <item>@string/last_used_action</item>
<item>@string/play_all</item>
<item>@string/enqueue_all</item>
</string-array> </string-array>
<string-array name="notification_mode_entries"> <string-array name="notification_mode_entries">
<item>@string/never_show</item> <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_PLAY = 0;
private static final int ACTION_ENQUEUE = 1; private static final int ACTION_ENQUEUE = 1;
private static final int ACTION_LAST_USED = 2; 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 = 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; private TabHost mTabHost;
@ -248,7 +251,7 @@ public class LibraryActivity
if (action == ACTION_LAST_USED) if (action == ACTION_LAST_USED)
action = mLastAction; 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); String text = getString(res);
mArtistAdapter.setHeaderText(text); mArtistAdapter.setHeaderText(text);
mAlbumAdapter.setHeaderText(text); mAlbumAdapter.setHeaderText(text);
@ -267,11 +270,28 @@ public class LibraryActivity
if (action == ACTION_LAST_USED) if (action == ACTION_LAST_USED)
action = mLastAction; action = mLastAction;
int mode = modeForAction[action]; long id = intent.getLongExtra("id", -1);
QueryTask query = buildQueryFromIntent(intent, false);
PlaybackService.get(this).addSongs(mode, query, 0);
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) { if (mDefaultAction == ACTION_LAST_USED && mLastAction != action) {
mLastAction = action; mLastAction = action;
@ -333,6 +353,7 @@ public class LibraryActivity
return tab; return tab;
} }
@Override
public void onItemClick(AdapterView<?> list, View view, int pos, long id) public void onItemClick(AdapterView<?> list, View view, int pos, long id)
{ {
MediaView mediaView = (MediaView)view; MediaView mediaView = (MediaView)view;
@ -349,14 +370,17 @@ public class LibraryActivity
} }
} }
@Override
public void afterTextChanged(Editable editable) public void afterTextChanged(Editable editable)
{ {
} }
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) public void beforeTextChanged(CharSequence s, int start, int count, int after)
{ {
} }
@Override
public void onTextChanged(CharSequence text, int start, int before, int count) public void onTextChanged(CharSequence text, int start, int before, int count)
{ {
String filter = text.toString(); String filter = text.toString();
@ -463,8 +487,10 @@ public class LibraryActivity
* @param intent An intent created with * @param intent An intent created with
* {@link LibraryActivity#createClickIntent(MediaAdapter,MediaView)}. * {@link LibraryActivity#createClickIntent(MediaAdapter,MediaView)}.
* @param empty If true, use the empty projection (only query id). * @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); int type = intent.getIntExtra("type", 1);
@ -476,10 +502,12 @@ public class LibraryActivity
long id = intent.getLongExtra("id", -1); long id = intent.getLongExtra("id", -1);
QueryTask query; QueryTask query;
if (id == MediaView.HEADER_ID) if (all || id == MediaView.HEADER_ID) {
query = mAdapters[type - 1].buildSongQuery(projection); query = mAdapters[type - 1].buildSongQuery(projection);
else query.setExtra(id);
} else {
query = MediaUtils.buildQuery(type, id, projection, null); query = MediaUtils.buildQuery(type, id, projection, null);
}
return query; return query;
} }
@ -493,6 +521,8 @@ public class LibraryActivity
private static final int MENU_EDIT = 6; private static final int MENU_EDIT = 6;
private static final int MENU_RENAME_PLAYLIST = 7; private static final int MENU_RENAME_PLAYLIST = 7;
private static final int MENU_SELECT_PLAYLIST = 8; 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 @Override
public void onCreateContextMenu(ContextMenu menu, View listView, ContextMenu.ContextMenuInfo absInfo) public void onCreateContextMenu(ContextMenu menu, View listView, ContextMenu.ContextMenuInfo absInfo)
@ -505,6 +535,7 @@ public class LibraryActivity
Intent intent = createClickIntent(adapter, view); Intent intent = createClickIntent(adapter, view);
boolean isHeader = view.getMediaId() == MediaView.HEADER_ID; boolean isHeader = view.getMediaId() == MediaView.HEADER_ID;
boolean isAllAdapter = adapter == mArtistAdapter || adapter == mAlbumAdapter || adapter == mSongAdapter;
if (isHeader) if (isHeader)
menu.setHeaderTitle(getString(R.string.all_songs)); menu.setHeaderTitle(getString(R.string.all_songs));
@ -512,7 +543,11 @@ public class LibraryActivity
menu.setHeaderTitle(view.getTitle()); menu.setHeaderTitle(view.getTitle());
menu.add(0, MENU_PLAY, 0, R.string.play).setIntent(intent); 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); 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) { if (adapter == mPlaylistAdapter) {
menu.add(0, MENU_RENAME_PLAYLIST, 0, R.string.rename).setIntent(intent); menu.add(0, MENU_RENAME_PLAYLIST, 0, R.string.rename).setIntent(intent);
menu.add(0, MENU_EDIT, 0, R.string.edit).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) 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); int count = Playlist.addToPlaylist(getContentResolver(), playlistId, query);
String message = getResources().getQuantityString(R.plurals.added_to_playlist, count, count, intent.getStringExtra("playlistName")); String message = getResources().getQuantityString(R.plurals.added_to_playlist, count, count, intent.getStringExtra("playlistName"));
@ -590,6 +625,12 @@ public class LibraryActivity
case MENU_PLAY: case MENU_PLAY:
pickSongs(intent, ACTION_PLAY); pickSongs(intent, ACTION_PLAY);
break; 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: { case MENU_NEW_PLAYLIST: {
NewPlaylistDialog dialog = new NewPlaylistDialog(this, null, R.string.create, intent); NewPlaylistDialog dialog = new NewPlaylistDialog(this, null, R.string.create, intent);
dialog.setDismissMessage(mHandler.obtainMessage(MSG_NEW_PLAYLIST, dialog)); 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. * worker thread.
* *
* @param mode How to add songs. Passed to * @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 query The query to run.
* @param jumpTo Passed to * @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) 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; int text;
switch (mode) { switch (mode) {
case SongTimeline.MODE_PLAY: 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; text = R.plurals.playing;
if (count != 0 && (mState & FLAG_PLAYING) == 0)
setFlag(FLAG_PLAYING);
break; break;
case SongTimeline.MODE_PLAY_NEXT: case SongTimeline.MODE_PLAY_NEXT:
case SongTimeline.MODE_ENQUEUE: case SongTimeline.MODE_ENQUEUE:
case SongTimeline.MODE_ENQUEUE_ID_FIRST:
text = R.plurals.enqueued; text = R.plurals.enqueued;
break; break;
default: 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(); 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. * immediately or enqueue them for later.
* @param query The query. * @param query The query.
* @param jumpTo Passed to * @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) public void addSongs(int mode, QueryTask query, int jumpTo)
{ {

View File

@ -139,7 +139,7 @@ public class PlaylistActivity extends Activity
mAdapter.remove(id); mAdapter.remove(id);
} else if (!mEditing) { } else if (!mEditing) {
QueryTask query = MediaUtils.buildPlaylistQuery(mPlaylistId, Song.FILLED_PLAYLIST_PROJECTION, null); 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 mSelection;
private final String[] mSelectionArgs; private final String[] mSelectionArgs;
private String mSortOrder; private String mSortOrder;
private long mExtra;
/** /**
* Create the tasks. All arguments are passed directly to * Create the tasks. All arguments are passed directly to
@ -69,6 +70,27 @@ public class QueryTask {
mSortOrder = sortOrder; 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. * 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. * 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; public static final int MODE_PLAY = 0;
/** /**
* Clear the queue and add the songs after the current song. * 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; public static final int MODE_PLAY_NEXT = 1;
/** /**
* Add the songs at the end of the timeline, clearing random songs. * 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; public static final int MODE_ENQUEUE = 2;
/** /**
* Like play mode, but make the current position point to the song at * Like play mode, but make the song at the given position play first by
* the given position. * 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. * Disable shuffle.
@ -486,10 +515,13 @@ public final class SongTimeline {
* *
* @param mode How to add the songs. One of SongTimeline.MODE_*. * @param mode How to add the songs. One of SongTimeline.MODE_*.
* @param cursor The cursor to fill from. * @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. * @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) if (cursor == null)
return 0; return 0;
@ -502,11 +534,12 @@ public final class SongTimeline {
saveActiveSongs(); saveActiveSongs();
switch (mode) { switch (mode) {
case MODE_ENQUEUE: { case MODE_ENQUEUE:
int i = timeline.size(); case MODE_ENQUEUE_ID_FIRST: {
while (--i > mCurrentPos) { int j = timeline.size();
if (timeline.get(i).isRandom()) while (--j > mCurrentPos) {
timeline.remove(i); if (timeline.get(j).isRandom())
timeline.remove(j);
} }
break; break;
} }
@ -514,7 +547,8 @@ public final class SongTimeline {
timeline.subList(mCurrentPos + 1, timeline.size()).clear(); timeline.subList(mCurrentPos + 1, timeline.size()).clear();
break; break;
case MODE_PLAY: case MODE_PLAY:
case MODE_PLAY_JUMP_TO: case MODE_PLAY_POS_FIRST:
case MODE_PLAY_ID_FIRST:
timeline.clear(); timeline.clear();
mCurrentPos = 0; mCurrentPos = 0;
break; break;
@ -530,15 +564,42 @@ public final class SongTimeline {
Song song = new Song(-1); Song song = new Song(-1);
song.populate(cursor); song.populate(cursor);
timeline.add(song); timeline.add(song);
if (j == jumpTo)
jumpSong = song; 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) if (mShuffleMode != SHUFFLE_NONE)
MediaUtils.shuffle(timeline.subList(start, timeline.size()), mShuffleMode == SHUFFLE_ALBUMS); MediaUtils.shuffle(timeline.subList(start, timeline.size()), mShuffleMode == SHUFFLE_ALBUMS);
if (mode == MODE_PLAY_JUMP_TO && jumpSong != null) if (jumpSong != null) {
mCurrentPos = timeline.indexOf(jumpSong); 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(); broadcastChangedSongs();
} }