Add genre tab to library
This commit is contained in:
parent
d1d875fd5f
commit
33374c2ebd
@ -67,6 +67,12 @@ THE SOFTWARE.
|
||||
android:layout_height="fill_parent"
|
||||
android:divider="@null"
|
||||
android:fastScrollEnabled="true" />
|
||||
<ListView
|
||||
android:id="@+id/genre_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:divider="@null"
|
||||
android:fastScrollEnabled="true" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</TabHost>
|
||||
@ -103,4 +109,4 @@ THE SOFTWARE.
|
||||
android:layout_alignParentRight="true"
|
||||
android:src="@android:drawable/ic_menu_close_clear_cancel" />
|
||||
</RelativeLayout>
|
||||
</merge>
|
||||
</merge>
|
||||
|
@ -79,6 +79,7 @@ THE SOFTWARE.
|
||||
<string name="albums">Albums</string>
|
||||
<string name="songs">Songs</string>
|
||||
<string name="playlists">Playlists</string>
|
||||
<string name="genres">Genres</string>
|
||||
|
||||
<string name="none">None</string>
|
||||
<string name="unknown">Unknown</string>
|
||||
|
@ -139,6 +139,11 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
|
||||
mFields = new String[] { MediaStore.Audio.Playlists.NAME };
|
||||
mFieldKeys = null;
|
||||
break;
|
||||
case MediaUtils.TYPE_GENRE:
|
||||
mStore = MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI;
|
||||
mFields = new String[] { MediaStore.Audio.Genres.NAME };
|
||||
mFieldKeys = null;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid value for type: " + type);
|
||||
}
|
||||
@ -195,7 +200,7 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
|
||||
* method may be overridden in subclasses to exclude certain media from the
|
||||
* adapter.
|
||||
*
|
||||
* @return The selection, formatted as an SQL WHERE clause or null for to
|
||||
* @return The selection, formatted as an SQL WHERE clause or null to
|
||||
* accept all media.
|
||||
*/
|
||||
protected String getDefaultSelection()
|
||||
@ -222,19 +227,19 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
|
||||
{
|
||||
ContentResolver resolver = ContextApplication.getContext().getContentResolver();
|
||||
|
||||
String[] projection;
|
||||
if (mFields.length == 1)
|
||||
projection = new String[] { BaseColumns._ID, mFields[0] };
|
||||
else
|
||||
projection = new String[] { BaseColumns._ID, mFields[mFields.length - 1], mFields[0] };
|
||||
|
||||
StringBuilder selection = new StringBuilder();
|
||||
String[] selectionArgs;
|
||||
String[] selectionArgs = null;
|
||||
|
||||
String defaultSelection = getDefaultSelection();
|
||||
if (defaultSelection != null)
|
||||
selection.append(defaultSelection);
|
||||
|
||||
if (mLimiter != null) {
|
||||
if (selection.length() != 0)
|
||||
selection.append(" AND ");
|
||||
selection.append(mLimiter.selection);
|
||||
}
|
||||
|
||||
if (constraint != null && constraint.length() != 0) {
|
||||
String[] needles;
|
||||
|
||||
@ -251,33 +256,36 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
|
||||
|
||||
int size = needles.length;
|
||||
selectionArgs = new String[size];
|
||||
int i = 0;
|
||||
|
||||
String[] keySource = mFieldKeys == null ? mFields : mFieldKeys;
|
||||
String keys = keySource[0];
|
||||
for (int j = 1; j != keySource.length; ++j)
|
||||
keys += "||" + keySource[j];
|
||||
|
||||
for (int j = 0; j != needles.length; ++i, ++j) {
|
||||
selectionArgs[i] = '%' + needles[j] + '%';
|
||||
for (int j = 0; j != needles.length; ++j) {
|
||||
selectionArgs[j] = '%' + needles[j] + '%';
|
||||
|
||||
// If we have something in the selection args (i.e. i > 0), we
|
||||
// If we have something in the selection args (i.e. j > 0), we
|
||||
// must have something in the selection, so we can skip the more
|
||||
// costly direct check of the selection length.
|
||||
if (i != 0 || selection.length() != 0)
|
||||
if (j != 0 || selection.length() != 0)
|
||||
selection.append(" AND ");
|
||||
selection.append(keys);
|
||||
selection.append(" LIKE ?");
|
||||
}
|
||||
} else {
|
||||
selectionArgs = null;
|
||||
}
|
||||
|
||||
String[] projection;
|
||||
if (mFields.length == 1)
|
||||
projection = new String[] { BaseColumns._ID, mFields[0] };
|
||||
else
|
||||
projection = new String[] { BaseColumns._ID, mFields[mFields.length - 1], mFields[0] };
|
||||
if (mLimiter != null) {
|
||||
if (mLimiter.type == MediaUtils.TYPE_GENRE) {
|
||||
// Genre is not standard metadata for MediaStore.Audio.Media.
|
||||
// We have to query it through a separate provider. : /
|
||||
return MediaUtils.queryGenre(mLimiter.id, projection, selection.toString(), selectionArgs);
|
||||
} else {
|
||||
if (selection.length() != 0)
|
||||
selection.append(" AND ");
|
||||
selection.append(mLimiter.selection);
|
||||
}
|
||||
}
|
||||
|
||||
return resolver.query(mStore, projection, selection.toString(), selectionArgs, getSortOrder());
|
||||
}
|
||||
@ -553,6 +561,10 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
|
||||
fields = new String[] { mSubTitle, mTitle };
|
||||
field = MediaStore.Audio.Media.ALBUM_ID;
|
||||
break;
|
||||
case MediaUtils.TYPE_GENRE:
|
||||
fields = new String[] { mTitle };
|
||||
field = null;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("getLimiter() is not supported for media type: " + mType);
|
||||
}
|
||||
@ -576,6 +588,7 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
|
||||
*/
|
||||
public static class Limiter implements Serializable {
|
||||
public final String[] names;
|
||||
public final long id;
|
||||
public final int type;
|
||||
public final String selection;
|
||||
|
||||
@ -583,7 +596,8 @@ public class MediaAdapter extends CursorAdapter implements FilterQueryProvider {
|
||||
{
|
||||
this.type = type;
|
||||
this.names = names;
|
||||
selection = String.format("%s=%d", field, id);
|
||||
this.id = id;
|
||||
selection = field == null ? null : String.format("%s=%d", field, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Christopher Eby <kreed@kreed.org>
|
||||
* Copyright (C) 2010, 2011 Christopher Eby <kreed@kreed.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -47,6 +47,10 @@ public class MediaUtils {
|
||||
* Type indicating an id represents a playlist.
|
||||
*/
|
||||
public static final int TYPE_PLAYLIST = 4;
|
||||
/**
|
||||
* Type indicating ids represent genres.
|
||||
*/
|
||||
public static final int TYPE_GENRE = 5;
|
||||
|
||||
/**
|
||||
* Return a cursor containing the ids of all the songs with artist or
|
||||
@ -96,6 +100,23 @@ public class MediaUtils {
|
||||
return resolver.query(uri, projection, null, null, sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a cursor containing the ids of all the songs in the genre
|
||||
* with the given id.
|
||||
*
|
||||
* @param id The id of the genre in MediaStore.Audio.Genres.
|
||||
* @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.
|
||||
*/
|
||||
public static Cursor queryGenre(long id, String[] projection, String selection, String[] selectionArgs)
|
||||
{
|
||||
ContentResolver resolver = ContextApplication.getContext().getContentResolver();
|
||||
Uri uri = MediaStore.Audio.Genres.Members.getContentUri("external", id);
|
||||
String sort = MediaStore.Audio.Genres.Members.TITLE_KEY;
|
||||
return resolver.query(uri, projection, selection, selectionArgs, sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array containing all the song ids that match the specified parameters
|
||||
*
|
||||
@ -118,6 +139,10 @@ public class MediaUtils {
|
||||
case TYPE_PLAYLIST:
|
||||
cursor = getPlaylistCursor(id, new String[] { MediaStore.Audio.Playlists.Members.AUDIO_ID });
|
||||
break;
|
||||
case TYPE_GENRE:
|
||||
// NOTE: AUDIO_ID does not seem to work here, strangely.
|
||||
cursor = queryGenre(id, new String[] { MediaStore.Audio.Genres.Members._ID }, null, null);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Specified type not valid: " + type);
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
|
||||
/**
|
||||
* The number of tabs in the song selector.
|
||||
*/
|
||||
private static final int TAB_COUNT = 4;
|
||||
private static final int TAB_COUNT = 5;
|
||||
|
||||
private TabHost mTabHost;
|
||||
|
||||
@ -86,6 +86,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
|
||||
private MediaAdapter mAlbumAdapter;
|
||||
private MediaAdapter mSongAdapter;
|
||||
private MediaAdapter mPlaylistAdapter;
|
||||
private MediaAdapter mGenreAdapter;
|
||||
private MediaAdapter mCurrentAdapter;
|
||||
|
||||
@Override
|
||||
@ -103,6 +104,8 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
|
||||
mTabHost.addTab(mTabHost.newTabSpec("tab_albums").setIndicator(res.getText(R.string.albums), res.getDrawable(R.drawable.tab_albums)).setContent(R.id.album_list));
|
||||
mTabHost.addTab(mTabHost.newTabSpec("tab_songs").setIndicator(res.getText(R.string.songs), res.getDrawable(R.drawable.tab_songs)).setContent(R.id.song_list));
|
||||
mTabHost.addTab(mTabHost.newTabSpec("tab_playlists").setIndicator(res.getText(R.string.playlists), res.getDrawable(R.drawable.tab_playlists)).setContent(R.id.playlist_list));
|
||||
// TODO: find/create genre icon
|
||||
mTabHost.addTab(mTabHost.newTabSpec("tab_genres").setIndicator(res.getText(R.string.genres), res.getDrawable(R.drawable.tab_songs)).setContent(R.id.genre_list));
|
||||
|
||||
mSearchBox = findViewById(R.id.search_box);
|
||||
|
||||
@ -118,7 +121,8 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
|
||||
mAlbumAdapter = setupView(R.id.album_list, new MediaAdapter(this, MediaUtils.TYPE_ALBUM, true, false, state == null ? null : (MediaAdapter.Limiter)state.getSerializable("limiter_albums")));
|
||||
mSongAdapter = setupView(R.id.song_list, new SongMediaAdapter(this, false, false, state == null ? null : (MediaAdapter.Limiter)state.getSerializable("limiter_songs")));
|
||||
mPlaylistAdapter = setupView(R.id.playlist_list, new MediaAdapter(this, MediaUtils.TYPE_PLAYLIST, false, true, null));
|
||||
mAdapters = new MediaAdapter[] { mArtistAdapter, mAlbumAdapter, mSongAdapter, mPlaylistAdapter };
|
||||
mGenreAdapter = setupView(R.id.genre_list, new MediaAdapter(this, MediaUtils.TYPE_GENRE, true, false, state == null ? null : (MediaAdapter.Limiter)state.getSerializable("limiter_genres")));
|
||||
mAdapters = new MediaAdapter[] { mArtistAdapter, mAlbumAdapter, mSongAdapter, mPlaylistAdapter, mGenreAdapter };
|
||||
|
||||
mTabHost.setOnTabChangedListener(this);
|
||||
|
||||
@ -185,6 +189,7 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
|
||||
out.putString("filter", mTextFilter.getText().toString());
|
||||
out.putSerializable("limiter_albums", mAlbumAdapter.getLimiter());
|
||||
out.putSerializable("limiter_songs", mSongAdapter.getLimiter());
|
||||
out.putSerializable("limiter_genres", mGenreAdapter.getLimiter());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -289,6 +294,10 @@ public class SongSelector extends PlaybackActivity implements AdapterView.OnItem
|
||||
mAlbumAdapter.setLimiter(limiter, false);
|
||||
mSongAdapter.setLimiter(limiter, true);
|
||||
return 1;
|
||||
case MediaUtils.TYPE_GENRE:
|
||||
mAlbumAdapter.setLimiter(null, true);
|
||||
mSongAdapter.setLimiter(limiter, false);
|
||||
return 2;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported limiter type: " + limiter.type);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user