From 798400d71353add597f382c811053612d5a8d675 Mon Sep 17 00:00:00 2001 From: Adrian Ulrich Date: Sat, 4 Mar 2017 18:29:20 +0100 Subject: [PATCH] expose support for albumartist and composer tags --- res/values/translatable.xml | 2 + .../android/medialibrary/MediaLibrary.java | 57 ++++++-- .../medialibrary/MediaLibraryBackend.java | 88 +++++++++--- .../android/medialibrary/MediaSchema.java | 64 ++++++++- .../android/vanilla/LibraryPagerAdapter.java | 126 ++++++++++++++---- .../android/vanilla/MediaAdapter.java | 28 +++- .../android/vanilla/MediaUtils.java | 28 +++- .../android/vanilla/TabOrderActivity.java | 4 +- 8 files changed, 327 insertions(+), 70 deletions(-) diff --git a/res/values/translatable.xml b/res/values/translatable.xml index aca08f81..14f692d3 100644 --- a/res/values/translatable.xml +++ b/res/values/translatable.xml @@ -109,6 +109,8 @@ THE SOFTWARE. Artists Albums + Album Artists + Composers Tracks Playlists Genres diff --git a/src/ch/blinkenlights/android/medialibrary/MediaLibrary.java b/src/ch/blinkenlights/android/medialibrary/MediaLibrary.java index 4a937010..fc446a26 100644 --- a/src/ch/blinkenlights/android/medialibrary/MediaLibrary.java +++ b/src/ch/blinkenlights/android/medialibrary/MediaLibrary.java @@ -29,19 +29,22 @@ import java.io.File; public class MediaLibrary { - public static final String TABLE_SONGS = "songs"; - public static final String TABLE_ALBUMS = "albums"; - public static final String TABLE_CONTRIBUTORS = "contributors"; - public static final String TABLE_CONTRIBUTORS_SONGS = "contributors_songs"; - public static final String TABLE_GENRES = "genres"; - public static final String TABLE_GENRES_SONGS = "genres_songs"; - public static final String TABLE_PLAYLISTS = "playlists"; - public static final String TABLE_PLAYLISTS_SONGS = "playlists_songs"; - public static final String TABLE_PREFERENCES = "preferences"; - public static final String VIEW_ARTISTS = "_artists"; - public static final String VIEW_ALBUMS_ARTISTS = "_albums_artists"; - public static final String VIEW_SONGS_ALBUMS_ARTISTS = "_songs_albums_artists"; - public static final String VIEW_PLAYLIST_SONGS = "_playlists_songs"; + public static final String TABLE_SONGS = "songs"; + public static final String TABLE_ALBUMS = "albums"; + public static final String TABLE_CONTRIBUTORS = "contributors"; + public static final String TABLE_CONTRIBUTORS_SONGS = "contributors_songs"; + public static final String TABLE_GENRES = "genres"; + public static final String TABLE_GENRES_SONGS = "genres_songs"; + public static final String TABLE_PLAYLISTS = "playlists"; + public static final String TABLE_PLAYLISTS_SONGS = "playlists_songs"; + public static final String TABLE_PREFERENCES = "preferences"; + public static final String VIEW_ARTISTS = "_artists"; + public static final String VIEW_ALBUMARTISTS = "_albumartists"; + public static final String VIEW_COMPOSERS = "_composers"; + public static final String VIEW_ALBUMS_ARTISTS = "_albums_artists"; + public static final String VIEW_SONGS_ALBUMS_ARTISTS = "_songs_albums_artists"; + public static final String VIEW_SONGS_ALBUMS_ARTISTS_HUGE = "_songs_albums_artists_huge"; + public static final String VIEW_PLAYLIST_SONGS = "_playlists_songs"; public static final int ROLE_ARTIST = 0; public static final int ROLE_COMPOSER = 1; @@ -561,6 +564,7 @@ public class MediaLibrary { * The mtime of this item */ String MTIME = "mtime"; + /** * ONLY IN VIEWS - the artist */ @@ -573,6 +577,33 @@ public class MediaLibrary { * ONLY IN VIEWS - the artist id */ String ARTIST_ID = "artist_id"; + + /** + * ONLY IN VIEWS - the albumartist + */ + String ALBUMARTIST = "albumartist"; + /** + * ONLY IN VIEWS - the albumartist_sort key + */ + String ALBUMARTIST_SORT = "albumartist_sort"; + /** + * ONLY IN VIEWS - the albumartist id + */ + String ALBUMARTIST_ID = "albumartist_id"; + + /** + * ONLY IN VIEWS - the composer + */ + String COMPOSER = "composer"; + /** + * ONLY IN VIEWS - the composer_sort key + */ + String COMPOSER_SORT = "composer_sort"; + /** + * ONLY IN VIEWS - the composer id + */ + String COMPOSER_ID = "composer_id"; + } // Songs <-> Contributor mapping diff --git a/src/ch/blinkenlights/android/medialibrary/MediaLibraryBackend.java b/src/ch/blinkenlights/android/medialibrary/MediaLibraryBackend.java index 6280bd57..a6998d02 100644 --- a/src/ch/blinkenlights/android/medialibrary/MediaLibraryBackend.java +++ b/src/ch/blinkenlights/android/medialibrary/MediaLibraryBackend.java @@ -35,7 +35,7 @@ public class MediaLibraryBackend extends SQLiteOpenHelper { /** * The database version we are using */ - private static final int DATABASE_VERSION = 20170211; + private static final int DATABASE_VERSION = 20170217; /** * on-disk file to store the database */ @@ -48,6 +48,14 @@ public class MediaLibraryBackend extends SQLiteOpenHelper { * Regexp to detect costy artist_id queries which we can optimize */ private static final Pattern sQueryMatchArtistSearch = Pattern.compile("(^|.+ )"+MediaLibrary.ContributorColumns.ARTIST_ID+"=(\\d+)$"); + /** + * Regexp to detect costy albumartist_id queries which we can optimize + */ + private static final Pattern sQueryMatchAlbArtistSearch = Pattern.compile("(^|.+ )"+MediaLibrary.ContributorColumns.ALBUMARTIST_ID+"=(\\d+)$"); + /** + * Regexp to detect costy composer_id queries which we can optimize + */ + private static final Pattern sQueryMatchComposerSearch = Pattern.compile("(^|.+ )"+MediaLibrary.ContributorColumns.COMPOSER_ID+"=(\\d+)$"); /** * Constructor for the MediaLibraryBackend helper @@ -232,29 +240,36 @@ public class MediaLibraryBackend extends SQLiteOpenHelper { */ Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) { + if (MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS_HUGE.equals(table)) { + Log.v("VanillaMusic", "+++ warning : using HUGE table in genquery!"); + } + if (selection != null) { if (MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS.equals(table)) { // artist matches in the song-view are costy: try to give sqlite a hint - Matcher artistMatch = sQueryMatchArtistSearch.matcher(selection); - if (artistMatch.matches()) { - selection = artistMatch.group(1); - final String artistId = artistMatch.group(2); + String[] contributorMatch = extractVirtualColumn(selection); + if (contributorMatch != null) { + selection = contributorMatch[0]; + final String contributorId = contributorMatch[1]; + final String contributorRole = contributorMatch[2]; selection += MediaLibrary.SongColumns._ID+" IN (SELECT "+MediaLibrary.ContributorSongColumns.SONG_ID+" FROM "+MediaLibrary.TABLE_CONTRIBUTORS_SONGS+" WHERE " - + MediaLibrary.ContributorSongColumns.ROLE+"=0 AND "+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID+"="+artistId+")"; + + MediaLibrary.ContributorSongColumns.ROLE+"="+contributorRole+" AND "+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID+"="+contributorId+")"; } } if (MediaLibrary.VIEW_ALBUMS_ARTISTS.equals(table)) { // looking up artists by albums will magically return every album where this // artist has at least one item (while still using the primary_artist_id as the artist key) - Matcher artistMatch = sQueryMatchArtistSearch.matcher(selection); - if (artistMatch.matches()) { - selection = artistMatch.group(1); - final String artistId = artistMatch.group(2); + String[] contributorMatch = extractVirtualColumn(selection); + if (contributorMatch != null) { + selection = contributorMatch[0]; + final String contributorId = contributorMatch[1]; + final String contributorRole = contributorMatch[2]; + selection += MediaLibrary.SongColumns._ID+" IN (SELECT DISTINCT "+MediaLibrary.SongColumns.ALBUM_ID+" FROM "+MediaLibrary.TABLE_SONGS+" WHERE " + MediaLibrary.SongColumns._ID+" IN (SELECT "+MediaLibrary.ContributorSongColumns.SONG_ID+" FROM "+MediaLibrary.TABLE_CONTRIBUTORS_SONGS+" WHERE " - + MediaLibrary.ContributorSongColumns.ROLE+"=0 AND "+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID+"="+artistId+"))"; + + MediaLibrary.ContributorSongColumns.ROLE+"="+contributorRole+" AND "+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID+"="+contributorId+"))"; } } @@ -265,17 +280,33 @@ public class MediaLibraryBackend extends SQLiteOpenHelper { final String genreId = genreMatch.group(2); // and extract the searched genre id final String songsQuery = buildSongIdFromGenreSelect(genreId); - if(table.equals(MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS)) { + if(table.equals(MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS) || + table.equals(MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS_HUGE) ) { selection += MediaLibrary.SongColumns._ID+" IN ("+songsQuery+") "; } - if (table.equals(MediaLibrary.VIEW_ARTISTS)) { - selection += MediaLibrary.ContributorColumns.ARTIST_ID+" IN ("+ buildSongIdFromGenreSelect(MediaLibrary.ContributorColumns.ARTIST_ID, songsQuery)+") "; + if (table.equals(MediaLibrary.VIEW_ALBUMS_ARTISTS)) { + selection += MediaLibrary.AlbumColumns._ID+" IN ("+ + buildSongIdFromGenreSelect(MediaLibrary.SongColumns.ALBUM_ID, MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS, songsQuery)+") "; } - if (table.equals(MediaLibrary.VIEW_ALBUMS_ARTISTS)) { - selection += MediaLibrary.AlbumColumns._ID+" IN ("+ buildSongIdFromGenreSelect(MediaLibrary.SongColumns.ALBUM_ID, songsQuery)+") "; + if (table.equals(MediaLibrary.VIEW_ARTISTS)) { + selection += MediaLibrary.ContributorColumns.ARTIST_ID+" IN ("+ + buildSongIdFromGenreSelect(MediaLibrary.ContributorColumns.ARTIST_ID, MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS, songsQuery)+") "; } + + if (table.equals(MediaLibrary.VIEW_ALBUMARTISTS)) { + selection += MediaLibrary.ContributorColumns.ALBUMARTIST_ID+" IN ("+ + buildSongIdFromGenreSelect(MediaLibrary.ContributorColumns.ALBUMARTIST_ID, MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS_HUGE, songsQuery)+") "; + Log.v("VanillaMusic", "+++ warning: huge genrequery for albumartist!"); + } + + if (table.equals(MediaLibrary.VIEW_COMPOSERS)) { + selection += MediaLibrary.ContributorColumns.COMPOSER_ID+" IN ("+ + buildSongIdFromGenreSelect(MediaLibrary.ContributorColumns.COMPOSER_ID, MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS_HUGE, songsQuery)+") "; + Log.v("VanillaMusic", "+++ warning: huge genrequery composer!"); + } + } } @@ -294,6 +325,26 @@ public class MediaLibraryBackend extends SQLiteOpenHelper { return cursor; } + /** + * Detects queries for artists, composers and albumartists and returns the + * role of the contributor. + * + * @param sql the raw sql query + * @return String[]{ sql-part, contributor-id, contributor-role } + */ + private String[] extractVirtualColumn(String sql) { + final Pattern[] pattern = new Pattern[]{ sQueryMatchArtistSearch, sQueryMatchComposerSearch, sQueryMatchAlbArtistSearch }; + final int[] roles = { MediaLibrary.ROLE_ARTIST, MediaLibrary.ROLE_COMPOSER, MediaLibrary.ROLE_ALBUMARTIST }; + + for (int i=0; i < roles.length; i++) { + Matcher matcher = pattern[i].matcher(sql); + if (matcher.matches()) { + return new String[]{ matcher.group(1), matcher.group(2), String.format("%d", roles[i]) }; + } + } + return null; + } + /** * Returns a select query to get all songs from a genre * @@ -309,11 +360,12 @@ public class MediaLibraryBackend extends SQLiteOpenHelper { * Returns a select query to get artists or albums from a genre * * @param target the target to query + * @param table the table to query * @param genreSelect the select string generated by buildSongIdFromGenreSelect * @return an SQL string */ - private String buildSongIdFromGenreSelect(String target, String genreSelect) { - return "SELECT "+target+" FROM "+MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS+" WHERE " + private String buildSongIdFromGenreSelect(String target, String table, String genreSelect) { + return "SELECT "+target+" FROM "+ table +" WHERE " +MediaLibrary.SongColumns._ID+" IN ("+genreSelect+") GROUP BY "+target; } diff --git a/src/ch/blinkenlights/android/medialibrary/MediaSchema.java b/src/ch/blinkenlights/android/medialibrary/MediaSchema.java index 6cbc8bc2..91d6ed8f 100644 --- a/src/ch/blinkenlights/android/medialibrary/MediaSchema.java +++ b/src/ch/blinkenlights/android/medialibrary/MediaSchema.java @@ -145,7 +145,22 @@ public class MediaSchema { +",_artist."+MediaLibrary.ContributorColumns._ID+" AS "+MediaLibrary.ContributorColumns.ARTIST_ID; /** - * View which includes song, album and artist information + * Additional columns to select for albumartist info + */ + private static final String VIEW_ALBUMARTIST_SELECT = "_albumartist."+MediaLibrary.ContributorColumns._CONTRIBUTOR+" AS "+MediaLibrary.ContributorColumns.ALBUMARTIST + +",_albumartist."+MediaLibrary.ContributorColumns._CONTRIBUTOR_SORT+" AS "+MediaLibrary.ContributorColumns.ALBUMARTIST_SORT + +",_albumartist."+MediaLibrary.ContributorColumns._ID+" AS "+MediaLibrary.ContributorColumns.ALBUMARTIST_ID; + + /** + * Additional columns to select for composer info + */ + private static final String VIEW_COMPOSER_SELECT = "_composer."+MediaLibrary.ContributorColumns._CONTRIBUTOR+" AS "+MediaLibrary.ContributorColumns.COMPOSER + +",_composer."+MediaLibrary.ContributorColumns._CONTRIBUTOR_SORT+" AS "+MediaLibrary.ContributorColumns.COMPOSER_SORT + +",_composer."+MediaLibrary.ContributorColumns._ID+" AS "+MediaLibrary.ContributorColumns.COMPOSER_ID; + + + /** + * View which includes song, album and artist information, enough for a filled song projection */ private static final String VIEW_CREATE_SONGS_ALBUMS_ARTISTS = "CREATE VIEW "+ MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS+ " AS " + "SELECT *, " + VIEW_ARTIST_SELECT + " FROM " + MediaLibrary.TABLE_SONGS @@ -155,6 +170,26 @@ public class MediaSchema { +" LEFT JOIN "+MediaLibrary.TABLE_CONTRIBUTORS+" AS _artist ON _artist."+MediaLibrary.ContributorColumns._ID+" = "+MediaLibrary.TABLE_CONTRIBUTORS_SONGS+"."+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID +" ;"; + /** + * View wich includes SONGS_ALBUMS_ARTISTS and any other contributors + * This view should only be used if needed as the SQL query is pretty expensive + */ + private static final String VIEW_CREATE_SONGS_ALBUMS_ARTISTS_HUGE = "CREATE VIEW "+ MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS_HUGE+" AS " + + "SELECT *, "+ VIEW_ALBUMARTIST_SELECT +", "+ VIEW_COMPOSER_SELECT +" FROM "+MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS + // albumartists + +" LEFT JOIN "+MediaLibrary.TABLE_CONTRIBUTORS_SONGS+" as __albumartists" + +" ON __albumartists."+MediaLibrary.ContributorSongColumns.ROLE+"="+MediaLibrary.ROLE_ALBUMARTIST + +" AND __albumartists."+MediaLibrary.ContributorSongColumns.SONG_ID+" = "+MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS+"."+MediaLibrary.SongColumns._ID + +" LEFT JOIN "+MediaLibrary.TABLE_CONTRIBUTORS+" AS _albumartist ON" + +" _albumartist."+MediaLibrary.ContributorColumns._ID+" = __albumartists."+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID + // composers + +" LEFT JOIN "+MediaLibrary.TABLE_CONTRIBUTORS_SONGS+" as __composers" + +" ON __composers."+MediaLibrary.ContributorSongColumns.ROLE+"="+MediaLibrary.ROLE_COMPOSER + +" AND __composers."+MediaLibrary.ContributorSongColumns.SONG_ID+" = "+MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS+"."+MediaLibrary.SongColumns._ID + +" LEFT JOIN "+MediaLibrary.TABLE_CONTRIBUTORS+" AS _composer ON" + +" _composer."+MediaLibrary.ContributorColumns._ID+" = __composers."+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID + +" ;"; + /** * View which includes album and artist information */ @@ -173,6 +208,24 @@ public class MediaSchema { +" WHERE "+MediaLibrary.ContributorSongColumns.ROLE+"="+MediaLibrary.ROLE_ARTIST+" GROUP BY "+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID+")" +" ;"; + /** + * View which includes albumArtists information + */ + private static final String VIEW_CREATE_ALBUMARTISTS = "CREATE VIEW "+ MediaLibrary.VIEW_ALBUMARTISTS+ " AS " + + "SELECT *, " + VIEW_ALBUMARTIST_SELECT + " FROM "+MediaLibrary.TABLE_CONTRIBUTORS+" AS _albumartist WHERE "+MediaLibrary.ContributorColumns._ID+" IN " + +" (SELECT "+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID+" FROM "+MediaLibrary.TABLE_CONTRIBUTORS_SONGS + +" WHERE "+MediaLibrary.ContributorSongColumns.ROLE+"="+MediaLibrary.ROLE_ALBUMARTIST+" GROUP BY "+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID+")" + +" ;"; + + /** + * View which includes composer information + */ + private static final String VIEW_CREATE_COMPOSERS = "CREATE VIEW "+ MediaLibrary.VIEW_COMPOSERS+ " AS " + + "SELECT *, " + VIEW_COMPOSER_SELECT + " FROM "+MediaLibrary.TABLE_CONTRIBUTORS+" AS _composer WHERE "+MediaLibrary.ContributorColumns._ID+" IN " + +" (SELECT "+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID+" FROM "+MediaLibrary.TABLE_CONTRIBUTORS_SONGS + +" WHERE "+MediaLibrary.ContributorSongColumns.ROLE+"="+MediaLibrary.ROLE_COMPOSER+" GROUP BY "+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID+")" + +" ;"; + /** * View like VIEW_CREATE_ARTISTS but includes playlist information */ @@ -204,8 +257,11 @@ public class MediaSchema { dbh.execSQL(INDEX_IDX_PLAYLIST_ID); dbh.execSQL(INDEX_IDX_PLAYLIST_ID_SONG); dbh.execSQL(VIEW_CREATE_SONGS_ALBUMS_ARTISTS); + dbh.execSQL(VIEW_CREATE_SONGS_ALBUMS_ARTISTS_HUGE); dbh.execSQL(VIEW_CREATE_ALBUMS_ARTISTS); dbh.execSQL(VIEW_CREATE_ARTISTS); + dbh.execSQL(VIEW_CREATE_ALBUMARTISTS); + dbh.execSQL(VIEW_CREATE_COMPOSERS); dbh.execSQL(VIEW_CREATE_PLAYLIST_SONGS); dbh.execSQL(DATABASE_CREATE_PREFERENCES); } @@ -240,6 +296,12 @@ public class MediaSchema { dbh.execSQL("UPDATE songs SET mtime=1 WHERE mtime=0"); } + if (oldVersion < 20170217) { + dbh.execSQL(VIEW_CREATE_ALBUMARTISTS); + dbh.execSQL(VIEW_CREATE_COMPOSERS); + dbh.execSQL(VIEW_CREATE_SONGS_ALBUMS_ARTISTS_HUGE); + } + } /** diff --git a/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java b/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java index e4d7907d..7aa5f80e 100644 --- a/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java +++ b/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java @@ -47,6 +47,7 @@ import android.widget.ListView; import android.widget.TextView; import android.widget.LinearLayout; import java.util.Arrays; +import java.util.ArrayList; /** * PagerAdapter that manages the library media ListViews. @@ -62,18 +63,25 @@ public class LibraryPagerAdapter * The number of unique list types. The number of visible lists may be * smaller. */ - public static final int MAX_ADAPTER_COUNT = 6; + public static final int MAX_ADAPTER_COUNT = MediaUtils.TYPE_COUNT; /** * The human-readable title for each list. The positions correspond to the * MediaUtils ids, so e.g. TITLES[MediaUtils.TYPE_SONG] = R.string.songs */ - public static final int[] TITLES = { R.string.artists, R.string.albums, R.string.songs, - R.string.playlists, R.string.genres, R.string.files }; + public static final int[] TITLES = { R.string.artists, R.string.albumartists, R.string.composers, + R.string.albums, R.string.songs, R.string.playlists, + R.string.genres, R.string.files }; /** * Default tab order. */ - public static final int[] DEFAULT_ORDER = { MediaUtils.TYPE_ARTIST, MediaUtils.TYPE_ALBUM, MediaUtils.TYPE_SONG, - MediaUtils.TYPE_PLAYLIST, MediaUtils.TYPE_GENRE, MediaUtils.TYPE_FILE }; + public static final int[] DEFAULT_TAB_ORDER = { MediaUtils.TYPE_ARTIST, MediaUtils.TYPE_ALBARTIST, MediaUtils.TYPE_COMPOSER, + MediaUtils.TYPE_ALBUM, MediaUtils.TYPE_SONG, MediaUtils.TYPE_PLAYLIST, + MediaUtils.TYPE_GENRE, MediaUtils.TYPE_FILE }; + /** + * The default visibility of tabs + */ + public static final boolean[] DEFAULT_TAB_VISIBILITY = { true, false, false, true, true, true, true, true }; + /** * The user-chosen tab order. */ @@ -99,6 +107,14 @@ public class LibraryPagerAdapter * The artist adapter instance, also stored at mAdapters[MediaUtils.TYPE_ARTIST]. */ private MediaAdapter mArtistAdapter; + /** + * The albumartist adapter instance, also stored at mAdapters[MediaUtils.TYPE_ALBART]. + */ + private MediaAdapter mAlbArtAdapter; + /** + * The composer adapter instance, also stored at mAdapters[MediaUtils.TYPE_COMPOSER]. + */ + private MediaAdapter mComposerAdapter; /** * The album adapter instance, also stored at mAdapters[MediaUtils.TYPE_ALBUM]. */ @@ -135,6 +151,14 @@ public class LibraryPagerAdapter * A limiter that should be set when the album adapter is created. */ private Limiter mPendingArtistLimiter; + /** + * A limiter that should be set when the albumartist adapter is created. + */ + private Limiter mPendingAlbArtLimiter; + /** + * A limiter that should be set when the composer adapter is created. + */ + private Limiter mPendingComposerLimiter; /** * A limiter that should be set when the album adapter is created. */ @@ -165,9 +189,10 @@ public class LibraryPagerAdapter * song limiters. */ private String mHeaderText; - private DraggableRow mArtistHeader; - private DraggableRow mAlbumHeader; - private DraggableRow mSongHeader; + /** + * A list of header rows which require test updates + */ + private ArrayList mHeaderViews = new ArrayList(); /** * The current filter text, or null if none. */ @@ -224,23 +249,18 @@ public class LibraryPagerAdapter public boolean loadTabOrder() { String in = PlaybackService.getSettings(mActivity).getString(PrefKeys.TAB_ORDER, PrefDefaults.TAB_ORDER); - int[] order; - int count; - if (in == null || in.length() != MAX_ADAPTER_COUNT) { - order = DEFAULT_ORDER; - count = MAX_ADAPTER_COUNT; - } else { + int[] order = new int[MAX_ADAPTER_COUNT]; + int count = 0; + if (in != null && in.length() == MAX_ADAPTER_COUNT) { char[] chars = in.toCharArray(); order = new int[MAX_ADAPTER_COUNT]; - count = 0; for (int i = 0; i != MAX_ADAPTER_COUNT; ++i) { char v = chars[i]; if (v >= 128) { v -= 128; if (v >= MediaUtils.TYPE_COUNT) { - // invalid media type; use default order - order = DEFAULT_ORDER; - count = MAX_ADAPTER_COUNT; + // invalid media type, ignore all data + count = 0; break; } order[count++] = v; @@ -248,6 +268,14 @@ public class LibraryPagerAdapter } } + // set default tabs if none were loaded + if (count == 0) { + for (int i=0; i != MAX_ADAPTER_COUNT; i++) { + if (DEFAULT_TAB_VISIBILITY[i]) + order[count++] = DEFAULT_TAB_ORDER[i]; + } + } + if (count != mTabCount || !Arrays.equals(order, mTabOrder)) { mTabOrder = order; mTabCount = count; @@ -316,18 +344,28 @@ public class LibraryPagerAdapter case MediaUtils.TYPE_ARTIST: adapter = mArtistAdapter = new MediaAdapter(activity, MediaUtils.TYPE_ARTIST, mPendingArtistLimiter, activity); mArtistAdapter.setExpandable(mSongsPosition != -1 || mAlbumsPosition != -1); - mArtistHeader = header = (DraggableRow)inflater.inflate(R.layout.draggable_row, null); + header = (DraggableRow)inflater.inflate(R.layout.draggable_row, null); + break; + case MediaUtils.TYPE_ALBARTIST: + adapter = mAlbArtAdapter = new MediaAdapter(activity, MediaUtils.TYPE_ALBARTIST, mPendingAlbArtLimiter, activity); + mAlbArtAdapter.setExpandable(mSongsPosition != -1 || mAlbumsPosition != -1); + header = (DraggableRow)inflater.inflate(R.layout.draggable_row, null); + break; + case MediaUtils.TYPE_COMPOSER: + adapter = mComposerAdapter = new MediaAdapter(activity, MediaUtils.TYPE_COMPOSER, mPendingComposerLimiter, activity); + mComposerAdapter.setExpandable(mSongsPosition != -1 || mAlbumsPosition != -1); + header = (DraggableRow)inflater.inflate(R.layout.draggable_row, null); break; case MediaUtils.TYPE_ALBUM: adapter = mAlbumAdapter = new MediaAdapter(activity, MediaUtils.TYPE_ALBUM, mPendingAlbumLimiter, activity); mAlbumAdapter.setExpandable(mSongsPosition != -1); mPendingAlbumLimiter = null; - mAlbumHeader = header = (DraggableRow)inflater.inflate(R.layout.draggable_row, null); + header = (DraggableRow)inflater.inflate(R.layout.draggable_row, null); break; case MediaUtils.TYPE_SONG: adapter = mSongAdapter = new MediaAdapter(activity, MediaUtils.TYPE_SONG, mPendingSongLimiter, activity); mPendingSongLimiter = null; - mSongHeader = header = (DraggableRow)inflater.inflate(R.layout.draggable_row, null); + header = (DraggableRow)inflater.inflate(R.layout.draggable_row, null); break; case MediaUtils.TYPE_PLAYLIST: adapter = mPlaylistAdapter = new MediaAdapter(activity, MediaUtils.TYPE_PLAYLIST, null, activity); @@ -353,6 +391,7 @@ public class LibraryPagerAdapter header.getTextView().setText(mHeaderText); header.setTag(new ViewHolder()); // behave like a normal library row view.addHeaderView(header); + mHeaderViews.add(header); } view.setAdapter(adapter); if (type != MediaUtils.TYPE_FILE) @@ -424,6 +463,8 @@ public class LibraryPagerAdapter { Bundle in = (Bundle)state; mPendingArtistLimiter = (Limiter)in.getSerializable("limiter_artists"); + mPendingAlbArtLimiter = (Limiter)in.getSerializable("limiter_albumartists"); + mPendingComposerLimiter = (Limiter)in.getSerializable("limiter_composer"); mPendingAlbumLimiter = (Limiter)in.getSerializable("limiter_albums"); mPendingSongLimiter = (Limiter)in.getSerializable("limiter_songs"); mPendingFileLimiter = (Limiter)in.getSerializable("limiter_files"); @@ -435,6 +476,10 @@ public class LibraryPagerAdapter Bundle out = new Bundle(10); if (mArtistAdapter != null) out.putSerializable("limiter_artists", mArtistAdapter.getLimiter()); + if (mAlbArtAdapter != null) + out.putSerializable("limiter_albumartists", mAlbArtAdapter.getLimiter()); + if (mComposerAdapter != null) + out.putSerializable("limiter_composer", mComposerAdapter.getLimiter()); if (mAlbumAdapter != null) out.putSerializable("limiter_albums", mAlbumAdapter.getLimiter()); if (mSongAdapter != null) @@ -452,12 +497,9 @@ public class LibraryPagerAdapter */ public void setHeaderText(String text) { - if (mArtistHeader != null) - mArtistHeader.getTextView().setText(text); - if (mAlbumHeader != null) - mAlbumHeader.getTextView().setText(text); - if (mSongHeader != null) - mSongHeader.getTextView().setText(text); + for(DraggableRow row : mHeaderViews) { + row.getTextView().setText(text); + } mHeaderText = text; } @@ -485,6 +527,20 @@ public class LibraryPagerAdapter loadSortOrder(mArtistAdapter); requestRequery(mArtistAdapter); } + if (mAlbArtAdapter == null) { + mPendingAlbArtLimiter = null; + } else { + mAlbArtAdapter.setLimiter(null); + loadSortOrder(mAlbArtAdapter); + requestRequery(mAlbArtAdapter); + } + if (mComposerAdapter == null) { + mPendingComposerLimiter = null; + } else { + mComposerAdapter.setLimiter(null); + loadSortOrder(mComposerAdapter); + requestRequery(mComposerAdapter); + } if (mAlbumAdapter == null) { mPendingAlbumLimiter = null; } else { @@ -526,6 +582,8 @@ public class LibraryPagerAdapter tab = mSongsPosition; break; case MediaUtils.TYPE_ARTIST: + case MediaUtils.TYPE_ALBARTIST: + case MediaUtils.TYPE_COMPOSER: if (mAlbumAdapter == null) { mPendingAlbumLimiter = limiter; } else { @@ -552,6 +610,20 @@ public class LibraryPagerAdapter loadSortOrder(mArtistAdapter); requestRequery(mArtistAdapter); } + if (mAlbArtAdapter == null) { + mPendingAlbArtLimiter = limiter; + } else { + mAlbArtAdapter.setLimiter(limiter); + loadSortOrder(mAlbArtAdapter); + requestRequery(mAlbArtAdapter); + } + if (mComposerAdapter == null) { + mPendingComposerLimiter = limiter; + } else { + mComposerAdapter.setLimiter(limiter); + loadSortOrder(mComposerAdapter); + requestRequery(mComposerAdapter); + } if (mAlbumAdapter == null) { mPendingAlbumLimiter = limiter; } else { diff --git a/src/ch/blinkenlights/android/vanilla/MediaAdapter.java b/src/ch/blinkenlights/android/vanilla/MediaAdapter.java index bcaaa68d..5a3be6d3 100644 --- a/src/ch/blinkenlights/android/vanilla/MediaAdapter.java +++ b/src/ch/blinkenlights/android/vanilla/MediaAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2016 Adrian Ulrich + * Copyright (C) 2015-2017 Adrian Ulrich * Copyright (C) 2010, 2011 Christopher Eby * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -189,6 +189,20 @@ public class MediaAdapter mSortEntries = new int[] { R.string.title, R.string.date_added }; mAdapterSortValues = new String[] { MediaLibrary.ContributorColumns.ARTIST_SORT+" %1$s", MediaLibrary.ContributorColumns.MTIME+" %1$s" }; break; + case MediaUtils.TYPE_ALBARTIST: + mSource = MediaLibrary.VIEW_ALBUMARTISTS; + mFields = new String[] { MediaLibrary.ContributorColumns.ALBUMARTIST }; + mFieldKeys = new String[] { MediaLibrary.ContributorColumns.ALBUMARTIST_SORT }; + mSortEntries = new int[] { R.string.title, R.string.date_added }; + mAdapterSortValues = new String[] { MediaLibrary.ContributorColumns.ALBUMARTIST_SORT+" %1$s", MediaLibrary.ContributorColumns.MTIME+" %1$s" }; + break; + case MediaUtils.TYPE_COMPOSER: + mSource = MediaLibrary.VIEW_COMPOSERS; + mFields = new String[] { MediaLibrary.ContributorColumns.COMPOSER }; + mFieldKeys = new String[] { MediaLibrary.ContributorColumns.COMPOSER_SORT }; + mSortEntries = new int[] { R.string.title, R.string.date_added }; + mAdapterSortValues = new String[] { MediaLibrary.ContributorColumns.COMPOSER_SORT+" %1$s", MediaLibrary.ContributorColumns.MTIME+" %1$s" }; + break; case MediaUtils.TYPE_ALBUM: mSource = MediaLibrary.VIEW_ALBUMS_ARTISTS; mFields = new String[] { MediaLibrary.AlbumColumns.ALBUM, MediaLibrary.ContributorColumns.ARTIST }; @@ -308,7 +322,7 @@ public class MediaAdapter String sortRaw = mAdapterSortValues[mode]; if (returnSongs) { // songs returned from the artist tab should also sort by album - if (mType == MediaUtils.TYPE_ARTIST) + if (mType == MediaUtils.TYPE_ARTIST) // fixme: composer? sortRaw += ", "+MediaLibrary.AlbumColumns.ALBUM_SORT+" %1$s"; // and this is for all types: sortRaw += ", "+MediaLibrary.SongColumns.DISC_NUMBER+", "+MediaLibrary.SongColumns.SONG_NUMBER; @@ -364,7 +378,7 @@ public class MediaAdapter } if (returnSongs == true) { - source = MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS; + source = MediaLibrary.VIEW_SONGS_ALBUMS_ARTISTS_HUGE; } else { enrichedProjection = Arrays.copyOf(projection, projection.length + 1); enrichedProjection[projection.length] = getFirstSortColumn(); @@ -443,6 +457,14 @@ public class MediaAdapter fields = new String[] { cursor.getString(2) }; data = String.format("%s=%d", MediaLibrary.ContributorColumns.ARTIST_ID, id); break; + case MediaUtils.TYPE_ALBARTIST: + fields = new String[] { cursor.getString(2) }; + data = String.format("%s=%d", MediaLibrary.ContributorColumns.ALBUMARTIST_ID, id); + break; + case MediaUtils.TYPE_COMPOSER: + fields = new String[] { cursor.getString(2) }; + data = String.format("%s=%d", MediaLibrary.ContributorColumns.COMPOSER_ID, id); + break; case MediaUtils.TYPE_ALBUM: fields = new String[] { cursor.getString(3), cursor.getString(2) }; data = String.format("%s=%d", MediaLibrary.SongColumns.ALBUM_ID, id); diff --git a/src/ch/blinkenlights/android/vanilla/MediaUtils.java b/src/ch/blinkenlights/android/vanilla/MediaUtils.java index 599b7d7a..c79e665f 100644 --- a/src/ch/blinkenlights/android/vanilla/MediaUtils.java +++ b/src/ch/blinkenlights/android/vanilla/MediaUtils.java @@ -62,31 +62,39 @@ public class MediaUtils { * Type indicating an id represents an artist. */ public static final int TYPE_ARTIST = 0; + /** + * Type indicating an id represents an albumartist + */ + public static final int TYPE_ALBARTIST = 1; + /** + * Type indicating an id represents a composer + */ + public static final int TYPE_COMPOSER = 2; /** * Type indicating an id represents an album. */ - public static final int TYPE_ALBUM = 1; + public static final int TYPE_ALBUM = 3; /** * Type indicating an id represents a song. */ - public static final int TYPE_SONG = 2; + public static final int TYPE_SONG = 4; /** * Type indicating an id represents a playlist. */ - public static final int TYPE_PLAYLIST = 3; + public static final int TYPE_PLAYLIST = 5; /** * Type indicating ids represent genres. */ - public static final int TYPE_GENRE = 4; + public static final int TYPE_GENRE = 6; /** * Special type for files and folders. Most methods do not accept this type * since files have no MediaStore id and require special handling. */ - public static final int TYPE_FILE = 5; + public static final int TYPE_FILE = 7; /** * The number of different valid media types. */ - public static final int TYPE_COUNT = 6; + public static final int TYPE_COUNT = 8; /** * The default sort order for media queries. First artist, then album, then @@ -157,6 +165,12 @@ public class MediaUtils { case TYPE_ARTIST: selection.append(MediaLibrary.ContributorColumns.ARTIST_ID); break; + case TYPE_ALBARTIST: + selection.append(MediaLibrary.ContributorColumns.ALBUMARTIST_ID); + break; + case TYPE_COMPOSER: + selection.append(MediaLibrary.ContributorColumns.COMPOSER_ID); + break; case TYPE_ALBUM: selection.append(MediaLibrary.SongColumns.ALBUM_ID); sort = ALBUM_SORT; @@ -206,6 +220,8 @@ public class MediaUtils { { switch (type) { case TYPE_ARTIST: + case TYPE_ALBARTIST: + case TYPE_COMPOSER: case TYPE_ALBUM: case TYPE_SONG: case TYPE_GENRE: diff --git a/src/ch/blinkenlights/android/vanilla/TabOrderActivity.java b/src/ch/blinkenlights/android/vanilla/TabOrderActivity.java index 702a850b..5a957cea 100644 --- a/src/ch/blinkenlights/android/vanilla/TabOrderActivity.java +++ b/src/ch/blinkenlights/android/vanilla/TabOrderActivity.java @@ -95,10 +95,10 @@ public class TabOrderActivity extends Activity */ public void restoreDefault() { - mAdapter.setTabIds(LibraryPagerAdapter.DEFAULT_ORDER.clone()); + mAdapter.setTabIds(LibraryPagerAdapter.DEFAULT_TAB_ORDER.clone()); DragSortListView list = mList; for (int i = 0; i != LibraryPagerAdapter.MAX_ADAPTER_COUNT; ++i) { - list.setItemChecked(i, true); + list.setItemChecked(i, LibraryPagerAdapter.DEFAULT_TAB_VISIBILITY[i]); } save(); }