Allow choice of sort modes in library

This commit is contained in:
Christopher Eby 2011-10-29 21:15:46 -05:00
parent fcad52ef5d
commit 4d43970c27
7 changed files with 147 additions and 13 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -55,6 +55,7 @@ THE SOFTWARE.
<string name="expand">Expand</string>
<string name="delete">Delete</string>
<string name="playback_view">Now Playing</string>
<string name="sort_by">Sort By</string>
<string name="search">Search</string>
<string name="done">Done</string>
@ -88,6 +89,15 @@ THE SOFTWARE.
<string name="enqueue_all">Enqueue All</string>
<string name="all_songs">All Songs</string>
<string name="name">Name</string>
<string name="number_of_tracks">Number of tracks</string>
<string name="year">Year</string>
<string name="date_added">Date added</string>
<string name="artist_album">Artist, album</string>
<string name="artist_album_track">Artist, album, track no.</string>
<string name="artist_album_title">Artist, album, title</string>
<string name="artist_year">Artist, year</string>
<!-- Notification -->
<string name="notification_title_paused">%s (Paused)</string>

View File

@ -361,6 +361,9 @@ public class LibraryActivity
}
}
loadSortOrder(mSongAdapter);
loadSortOrder(mArtistAdapter);
requestRequery(mSongAdapter);
requestRequery(mAlbumAdapter);
@ -702,14 +705,31 @@ public class LibraryActivity
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
menu.add(0, MENU_PLAYBACK, 0, R.string.playback_view).setIcon(R.drawable.ic_menu_gallery);
menu.addSubMenu(0, MENU_SORT, 0, R.string.sort_by).setIcon(R.drawable.ic_menu_sort_alphabetically);
menu.add(0, MENU_SEARCH, 0, R.string.search).setIcon(R.drawable.ic_menu_search);
menu.add(0, MENU_PLAYBACK, 0, R.string.playback_view).setIcon(R.drawable.ic_menu_gallery);
return super.onCreateOptionsMenu(menu);
}
public static final int GROUP_SORT = 100;
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getGroupId() == GROUP_SORT) {
MediaAdapter adapter = mCurrentAdapter;
ListView view = (ListView)mTabHost.getCurrentView();
int i = item.getItemId();
adapter.setSortMode(i);
requestRequery(adapter);
// Force a new FastScroller to be created so the scroll section
// are updated.
view.setFastScrollEnabled(false);
view.setFastScrollEnabled(true);
mHandler.sendMessage(mHandler.obtainMessage(MSG_SAVE_SORT, adapter));
return true;
}
switch (item.getItemId()) {
case MENU_SEARCH:
setSearchBoxVisible(!mSearchBoxVisible);
@ -717,6 +737,9 @@ public class LibraryActivity
case MENU_PLAYBACK:
startActivity(new Intent(this, FullPlaybackActivity.class));
return true;
case MENU_SORT:
mCurrentAdapter.buildSortMenu(item.getSubMenu());
return true;
default:
return super.onOptionsItemSelected(item);
}
@ -744,6 +767,7 @@ public class LibraryActivity
MediaAdapter adapter = new MediaAdapter(this, type, expandable, hasHeader, limiter);
view.setAdapter(adapter);
loadSortOrder(adapter);
Resources res = getResources();
String labelRes = res.getString(label);
@ -779,6 +803,10 @@ public class LibraryActivity
* Call addToPlaylist with data from the intent in obj.
*/
private static final int MSG_ADD_TO_PLAYLIST = 15;
/**
* Save the sort mode for the adapter passed in obj.
*/
private static final int MSG_SAVE_SORT = 16;
@Override
public boolean handleMessage(Message message)
@ -824,6 +852,13 @@ public class LibraryActivity
});
break;
}
case MSG_SAVE_SORT: {
MediaAdapter adapter = (MediaAdapter)message.obj;
SharedPreferences.Editor editor = PlaybackService.getSettings(this).edit();
editor.putInt(String.format("sort_%d_%d", adapter.getMediaType(), adapter.getLimiterType()), adapter.getSortMode());
editor.commit();
break;
}
default:
return super.handleMessage(message);
}
@ -928,4 +963,18 @@ public class LibraryActivity
runQuery(adapter);
updateLimiterViews();
}
/**
* Set the saved sort mode for the given adapter. The adapter should
* be re-queried after calling this.
*
* @param adapter The adapter to load for.
*/
private void loadSortOrder(MediaAdapter adapter)
{
String key = String.format("sort_%d_%d", adapter.getMediaType(), adapter.getLimiterType());
int def = adapter.getDefaultSortMode();
int sort = PlaybackService.getSettings(this).getInt(key, def);
adapter.setSortMode(sort);
}
}

View File

@ -28,6 +28,7 @@ import android.database.DatabaseUtils;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
@ -113,6 +114,9 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer {
* True if the data is stale and the query should be re-run.
*/
private boolean mNeedsRequery;
private int[] mSortEntries;
private String[] mSortValues;
private int mSortMode;
/**
* Construct a MediaAdapter representing the given <code>type</code> of
@ -124,7 +128,7 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer {
* and what fields to display in the views.
* @param expandable Whether an expand arrow should be shown to the right
* of the views' text
* @param hasHeader Wether this view has a header row.
* @param hasHeader Whether this view has a header row.
* @param limiter An initial limiter to use
*/
public MediaAdapter(Context context, int type, boolean expandable, boolean hasHeader, Limiter limiter)
@ -145,6 +149,8 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer {
mFields = new String[] { MediaStore.Audio.Artists.ARTIST };
mFieldKeys = new String[] { MediaStore.Audio.Artists.ARTIST_KEY };
mSongSort = MediaUtils.DEFAULT_SORT;
mSortEntries = new int[] { R.string.name, R.string.number_of_tracks };
mSortValues = new String[] { "artist_key", "number_of_tracks" };
break;
case MediaUtils.TYPE_ALBUM:
mStore = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
@ -152,21 +158,29 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer {
// Why is there no artist_key column constant in the album MediaStore? The column does seem to exist.
mFieldKeys = new String[] { "artist_key", MediaStore.Audio.Albums.ALBUM_KEY };
mSongSort = "album_key,track";
mSortEntries = new int[] { R.string.name, R.string.artist_album, R.string.year, R.string.number_of_tracks };
mSortValues = new String[] { "album_key", "artist_key,album_key", "minyear", "numsongs" };
break;
case MediaUtils.TYPE_SONG:
mStore = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
mFields = new String[] { MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.TITLE };
mFieldKeys = new String[] { MediaStore.Audio.Media.ARTIST_KEY, MediaStore.Audio.Media.ALBUM_KEY, MediaStore.Audio.Media.TITLE_KEY };
mSortEntries = new int[] { R.string.name, R.string.artist_album_track, R.string.artist_album_title, R.string.artist_year, R.string.year };
mSortValues = new String[] { "title_key", "artist_key,album_key,track", "artist_key,album_key,title_key", "artist_key,year", "year" };
break;
case MediaUtils.TYPE_PLAYLIST:
mStore = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
mFields = new String[] { MediaStore.Audio.Playlists.NAME };
mFieldKeys = null;
mSortEntries = new int[] { R.string.name, R.string.date_added };
mSortValues = new String[] { "name", "date_added" };
break;
case MediaUtils.TYPE_GENRE:
mStore = MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI;
mFields = new String[] { MediaStore.Audio.Genres.NAME };
mFieldKeys = null;
mSortEntries = new int[] { R.string.name };
mSortValues = new String[] { "name" };
break;
default:
throw new IllegalArgumentException("Invalid value for type: " + type);
@ -292,13 +306,7 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer {
StringBuilder selection = new StringBuilder();
String[] selectionArgs = null;
String sort;
if (limiter != null && limiter.type == MediaUtils.TYPE_ALBUM)
sort = MediaStore.Audio.Media.TRACK;
else if (mFieldKeys == null)
sort = mFields[mFields.length - 1];
else
sort = mFieldKeys[mFieldKeys.length - 1];
String sort = mSortValues[mSortMode];
if (mType == MediaUtils.TYPE_SONG || forceMusicCheck)
selection.append("is_music!=0");
@ -346,7 +354,7 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer {
if (limiter != null && limiter.type == MediaUtils.TYPE_GENRE) {
// Genre is not standard metadata for MediaStore.Audio.Media.
// We have to query it through a separate provider. : /
return MediaUtils.buildGenreQuery(limiter.id, projection, selection.toString(), selectionArgs);
return MediaUtils.buildGenreQuery(limiter.id, projection, selection.toString(), selectionArgs, sort);
} else {
if (limiter != null) {
if (selection.length() != 0)
@ -379,6 +387,8 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer {
QueryTask query = buildQuery(projection, true);
if (mType != MediaUtils.TYPE_SONG) {
query.setUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
// Would be better to match the sort order in the adapter. This
// is likely to require significantly more work though.
query.setSortOrder(mSongSort);
}
return query;
@ -471,6 +481,8 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer {
@Override
public Object[] getSections()
{
if (mSortMode != 0)
return null;
return mIndexer.getSections();
}
@ -511,6 +523,64 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer {
return new MediaView(mContext, null, mExpandable ? MediaView.sExpander : null);
}
/**
* Returns the type of the current limiter.
*
* @return One of MediaUtils.TYPE_, or MediaUtils.TYPE_INVALID if there is
* no limiter set.
*/
public int getLimiterType()
{
Limiter limiter = mLimiter;
if (limiter != null)
return limiter.type;
return MediaUtils.TYPE_INVALID;
}
/**
* Build a menu of sort modes appropriate for this adapter.
*
* @param menu The menu the items will be added to.
*/
public void buildSortMenu(SubMenu menu)
{
int[] entries = mSortEntries;
menu.clear();
for (int i = 0; i != entries.length; ++i)
menu.add(LibraryActivity.GROUP_SORT, i, 0, entries[i]);
}
/**
* Set the sorting mode. The adapter should be re-queried after changing
* this.
*
* @param i The index of the sort mode.
*/
public void setSortMode(int i)
{
mSortMode = i;
}
/**
* Returns the sort mode that should be used if no preference is saved. This
* may very based on the active limiter.
*/
public int getDefaultSortMode()
{
Limiter limiter = mLimiter;
if (limiter != null && limiter.type == MediaUtils.TYPE_SONG)
return 1; // artist,album,track
return 0;
}
/**
* Return the current sort mode set on this adapter.
*/
public int getSortMode()
{
return mSortMode;
}
/**
* Limiter is a constraint for MediaAdapters used when a row is "expanded".
*/

View File

@ -33,6 +33,10 @@ import java.util.Random;
import junit.framework.Assert;
public class MediaUtils {
/**
* A special invalid media type.
*/
public static final int TYPE_INVALID = 0;
/**
* Type indicating an id represents an artist.
*/
@ -159,11 +163,11 @@ public class MediaUtils {
* @param projection The columns to query.
* @param selection The selection to pass to the query, or null.
* @param selectionArgs The arguments to substitute into the selection.
* @param sort The sort order.
*/
public static QueryTask buildGenreQuery(long id, String[] projection, String selection, String[] selectionArgs)
public static QueryTask buildGenreQuery(long id, String[] projection, String selection, String[] selectionArgs, String sort)
{
Uri uri = MediaStore.Audio.Genres.Members.getContentUri("external", id);
String sort = MediaStore.Audio.Genres.Members.TITLE_KEY;
return new QueryTask(uri, projection, selection, selectionArgs, sort);
}
@ -187,7 +191,7 @@ public class MediaUtils {
case TYPE_PLAYLIST:
return buildPlaylistQuery(id, projection, selection);
case TYPE_GENRE:
return buildGenreQuery(id, projection, selection, null);
return buildGenreQuery(id, projection, selection, null, MediaStore.Audio.Genres.Members.TITLE_KEY);
default:
throw new IllegalArgumentException("Specified type not valid: " + type);
}

View File

@ -355,6 +355,7 @@ public class PlaybackActivity extends Activity
{
}
static final int MENU_SORT = 1;
static final int MENU_PREFS = 2;
static final int MENU_LIBRARY = 3;
static final int MENU_PLAYBACK = 5;