From eedd1e99b4ee6ab2fe513cdf460e134d2416e46c Mon Sep 17 00:00:00 2001 From: Martijn Brekhof Date: Thu, 20 Oct 2016 20:26:43 +0200 Subject: [PATCH] Implemented showing artist names in song lists (#281) * Song lists for artist, album, and overall now shows the artist name for each individual song * Added display artist column to songs table. This required a DB upgrade and users should do a refresh for music items to be able to see the display artist in the song lists. --- .../mediaprovider/MediaProviderMusicTest.java | 3 +- .../org/xbmc/kore/provider/MediaContract.java | 2 +- .../org/xbmc/kore/provider/MediaDatabase.java | 18 +++++++-- .../org/xbmc/kore/provider/MediaProvider.java | 39 +++++++++++++++---- .../xbmc/kore/service/library/SyncMusic.java | 3 +- .../xbmc/kore/service/library/SyncUtils.java | 1 + .../xbmc/kore/ui/AlbumDetailsFragment.java | 27 ++++++++----- .../org/xbmc/kore/ui/SongsListFragment.java | 38 ++++++++---------- app/src/main/res/layout/list_item_song.xml | 10 ++--- 9 files changed, 92 insertions(+), 49 deletions(-) diff --git a/app/src/androidTest/java/org/xbmc/kore/tests/mediaprovider/MediaProviderMusicTest.java b/app/src/androidTest/java/org/xbmc/kore/tests/mediaprovider/MediaProviderMusicTest.java index 868213f..d5d20ce 100644 --- a/app/src/androidTest/java/org/xbmc/kore/tests/mediaprovider/MediaProviderMusicTest.java +++ b/app/src/androidTest/java/org/xbmc/kore/tests/mediaprovider/MediaProviderMusicTest.java @@ -31,6 +31,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.xbmc.kore.host.HostInfo; import org.xbmc.kore.provider.MediaContract; +import org.xbmc.kore.provider.MediaProvider; import org.xbmc.kore.testhelpers.Database; import org.xbmc.kore.testhelpers.TestUtils; import org.xbmc.kore.testhelpers.Utils; @@ -161,7 +162,7 @@ public class MediaProviderMusicTest { public void queryAlbumSongsTest() { Uri uri = MediaContract.Songs.buildAlbumSongsListUri(hostInfo.getId(), TestValues.Album.albumId); - Cursor cursor = contentResolver.query(uri, new String[] {MediaContract.Songs.SONGID}, null, null, null); + Cursor cursor = contentResolver.query(uri, new String[] {MediaProvider.Qualified.SONGS_SONGID}, null, null, null); assertNotNull(cursor); assertEquals("cursor size ", 17, cursor.getCount()); diff --git a/app/src/main/java/org/xbmc/kore/provider/MediaContract.java b/app/src/main/java/org/xbmc/kore/provider/MediaContract.java index f12cf86..8acfa2d 100644 --- a/app/src/main/java/org/xbmc/kore/provider/MediaContract.java +++ b/app/src/main/java/org/xbmc/kore/provider/MediaContract.java @@ -571,7 +571,7 @@ public class MediaContract { String HOST_ID = "host_id"; String ALBUMID = "albumid"; String SONGID = "songid"; - + String DISPLAYARTIST = "displayartist"; String DURATION = "duration"; String THUMBNAIL = "thumbnail"; String FILE = "file"; diff --git a/app/src/main/java/org/xbmc/kore/provider/MediaDatabase.java b/app/src/main/java/org/xbmc/kore/provider/MediaDatabase.java index fa127c1..df9ed2a 100644 --- a/app/src/main/java/org/xbmc/kore/provider/MediaDatabase.java +++ b/app/src/main/java/org/xbmc/kore/provider/MediaDatabase.java @@ -32,7 +32,9 @@ public class MediaDatabase extends SQLiteOpenHelper { private static final String DB_NAME = "xbmc.sqlite"; private static final int DB_VERSION_PRE_EVENT_SERVER = 4, - DB_VERSION_PRE_SONG_ARTISTS = 5, DB_VERSION = 6; + DB_VERSION_PRE_SONG_ARTISTS = 5, + DB_VERSION_PRE_SONG_DISPLAY_ARTIST = 6, + DB_VERSION = 7; /** * Tables exposed @@ -94,7 +96,7 @@ public class MediaDatabase extends SQLiteOpenHelper { * Join to get Songs for an Artist or Album with artist info and album info only if available */ String SONGS_FOR_ARTIST_AND_OR_ALBUM_JOIN = - SONG_ARTISTS + " JOIN " + SONGS + " ON " + + SONGS + " JOIN " + SONG_ARTISTS + " ON " + SONG_ARTISTS + "." + MediaContract.SongArtists.HOST_ID + "=" + SONGS + "." + MediaContract.Songs.HOST_ID + " AND " + SONG_ARTISTS + "." + MediaContract.SongArtists.SONGID + "=" + SONGS + "." + MediaContract.Songs.SONGID + @@ -102,10 +104,14 @@ public class MediaDatabase extends SQLiteOpenHelper { SONG_ARTISTS + "." + MediaContract.SongArtists.HOST_ID + "=" + ARTISTS + "." + MediaContract.Artists.HOST_ID + " AND " + SONG_ARTISTS + "." + MediaContract.SongArtists.ARTISTID + "=" + ARTISTS + "." + MediaContract.Artists.ARTISTID + + " LEFT JOIN " + ALBUM_ARTISTS + " ON " + + SONG_ARTISTS + "." + MediaContract.SongArtists.HOST_ID + "=" + ALBUM_ARTISTS + "." + MediaContract.AlbumArtists.HOST_ID + + " AND " + + SONGS + "." + MediaContract.Songs.ALBUMID + "=" + ALBUM_ARTISTS + "." + MediaContract.AlbumArtists.ALBUMID + " LEFT JOIN " + ALBUMS + " ON " + SONG_ARTISTS + "." + MediaContract.SongArtists.HOST_ID + "=" + ALBUMS + "." + MediaContract.Albums.HOST_ID + " AND " + - SONGS + "." + MediaContract.Songs.ALBUMID + "=" + ALBUMS + "." + MediaContract.Albums.ALBUMID; + ALBUM_ARTISTS + "." + MediaContract.AlbumArtists.ALBUMID + "=" + ALBUMS + "." + MediaContract.Albums.ALBUMID; } @@ -355,6 +361,7 @@ public class MediaDatabase extends SQLiteOpenHelper { MediaContract.SongsColumns.FILE + " TEXT, " + MediaContract.SongsColumns.TRACK + " INTEGER, " + MediaContract.SongsColumns.TITLE + " TEXT, " + + MediaContract.SongsColumns.DISPLAYARTIST + " TEXT, " + "UNIQUE (" + MediaContract.SongsColumns.HOST_ID + ", " + MediaContract.SongsColumns.ALBUMID + ", " + @@ -452,6 +459,7 @@ public class MediaDatabase extends SQLiteOpenHelper { db.execSQL(buildHostsDeleteTrigger(Tables.SONGS, MediaContract.SongsColumns.HOST_ID)); db.execSQL(buildHostsDeleteTrigger(Tables.AUDIO_GENRES, MediaContract.AudioGenresColumns.HOST_ID)); db.execSQL(buildHostsDeleteTrigger(Tables.ALBUM_ARTISTS, MediaContract.AlbumArtistsColumns.HOST_ID)); + db.execSQL(buildHostsDeleteTrigger(Tables.SONG_ARTISTS, MediaContract.SongArtistsColumns.HOST_ID)); db.execSQL(buildHostsDeleteTrigger(Tables.ALBUM_GENRES, MediaContract.AlbumGenresColumns.HOST_ID)); db.execSQL(buildHostsDeleteTrigger(Tables.MUSIC_VIDEOS, MediaContract.MusicVideosColumns.HOST_ID)); @@ -478,6 +486,10 @@ public class MediaDatabase extends SQLiteOpenHelper { " INTEGER DEFAULT " + HostInfo.DEFAULT_EVENT_SERVER_PORT + ";"); case DB_VERSION_PRE_SONG_ARTISTS: createSongArtistsTable(db); + case DB_VERSION_PRE_SONG_DISPLAY_ARTIST: + db.execSQL("ALTER TABLE " + Tables.SONGS + + " ADD COLUMN " + MediaContract.SongsColumns.DISPLAYARTIST + + " TEXT;"); } } diff --git a/app/src/main/java/org/xbmc/kore/provider/MediaProvider.java b/app/src/main/java/org/xbmc/kore/provider/MediaProvider.java index 385ba21..59ad46a 100644 --- a/app/src/main/java/org/xbmc/kore/provider/MediaProvider.java +++ b/app/src/main/java/org/xbmc/kore/provider/MediaProvider.java @@ -23,7 +23,6 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.provider.BaseColumns; -import android.support.annotation.Nullable; import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.SelectionBuilder; @@ -671,8 +670,9 @@ public class MediaProvider extends ContentProvider { .mapToTable(MediaContract.Songs.SONGID, MediaDatabase.Tables.SONGS) .mapToTable(MediaContract.Songs.TITLE, MediaDatabase.Tables.SONGS) .mapToTable(MediaContract.Songs.ALBUMID, MediaDatabase.Tables.SONGS) - .mapToTable(MediaContract.Songs.UPDATED, MediaDatabase.Tables.SONGS) .mapToTable(MediaContract.Songs.THUMBNAIL, MediaDatabase.Tables.SONGS) + .mapToTable(MediaContract.Songs.DISPLAYARTIST, MediaDatabase.Tables.SONGS) + .mapToTable(MediaContract.AlbumArtists.ARTISTID, MediaDatabase.Tables.ALBUM_ARTISTS) .mapToTable(MediaContract.SongArtists.ARTISTID, MediaDatabase.Tables.SONG_ARTISTS) .where(Qualified.SONGS_HOST_ID + "=?", hostId) .groupBy(Qualified.SONGS_SONGID); @@ -681,8 +681,8 @@ public class MediaProvider extends ContentProvider { final String hostId = MediaContract.Hosts.getHostId(uri); final String albumId = MediaContract.Albums.getAlbumId(uri); return builder.table(MediaDatabase.Tables.SONGS) - .where(MediaContract.Songs.HOST_ID + "=?", hostId) - .where(MediaContract.Songs.ALBUMID + "=?", albumId); + .where(Qualified.SONGS_HOST_ID + "=?", hostId) + .where(Qualified.SONGS_ALBUMID + "=?", albumId); } case SONGS_ID: { final String hostId = MediaContract.Hosts.getHostId(uri); @@ -737,11 +737,14 @@ public class MediaProvider extends ContentProvider { .mapToTable(MediaContract.Songs.SONGID, MediaDatabase.Tables.SONGS) .mapToTable(MediaContract.Songs.TITLE, MediaDatabase.Tables.SONGS) .mapToTable(MediaContract.Songs.ALBUMID, MediaDatabase.Tables.SONGS) - .mapToTable(MediaContract.Songs.UPDATED, MediaDatabase.Tables.SONGS) - .mapToTable(MediaContract.Songs.THUMBNAIL, MediaDatabase.Tables.SONGS) + .mapToTable(MediaContract.Songs.DISPLAYARTIST, MediaDatabase.Tables.SONGS) + .mapToTable(MediaContract.AlbumArtists.ARTISTID, MediaDatabase.Tables.ALBUM_ARTISTS) .mapToTable(MediaContract.SongArtists.ARTISTID, MediaDatabase.Tables.SONG_ARTISTS) .where(Qualified.SONG_ARTISTS_HOST_ID + "=?", hostId) - .where(Qualified.SONG_ARTISTS_ARTISTID + "=?", artistId); + .where(Qualified.SONG_ARTISTS_ARTISTID + "=?" + + " OR " + + Qualified.ALBUM_ARTISTS_ARTISTID + "=?", artistId, artistId) + .groupBy(Qualified.SONGS_ID); } case ALBUM_ARTISTS_LIST: { // Artists for Album @@ -806,6 +809,14 @@ public class MediaProvider extends ContentProvider { * parent {@link MediaDatabase.Tables}. Used when needed to work around SQL ambiguity. */ public interface Qualified { + String ALBUMS_TITLE = + MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.TITLE; + String ALBUMS_GENRE = + MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.GENRE; + String ALBUMS_YEAR = + MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.YEAR; + String ALBUMS_THUMBNAIL = + MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.THUMBNAIL; String ALBUM_ARTISTS_HOST_ID = MediaDatabase.Tables.ALBUM_ARTISTS + "." + MediaContract.AlbumArtists.HOST_ID; String ALBUM_ARTISTS_ARTISTID = @@ -818,10 +829,24 @@ public class MediaProvider extends ContentProvider { MediaDatabase.Tables.ALBUM_GENRES + "." + MediaContract.AlbumGenres.GENREID; String ALBUM_GENRES_ALBUMID = MediaDatabase.Tables.ALBUM_GENRES + "." + MediaContract.AlbumGenres.ALBUMID; + String SONGS_ID = + MediaDatabase.Tables.SONGS + "." + MediaContract.Songs._ID; + String SONGS_TRACK = + MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.TRACK; + String SONGS_DURATION = + MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.DURATION; + String SONGS_FILE = + MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.FILE; String SONGS_HOST_ID = MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.HOST_ID; String SONGS_SONGID = MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.SONGID; + String SONGS_DISPLAYARTIST = + MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.DISPLAYARTIST; + String SONGS_TITLE = + MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.TITLE; + String SONGS_ALBUMID = + MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.ALBUMID; String SONG_ARTISTS_HOST_ID = MediaDatabase.Tables.SONG_ARTISTS + "." + MediaContract.SongArtists.HOST_ID; String SONG_ARTISTS_ARTISTID = diff --git a/app/src/main/java/org/xbmc/kore/service/library/SyncMusic.java b/app/src/main/java/org/xbmc/kore/service/library/SyncMusic.java index 1f2e247..345a42e 100644 --- a/app/src/main/java/org/xbmc/kore/service/library/SyncMusic.java +++ b/app/src/main/java/org/xbmc/kore/service/library/SyncMusic.java @@ -272,7 +272,8 @@ public class SyncMusic extends SyncItem { //AudioType.FieldsSong.LASTPLAYED, AudioType.FieldsSong.DISC, //AudioType.FieldsSong.GENREID, AudioType.FieldsSong.ARTISTID, - //AudioType.FieldsSong.DISPLAYARTIST, AudioType.FieldsSong.ALBUMARTISTID +// AudioType.FieldsSong.ALBUMARTISTID, + AudioType.FieldsSong.DISPLAYARTIST }; /** diff --git a/app/src/main/java/org/xbmc/kore/service/library/SyncUtils.java b/app/src/main/java/org/xbmc/kore/service/library/SyncUtils.java index 32985ea..f36abbf 100644 --- a/app/src/main/java/org/xbmc/kore/service/library/SyncUtils.java +++ b/app/src/main/java/org/xbmc/kore/service/library/SyncUtils.java @@ -336,6 +336,7 @@ public class SyncUtils { songValues.put(MediaContract.Songs.FILE, song.file); songValues.put(MediaContract.Songs.TRACK, song.track); songValues.put(MediaContract.Songs.TITLE, song.title); + songValues.put(MediaContract.Songs.DISPLAYARTIST, song.displayartist); return songValues; } diff --git a/app/src/main/java/org/xbmc/kore/ui/AlbumDetailsFragment.java b/app/src/main/java/org/xbmc/kore/ui/AlbumDetailsFragment.java index 6304df8..29e8110 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AlbumDetailsFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AlbumDetailsFragment.java @@ -60,6 +60,7 @@ import org.xbmc.kore.jsonrpc.method.Player; import org.xbmc.kore.jsonrpc.method.Playlist; import org.xbmc.kore.jsonrpc.type.PlaylistType; import org.xbmc.kore.provider.MediaContract; +import org.xbmc.kore.provider.MediaDatabase; import org.xbmc.kore.utils.FileDownloadHelper; import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.UIUtils; @@ -651,7 +652,7 @@ public class AlbumDetailsFragment extends AbstractDetailsFragment .inflate(R.layout.list_item_song, songListView, false); TextView songTitle = (TextView)songView.findViewById(R.id.song_title); TextView trackNumber = (TextView)songView.findViewById(R.id.track_number); - TextView duration = (TextView)songView.findViewById(R.id.duration); + TextView details = (TextView)songView.findViewById(R.id.details); ImageView contextMenu = (ImageView)songView.findViewById(R.id.list_context_menu); // Add this song to the list @@ -665,8 +666,14 @@ public class AlbumDetailsFragment extends AbstractDetailsFragment songInfoList.add(songInfo); songTitle.setText(songInfo.title); + + trackNumber.setText(String.valueOf(songInfo.track)); - duration.setText(UIUtils.formatTime(cursor.getInt(AlbumSongsListQuery.DURATION))); + + String artist = cursor.getString(AlbumSongsListQuery.ARTIST); + String duration = UIUtils.formatTime(cursor.getInt(AlbumSongsListQuery.DURATION)); + String detailsText = TextUtils.isEmpty(artist) ? duration : duration + " | " + artist; + details.setText(detailsText); contextMenu.setTag(songInfo); contextMenu.setOnClickListener(songItemMenuClickListener); @@ -737,7 +744,7 @@ public class AlbumDetailsFragment extends AbstractDetailsFragment } /** - * Movie cast list query parameters. + * Album songs list query parameters. */ public interface AlbumSongsListQuery { String[] PROJECTION = { @@ -747,15 +754,17 @@ public class AlbumDetailsFragment extends AbstractDetailsFragment MediaContract.Songs.DURATION, MediaContract.Songs.FILE, MediaContract.Songs.SONGID, + MediaContract.Songs.DISPLAYARTIST }; String SORT = MediaContract.Songs.TRACK + " ASC"; - final int ID = 0; - final int TITLE = 1; - final int TRACK = 2; - final int DURATION = 3; - final int FILE = 4; - final int SONGID = 5; + int ID = 0; + int TITLE = 1; + int TRACK = 2; + int DURATION = 3; + int FILE = 4; + int SONGID = 5; + int ARTIST = 6; } } diff --git a/app/src/main/java/org/xbmc/kore/ui/SongsListFragment.java b/app/src/main/java/org/xbmc/kore/ui/SongsListFragment.java index 4e0cf09..d1b1ee8 100644 --- a/app/src/main/java/org/xbmc/kore/ui/SongsListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/SongsListFragment.java @@ -44,6 +44,7 @@ import org.xbmc.kore.host.HostManager; import org.xbmc.kore.jsonrpc.type.PlaylistType; import org.xbmc.kore.provider.MediaContract; import org.xbmc.kore.provider.MediaDatabase; +import org.xbmc.kore.provider.MediaProvider; import org.xbmc.kore.service.library.LibrarySyncService; import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.MediaPlayerUtils; @@ -136,23 +137,20 @@ public class SongsListFragment extends AbstractCursorListFragment { */ public interface SongsListQuery { String[] PROJECTION = { - MediaDatabase.Tables.SONGS + "." + BaseColumns._ID, - MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.TITLE, - MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.TRACK, - MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.DURATION, - MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.FILE, - MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.SONGID, - MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.TITLE, - MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.DISPLAYARTIST, - MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.GENRE, - MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.YEAR, - MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.THUMBNAIL, - MediaDatabase.Tables.ARTISTS + "." + MediaContract.Artists.ARTIST + MediaProvider.Qualified.SONGS_ID, + MediaProvider.Qualified.SONGS_TITLE, + MediaProvider.Qualified.SONGS_TRACK, + MediaProvider.Qualified.SONGS_DURATION, + MediaProvider.Qualified.SONGS_FILE, + MediaProvider.Qualified.SONGS_SONGID, + MediaProvider.Qualified.SONGS_DISPLAYARTIST, + MediaProvider.Qualified.ALBUMS_TITLE, + MediaProvider.Qualified.ALBUMS_GENRE, + MediaProvider.Qualified.ALBUMS_YEAR, + MediaProvider.Qualified.ALBUMS_THUMBNAIL }; - String SORT = MediaDatabase.sortCommonTokens(MediaDatabase.Tables.SONGS - + "." + - MediaContract.Songs.TITLE) + " ASC"; + String SORT = MediaDatabase.sortCommonTokens(MediaProvider.Qualified.SONGS_TITLE) + " ASC"; int ID = 0; int TITLE = 1; @@ -160,12 +158,11 @@ public class SongsListFragment extends AbstractCursorListFragment { int DURATION = 3; int FILE = 4; int SONGID = 5; - int ALBUMTITLE = 6; - int ALBUMARTIST = 7; + int SONGARTIST = 6; + int ALBUMTITLE = 7; int GENRE = 8; int YEAR = 9; int THUMBNAIL = 10; - int ARTIST = 11; } private class SongsAdapter extends CursorAdapter { @@ -213,10 +210,7 @@ public class SongsListFragment extends AbstractCursorListFragment { viewHolder.title.setText(title); - String artist = cursor.getString(SongsListQuery.ALBUMARTIST); - if (TextUtils.isEmpty(artist)) - artist = cursor.getString(SongsListQuery.ARTIST); - + String artist = cursor.getString(SongsListQuery.SONGARTIST); viewHolder.artist.setText(artist); int year = cursor.getInt(SongsListQuery.YEAR); diff --git a/app/src/main/res/layout/list_item_song.xml b/app/src/main/res/layout/list_item_song.xml index a71e058..a841e2f 100644 --- a/app/src/main/res/layout/list_item_song.xml +++ b/app/src/main/res/layout/list_item_song.xml @@ -16,10 +16,10 @@ --> + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground">