Fixed issue showing songs without an album or artist

This commit is contained in:
Martijn Brekhof 2016-06-09 21:57:29 +02:00
parent e326da03d7
commit 21389b1e9e
8 changed files with 202 additions and 114 deletions

View File

@ -41,6 +41,7 @@ public class MediaContract {
public static final String PATH_ALBUMS = "albums";
public static final String PATH_AUDIO_GENRES = "audio_genres";
public static final String PATH_SONGS = "songs";
public static final String PATH_SONG_ARTISTS = "song_artists";
public static final String PATH_ALBUM_ARTISTS = "album_artists";
public static final String PATH_ALBUM_GENRES = "album_genres";
public static final String PATH_MUSIC_VIDEOS = "music_videos";
@ -701,6 +702,36 @@ public class MediaContract {
};
}
/**
* Columns for SongArtists table
* All Other IDs refer to XBMC Ids, not Internal ones
*/
public interface SongArtistsColumns {
String HOST_ID = "host_id";
String SONGID = "songid";
String ARTISTID = "artistid";
}
public static class SongArtists implements BaseColumns, SongArtistsColumns {
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_SONG_ARTISTS).build();
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.org.xbmc." + PATH_SONG_ARTISTS;
/** Build {@link Uri} for requested {@link #_ID}. */
public static Uri buildSongsForArtistListUri(long hostId, long artistId) {
return Hosts.buildHostUri(hostId).buildUpon()
.appendPath(PATH_ARTISTS)
.appendPath(String.valueOf(artistId))
.appendPath(PATH_SONGS)
.build();
}
public final static String[] ALL_COLUMNS = {
_ID, HOST_ID, SONGID, ARTISTID,
};
}
/**
* Columns for AlbumGenres table
* All Other IDs refer to XBMC Ids, not Internal ones

View File

@ -32,7 +32,7 @@ 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 = 5;
DB_VERSION_PRE_SONG_ARTISTS = 5, DB_VERSION = 6;
/**
* Tables exposed
@ -48,6 +48,7 @@ public class MediaDatabase extends SQLiteOpenHelper {
String ARTISTS = "artists";
String ALBUMS = "albums";
String SONGS = "songs";
String SONG_ARTISTS = "song_artists";
String AUDIO_GENRES = "audio_genres";
String ALBUM_ARTISTS = "album_artists";
String ALBUM_GENRES = "album_genres";
@ -90,25 +91,25 @@ public class MediaDatabase extends SQLiteOpenHelper {
ALBUM_GENRES + "." + MediaContract.AlbumGenres.GENREID + "=" + AUDIO_GENRES + "." + MediaContract.AudioGenres.GENREID;
/**
* Join to get Songs for an Artist
* Join to get Songs for an Artist or Album with artist info and album info only if available
*/
String SONGS_FOR_ARTIST_JOIN =
SONGS + " JOIN " + ALBUM_ARTISTS + " ON " +
SONGS + "." + MediaContract.Songs.HOST_ID + "=" + ALBUM_ARTISTS + "." + MediaContract.AlbumArtists.HOST_ID +
String SONGS_FOR_ARTIST_AND_OR_ALBUM_JOIN =
SONG_ARTISTS + " JOIN " + SONGS + " ON " +
SONG_ARTISTS + "." + MediaContract.SongArtists.HOST_ID + "=" + SONGS + "." + MediaContract.Songs.HOST_ID +
" AND " +
SONGS + "." + MediaContract.Songs.ALBUMID + "=" + ALBUM_ARTISTS + "." + MediaContract.AlbumArtists.ALBUMID +
" JOIN " + ALBUMS + " ON " +
SONGS + "." + MediaContract.Songs.HOST_ID + "=" + ALBUMS + "." + MediaContract.Albums.HOST_ID +
SONG_ARTISTS + "." + MediaContract.SongArtists.SONGID + "=" + SONGS + "." + MediaContract.Songs.SONGID +
" LEFT JOIN " + ARTISTS + " ON " +
SONG_ARTISTS + "." + MediaContract.SongArtists.HOST_ID + "=" + ARTISTS + "." + MediaContract.Artists.HOST_ID +
" AND " +
SONGS + "." + MediaContract.Songs.ALBUMID + "=" + ALBUMS + "." + MediaContract.Albums.ALBUMID;
String SONGS_AND_ALBUM_JOIN =
SONGS + " JOIN " + ALBUMS + " ON " +
SONGS + "." + MediaContract.Songs.HOST_ID + "=" + ALBUMS + "." + MediaContract.Albums.HOST_ID +
SONG_ARTISTS + "." + MediaContract.SongArtists.ARTISTID + "=" + ARTISTS + "." + MediaContract.Artists.ARTISTID +
" LEFT JOIN " + ALBUMS + " ON " +
SONG_ARTISTS + "." + MediaContract.SongArtists.HOST_ID + "=" + ALBUMS + "." + MediaContract.Albums.HOST_ID +
" AND " +
SONGS + "." + MediaContract.Songs.ALBUMID + "=" + ALBUMS + "." + MediaContract.Albums.ALBUMID;
}
private interface References {
String HOST_ID =
"REFERENCES " + Tables.HOSTS + "(" + BaseColumns._ID + ")";
@ -118,6 +119,8 @@ public class MediaDatabase extends SQLiteOpenHelper {
"REFERENCES " + Tables.ARTISTS + "(" + MediaContract.ArtistsColumns.ARTISTID + ")";
String GENREID =
"REFERENCES " + Tables.AUDIO_GENRES + "(" + MediaContract.AudioGenresColumns.GENREID + ")";
String SONGID =
"REFERENCES " + Tables.SONGS + "(" + MediaContract.Songs.SONGID + ")";
}
public MediaDatabase(Context context) {
@ -359,6 +362,8 @@ public class MediaDatabase extends SQLiteOpenHelper {
") ON CONFLICT REPLACE)"
);
createSongArtistsTable(db);
// AudioGenres
db.execSQL("CREATE TABLE " + Tables.AUDIO_GENRES + "(" +
BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
@ -378,7 +383,7 @@ public class MediaDatabase extends SQLiteOpenHelper {
BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
MediaContract.AlbumArtistsColumns.HOST_ID + " INTEGER NOT NULL " + References.HOST_ID + ", " +
MediaContract.AlbumArtistsColumns.ALBUMID + " INTEGER NOT NULL " + References.ALBUMID + ", " +
MediaContract.AlbumArtistsColumns.ARTISTID + " INTEGER NOT NULL " + References .ARTISTID + ", " +
MediaContract.AlbumArtistsColumns.ARTISTID + " INTEGER NOT NULL " + References.ARTISTID + ", " +
"UNIQUE (" +
MediaContract.AlbumArtistsColumns.HOST_ID + ", " +
MediaContract.AlbumArtistsColumns.ALBUMID + ", " +
@ -471,6 +476,8 @@ public class MediaDatabase extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + Tables.HOSTS +
" ADD COLUMN " + MediaContract.HostsColumns.EVENT_SERVER_PORT +
" INTEGER DEFAULT " + HostInfo.DEFAULT_EVENT_SERVER_PORT + ";");
case DB_VERSION_PRE_SONG_ARTISTS:
createSongArtistsTable(db);
}
}
@ -515,5 +522,19 @@ public class MediaDatabase extends SQLiteOpenHelper {
return order.toString();
}
private void createSongArtistsTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Tables.SONG_ARTISTS + "(" +
BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
MediaContract.SongArtistsColumns.HOST_ID + " INTEGER NOT NULL " + References.HOST_ID + ", " +
MediaContract.SongArtistsColumns.SONGID + " INTEGER NOT NULL " + References.SONGID + ", " +
MediaContract.SongArtistsColumns.ARTISTID + " INTEGER NOT NULL " + References .ARTISTID + ", " +
"UNIQUE (" +
MediaContract.SongArtistsColumns.HOST_ID + ", " +
MediaContract.SongArtistsColumns.SONGID + ", " +
MediaContract.SongArtistsColumns.ARTISTID +
") ON CONFLICT REPLACE)"
);
}
}

View File

@ -73,6 +73,7 @@ public class MediaProvider extends ContentProvider {
private static final int ARTISTS_LIST = 601;
private static final int ARTISTS_ID = 602;
private static final int ARTIST_ALBUMS_LIST = 610;
private static final int ARTIST_SONGS_LIST = 611;
private static final int ALBUMS_ALL = 700;
private static final int ALBUMS_LIST = 701;
@ -81,7 +82,6 @@ public class MediaProvider extends ContentProvider {
private static final int ALBUM_GENRES_LIST = 711;
private static final int SONGS_ALL = 800;
private static final int SONGS_ARTIST = 801;
private static final int SONGS_ALBUM = 802;
private static final int SONGS_ID = 803;
private static final int SONGS_LIST = 804;
@ -93,6 +93,7 @@ public class MediaProvider extends ContentProvider {
private static final int ALBUM_ARTISTS_ALL = 1000;
private static final int ALBUM_GENRES_ALL = 1001;
private static final int SONG_ARTISTS_ALL = 1002;
private static final int MUSIC_VIDEOS_ALL = 1100;
private static final int MUSIC_VIDEOS_LIST = 1101;
@ -169,6 +170,9 @@ public class MediaProvider extends ContentProvider {
matcher.addURI(authority, MediaContract.PATH_HOSTS + "/*/" +
MediaContract.PATH_ARTISTS + "/*/" +
MediaContract.PATH_ALBUMS, ARTIST_ALBUMS_LIST);
matcher.addURI(authority, MediaContract.PATH_HOSTS + "/*/" +
MediaContract.PATH_ARTISTS + "/*/" +
MediaContract.PATH_SONGS, ARTIST_SONGS_LIST);
// Albums
matcher.addURI(authority, MediaContract.PATH_ALBUMS, ALBUMS_ALL);
@ -193,9 +197,6 @@ public class MediaProvider extends ContentProvider {
matcher.addURI(authority, MediaContract.PATH_HOSTS + "/*/" +
MediaContract.PATH_ALBUMS + "/*/" +
MediaContract.PATH_SONGS + "/*", SONGS_ID);
matcher.addURI(authority, MediaContract.PATH_HOSTS + "/*/" +
MediaContract.PATH_ARTISTS + "/*/" +
MediaContract.PATH_SONGS, SONGS_ARTIST);
// Genres
matcher.addURI(authority, MediaContract.PATH_AUDIO_GENRES, AUDIO_GENRES_ALL);
@ -211,6 +212,8 @@ public class MediaProvider extends ContentProvider {
matcher.addURI(authority, MediaContract.PATH_ALBUM_ARTISTS, ALBUM_ARTISTS_ALL);
// AlbumGenres
matcher.addURI(authority, MediaContract.PATH_ALBUM_GENRES, ALBUM_GENRES_ALL);
// SongArtists
matcher.addURI(authority, MediaContract.PATH_SONG_ARTISTS, SONG_ARTISTS_ALL);
// Music Videos
matcher.addURI(authority, MediaContract.PATH_MUSIC_VIDEOS, MUSIC_VIDEOS_ALL);
@ -288,7 +291,7 @@ public class MediaProvider extends ContentProvider {
return MediaContract.Albums.CONTENT_ITEM_TYPE;
case SONGS_ALL:
case SONGS_LIST:
case SONGS_ARTIST:
case ARTIST_SONGS_LIST:
case SONGS_ALBUM:
return MediaContract.Songs.CONTENT_TYPE;
case SONGS_ID:
@ -303,6 +306,8 @@ public class MediaProvider extends ContentProvider {
return MediaContract.AlbumArtists.CONTENT_TYPE;
case ALBUM_GENRES_ALL:
return MediaContract.AlbumGenres.CONTENT_TYPE;
case SONG_ARTISTS_ALL:
return MediaContract.SongArtists.CONTENT_TYPE;
case MUSIC_VIDEOS_ALL:
case MUSIC_VIDEOS_LIST:
return MediaContract.MusicVideos.CONTENT_TYPE;
@ -326,7 +331,6 @@ public class MediaProvider extends ContentProvider {
default: {
// Most cases are handled with simple SelectionBuilder
final SelectionBuilder builder = buildQuerySelection(uri, match);
cursor = builder.where(selection, selectionArgs)
.query(db, projection, sortOrder);
}
@ -413,6 +417,10 @@ public class MediaProvider extends ContentProvider {
table = MediaDatabase.Tables.ALBUM_ARTISTS;
break;
}
case SONG_ARTISTS_ALL: {
table = MediaDatabase.Tables.SONG_ARTISTS;
break;
}
case MUSIC_VIDEOS_ALL: {
table = MediaDatabase.Tables.MUSIC_VIDEOS;
break;
@ -430,6 +438,7 @@ public class MediaProvider extends ContentProvider {
switch (match) {
case ALBUM_GENRES_ALL:
case ALBUM_ARTISTS_ALL:
case SONG_ARTISTS_ALL:
// Nothing to add to these tables
break;
default:
@ -658,8 +667,15 @@ public class MediaProvider extends ContentProvider {
}
case SONGS_LIST: {
final String hostId = MediaContract.Hosts.getHostId(uri);
return builder.table(MediaDatabase.Tables.SONGS_AND_ALBUM_JOIN)
.where(Qualified.SONGS_HOST_ID + "=?", hostId);
return builder.table(MediaDatabase.Tables.SONGS_FOR_ARTIST_AND_OR_ALBUM_JOIN)
.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.SongArtists.ARTISTID, MediaDatabase.Tables.SONG_ARTISTS)
.where(Qualified.SONGS_HOST_ID + "=?", hostId)
.groupBy(Qualified.SONGS_SONGID);
}
case SONGS_ALBUM: {
final String hostId = MediaContract.Hosts.getHostId(uri);
@ -668,14 +684,6 @@ public class MediaProvider extends ContentProvider {
.where(MediaContract.Songs.HOST_ID + "=?", hostId)
.where(MediaContract.Songs.ALBUMID + "=?", albumId);
}
case SONGS_ARTIST: {
final String hostId = MediaContract.Hosts.getHostId(uri);
final String artistId = MediaContract.Artists.getArtistId(uri);
LogUtils.LOGD(TAG, "buildQuerySelection: SONGS_ARTIST: "+MediaDatabase.Tables.SONGS_FOR_ARTIST_JOIN);
return builder.table(MediaDatabase.Tables.SONGS_FOR_ARTIST_JOIN)
.where(Qualified.SONGS_HOST_ID + "=?", hostId)
.where(Qualified.ALBUM_ARTISTS_ARTISTID + "=?", artistId);
}
case SONGS_ID: {
final String hostId = MediaContract.Hosts.getHostId(uri);
final String albumId = MediaContract.Albums.getAlbumId(uri);
@ -703,6 +711,9 @@ public class MediaProvider extends ContentProvider {
case ALBUM_ARTISTS_ALL: {
return builder.table(MediaDatabase.Tables.ALBUM_ARTISTS);
}
case SONG_ARTISTS_ALL: {
return builder.table(MediaDatabase.Tables.SONG_ARTISTS);
}
case ALBUM_GENRES_ALL: {
return builder.table(MediaDatabase.Tables.ALBUM_GENRES);
}
@ -718,6 +729,20 @@ public class MediaProvider extends ContentProvider {
.where(Qualified.ALBUM_ARTISTS_HOST_ID + "=?", hostId)
.where(Qualified.ALBUM_ARTISTS_ARTISTID + "=?", artistId);
}
case ARTIST_SONGS_LIST: {
// Songs for Artists
final String hostId = MediaContract.Hosts.getHostId(uri);
final String artistId = MediaContract.Artists.getArtistId(uri);
return builder.table(MediaDatabase.Tables.SONGS_FOR_ARTIST_AND_OR_ALBUM_JOIN)
.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.SongArtists.ARTISTID, MediaDatabase.Tables.SONG_ARTISTS)
.where(Qualified.SONG_ARTISTS_HOST_ID + "=?", hostId)
.where(Qualified.SONG_ARTISTS_ARTISTID + "=?", artistId);
}
case ALBUM_ARTISTS_LIST: {
// Artists for Album
final String hostId = MediaContract.Hosts.getHostId(uri);
@ -795,5 +820,11 @@ public class MediaProvider extends ContentProvider {
MediaDatabase.Tables.ALBUM_GENRES + "." + MediaContract.AlbumGenres.ALBUMID;
String SONGS_HOST_ID =
MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.HOST_ID;
String SONGS_SONGID =
MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.SONGID;
String SONG_ARTISTS_HOST_ID =
MediaDatabase.Tables.SONG_ARTISTS + "." + MediaContract.SongArtists.HOST_ID;
String SONG_ARTISTS_ARTISTID =
MediaDatabase.Tables.SONG_ARTISTS + "." + MediaContract.SongArtists.ARTISTID;
}
}

View File

@ -317,7 +317,8 @@ public class SyncMusic extends SyncItem {
AudioType.FieldsSong.THUMBNAIL, AudioType.FieldsSong.FILE,
AudioType.FieldsSong.ALBUMID,
//AudioType.FieldsSong.LASTPLAYED, AudioType.FieldsSong.DISC,
//AudioType.FieldsSong.GENREID, AudioType.FieldsSong.ARTISTID,
//AudioType.FieldsSong.GENREID,
AudioType.FieldsSong.ARTISTID,
//AudioType.FieldsSong.DISPLAYARTIST, AudioType.FieldsSong.ALBUMARTISTID
};
@ -345,14 +346,30 @@ public class SyncMusic extends SyncItem {
limitsReturned = result.limits;
}
int totalArtistsCount = 0;
// Save partial results to DB
ContentValues songValuesBatch[] = new ContentValues[items.size()];
for (int i = 0; i < items.size(); i++) {
AudioType.DetailsSong song = items.get(i);
songValuesBatch[i] = SyncUtils.contentValuesFromSong(hostId, song);
totalArtistsCount += song.artistid.size();
}
contentResolver.bulkInsert(MediaContract.Songs.CONTENT_URI, songValuesBatch);
// Iterate on each song, collect the artists and insert them
ContentValues songArtistsValuesBatch[] = new ContentValues[totalArtistsCount];
int artistCount = 0;
for (AudioType.DetailsSong song : items) {
for (int artistId : song.artistid) {
songArtistsValuesBatch[artistCount] = new ContentValues();
songArtistsValuesBatch[artistCount].put(MediaContract.SongArtists.HOST_ID, hostId);
songArtistsValuesBatch[artistCount].put(MediaContract.SongArtists.SONGID, song.songid);
songArtistsValuesBatch[artistCount].put(MediaContract.SongArtists.ARTISTID, artistId);
artistCount++;
}
}
contentResolver.bulkInsert(MediaContract.SongArtists.CONTENT_URI, songArtistsValuesBatch);
if (moreItemsAvailable(limitsReturned)) {
LogUtils.LOGD(TAG, "chainCallSyncSongs: More results on media center, recursing.");
result = null; // Help the GC?

View File

@ -27,7 +27,6 @@ import org.xbmc.kore.jsonrpc.type.VideoType;
import org.xbmc.kore.jsonrpc.type.AudioType;
import org.xbmc.kore.jsonrpc.type.LibraryType;
import org.xbmc.kore.provider.MediaContract;
import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.utils.Utils;
import java.util.ArrayList;
@ -42,7 +41,7 @@ public class SyncUtils {
void onServiceConnected(LibrarySyncService librarySyncService);
}
public static final String LIST_DELIMETER = ", ";
public static final String LIST_DELIMITER = ", ";
/**
* Returns {@link android.content.ContentValues} from a {@link org.xbmc.kore.jsonrpc.type.VideoType.DetailsMovie} movie
@ -61,7 +60,7 @@ public class SyncUtils {
movieValues.put(MediaContract.MoviesColumns.TITLE, movie.title);
movieValues.put(MediaContract.MoviesColumns.FILE, movie.file);
movieValues.put(MediaContract.MoviesColumns.PLOT, movie.plot);
movieValues.put(MediaContract.MoviesColumns.DIRECTOR, Utils.listStringConcat(movie.director, LIST_DELIMETER));
movieValues.put(MediaContract.MoviesColumns.DIRECTOR, Utils.listStringConcat(movie.director, LIST_DELIMITER));
movieValues.put(MediaContract.MoviesColumns.RUNTIME, movie.runtime);
if (movie.streamdetails != null) {
if (movie.streamdetails.audio.size() > 0) {
@ -78,7 +77,7 @@ public class SyncUtils {
movieValues.put(MediaContract.MoviesColumns.AUDIO_CHANNELS, selectedStream.channels);
movieValues.put(MediaContract.MoviesColumns.AUDIO_CODEC, selectedStream.codec);
movieValues.put(MediaContract.MoviesColumns.AUDIO_LANGUAGE,
Utils.listStringConcat(languages, LIST_DELIMETER));
Utils.listStringConcat(languages, LIST_DELIMITER));
}
if (movie.streamdetails.subtitle.size() > 0) {
// Concat all subtitle languages
@ -87,7 +86,7 @@ public class SyncUtils {
subtitles.add(movie.streamdetails.subtitle.get(j).language);
}
movieValues.put(MediaContract.MoviesColumns.SUBTITLES_LANGUAGES,
Utils.listStringConcat(subtitles, LIST_DELIMETER));
Utils.listStringConcat(subtitles, LIST_DELIMITER));
}
if (movie.streamdetails.video.size() > 0) {
// We're only getting the first video channel...
@ -102,22 +101,22 @@ public class SyncUtils {
}
}
movieValues.put(MediaContract.MoviesColumns.COUNTRIES,
Utils.listStringConcat(movie.country, LIST_DELIMETER));
Utils.listStringConcat(movie.country, LIST_DELIMITER));
movieValues.put(MediaContract.MoviesColumns.GENRES,
Utils.listStringConcat(movie.genre, LIST_DELIMETER));
Utils.listStringConcat(movie.genre, LIST_DELIMITER));
movieValues.put(MediaContract.MoviesColumns.IMDBNUMBER, movie.imdbnumber);
movieValues.put(MediaContract.MoviesColumns.MPAA, movie.mpaa);
movieValues.put(MediaContract.MoviesColumns.RATING, movie.rating);
movieValues.put(MediaContract.MoviesColumns.SET, movie.set);
movieValues.put(MediaContract.MoviesColumns.SETID, movie.setid);
movieValues.put(MediaContract.MoviesColumns.STUDIOS,
Utils.listStringConcat(movie.studio, LIST_DELIMETER));
Utils.listStringConcat(movie.studio, LIST_DELIMITER));
movieValues.put(MediaContract.MoviesColumns.TAGLINE, movie.tagline);
movieValues.put(MediaContract.MoviesColumns.TOP250, movie.top250);
movieValues.put(MediaContract.MoviesColumns.TRAILER, movie.trailer);
movieValues.put(MediaContract.MoviesColumns.VOTES, movie.votes);
movieValues.put(MediaContract.MoviesColumns.WRITERS,
Utils.listStringConcat(movie.writer, LIST_DELIMETER));
Utils.listStringConcat(movie.writer, LIST_DELIMITER));
movieValues.put(MediaContract.MoviesColumns.YEAR, movie.year);
return movieValues;
@ -164,10 +163,10 @@ public class SyncUtils {
tvshowValues.put(MediaContract.TVShowsColumns.PREMIERED, tvshow.premiered);
tvshowValues.put(MediaContract.TVShowsColumns.RATING, tvshow.rating);
tvshowValues.put(MediaContract.TVShowsColumns.STUDIO,
Utils.listStringConcat(tvshow.studio, LIST_DELIMETER));
Utils.listStringConcat(tvshow.studio, LIST_DELIMITER));
tvshowValues.put(MediaContract.TVShowsColumns.WATCHEDEPISODES, tvshow.watchedepisodes);
tvshowValues.put(MediaContract.TVShowsColumns.GENRES,
Utils.listStringConcat(tvshow.genre, LIST_DELIMETER));
Utils.listStringConcat(tvshow.genre, LIST_DELIMITER));
return tvshowValues;
}
@ -215,12 +214,12 @@ public class SyncUtils {
episodeValues.put(MediaContract.EpisodesColumns.TITLE, episode.title);
episodeValues.put(MediaContract.EpisodesColumns.FILE, episode.file);
episodeValues.put(MediaContract.EpisodesColumns.PLOT, episode.plot);
episodeValues.put(MediaContract.EpisodesColumns.DIRECTOR, Utils.listStringConcat(episode.director, LIST_DELIMETER));
episodeValues.put(MediaContract.EpisodesColumns.DIRECTOR, Utils.listStringConcat(episode.director, LIST_DELIMITER));
episodeValues.put(MediaContract.EpisodesColumns.RUNTIME, episode.runtime);
episodeValues.put(MediaContract.EpisodesColumns.FIRSTAIRED, episode.firstaired);
episodeValues.put(MediaContract.EpisodesColumns.RATING, episode.rating);
episodeValues.put(MediaContract.EpisodesColumns.SHOWTITLE, episode.showtitle);
episodeValues.put(MediaContract.EpisodesColumns.WRITER, Utils.listStringConcat(episode.writer, LIST_DELIMETER));
episodeValues.put(MediaContract.EpisodesColumns.WRITER, Utils.listStringConcat(episode.writer, LIST_DELIMITER));
if (episode.streamdetails.audio.size() > 0) {
// Get the stream with the most channels and concat all the languages
@ -235,7 +234,7 @@ public class SyncUtils {
}
episodeValues.put(MediaContract.EpisodesColumns.AUDIO_CHANNELS, selectedStream.channels);
episodeValues.put(MediaContract.EpisodesColumns.AUDIO_CODEC, selectedStream.codec);
episodeValues.put(MediaContract.EpisodesColumns.AUDIO_LANGUAGE, Utils.listStringConcat(languages, LIST_DELIMETER));
episodeValues.put(MediaContract.EpisodesColumns.AUDIO_LANGUAGE, Utils.listStringConcat(languages, LIST_DELIMITER));
}
if (episode.streamdetails.subtitle.size() > 0) {
// Concat all subtitle languages
@ -243,7 +242,7 @@ public class SyncUtils {
for (int j = 0; j < episode.streamdetails.subtitle.size(); j++) {
subtitles.add(episode.streamdetails.subtitle.get(j).language);
}
episodeValues.put(MediaContract.EpisodesColumns.SUBTITLES_LANGUAGES, Utils.listStringConcat(subtitles, LIST_DELIMETER));
episodeValues.put(MediaContract.EpisodesColumns.SUBTITLES_LANGUAGES, Utils.listStringConcat(subtitles, LIST_DELIMITER));
}
if (episode.streamdetails.video.size() > 0) {
// We're only getting the first video channel...
@ -273,7 +272,7 @@ public class SyncUtils {
castValues.put(MediaContract.ArtistsColumns.ARTIST, artist.artist);
castValues.put(MediaContract.ArtistsColumns.DESCRIPTION, artist.description);
castValues.put(MediaContract.ArtistsColumns.GENRE,
Utils.listStringConcat(artist.genre, LIST_DELIMETER));
Utils.listStringConcat(artist.genre, LIST_DELIMITER));
castValues.put(MediaContract.ArtistsColumns.FANART, artist.fanart);
castValues.put(MediaContract.ArtistsColumns.THUMBNAIL, artist.thumbnail);
@ -315,7 +314,7 @@ public class SyncUtils {
castValues.put(MediaContract.Albums.ALBUMLABEL, album.albumlabel);
castValues.put(MediaContract.Albums.DESCRIPTION, album.description);
castValues.put(MediaContract.Albums.PLAYCOUNT, album.playcount);
castValues.put(MediaContract.Albums.GENRE, Utils.listStringConcat(album.genre, LIST_DELIMETER));
castValues.put(MediaContract.Albums.GENRE, Utils.listStringConcat(album.genre, LIST_DELIMITER));
return castValues;
}
@ -357,7 +356,7 @@ public class SyncUtils {
musicVideoValues.put(MediaContract.MusicVideosColumns.TITLE, musicVideo.title);
musicVideoValues.put(MediaContract.MusicVideosColumns.FILE, musicVideo.file);
musicVideoValues.put(MediaContract.MusicVideosColumns.PLOT, musicVideo.plot);
musicVideoValues.put(MediaContract.MusicVideosColumns.DIRECTOR, Utils.listStringConcat(musicVideo.director, LIST_DELIMETER));
musicVideoValues.put(MediaContract.MusicVideosColumns.DIRECTOR, Utils.listStringConcat(musicVideo.director, LIST_DELIMITER));
musicVideoValues.put(MediaContract.MusicVideosColumns.RUNTIME, musicVideo.runtime);
if (musicVideo.streamdetails != null) {
if (musicVideo.streamdetails.audio.size() > 0) {
@ -374,7 +373,7 @@ public class SyncUtils {
musicVideoValues.put(MediaContract.MusicVideosColumns.AUDIO_CHANNELS, selectedStream.channels);
musicVideoValues.put(MediaContract.MusicVideosColumns.AUDIO_CODEC, selectedStream.codec);
musicVideoValues.put(MediaContract.MusicVideosColumns.AUDIO_LANGUAGE,
Utils.listStringConcat(languages, LIST_DELIMETER));
Utils.listStringConcat(languages, LIST_DELIMITER));
}
if (musicVideo.streamdetails.subtitle.size() > 0) {
// Concat all subtitle languages
@ -383,7 +382,7 @@ public class SyncUtils {
subtitles.add(musicVideo.streamdetails.subtitle.get(j).language);
}
musicVideoValues.put(MediaContract.MusicVideosColumns.SUBTITLES_LANGUAGES,
Utils.listStringConcat(subtitles, LIST_DELIMETER));
Utils.listStringConcat(subtitles, LIST_DELIMITER));
}
if (musicVideo.streamdetails.video.size() > 0) {
// We're only getting the first video channel...
@ -399,13 +398,13 @@ public class SyncUtils {
}
musicVideoValues.put(MediaContract.MusicVideosColumns.ALBUM, musicVideo.album);
musicVideoValues.put(MediaContract.MusicVideosColumns.ARTIST,
Utils.listStringConcat(musicVideo.artist, LIST_DELIMETER));
Utils.listStringConcat(musicVideo.artist, LIST_DELIMITER));
musicVideoValues.put(MediaContract.MusicVideosColumns.GENRES,
Utils.listStringConcat(musicVideo.genre, LIST_DELIMETER));
Utils.listStringConcat(musicVideo.genre, LIST_DELIMITER));
musicVideoValues.put(MediaContract.MusicVideosColumns.STUDIOS,
Utils.listStringConcat(musicVideo.studio, LIST_DELIMETER));
Utils.listStringConcat(musicVideo.studio, LIST_DELIMITER));
musicVideoValues.put(MediaContract.MusicVideosColumns.TAG,
Utils.listStringConcat(musicVideo.tag, LIST_DELIMETER));
Utils.listStringConcat(musicVideo.tag, LIST_DELIMITER));
musicVideoValues.put(MediaContract.MusicVideosColumns.TRACK, musicVideo.track);
musicVideoValues.put(MediaContract.MusicVideosColumns.YEAR, musicVideo.year);

View File

@ -229,7 +229,7 @@ public class ArtistOverviewFragment extends AbstractDetailsFragment
case LOADER_SONGS:
uri = MediaContract.Songs.buildArtistSongsListUri(hostInfo.getId(), artistId);
return new CursorLoader(getActivity(), uri,
AlbumSongsListQuery.PROJECTION, null, null, AlbumSongsListQuery.SORT);
SongsListQuery.PROJECTION, null, null, SongsListQuery.SORT);
default:
return null;
}
@ -299,16 +299,16 @@ public class ArtistOverviewFragment extends AbstractDetailsFragment
private FileDownloadHelper.SongInfo createSongInfo(Cursor cursor) {
FileDownloadHelper.SongInfo songInfo = null;
String albumTitle = albumTitles.get(cursor.getInt(AlbumSongsListQuery.ALBUMID));
String albumTitle = albumTitles.get(cursor.getInt(SongsListQuery.ALBUMID));
if (albumTitle != null) {
// Add this song to the list
songInfo = new FileDownloadHelper.SongInfo(
artistTitle,
albumTitle,
cursor.getInt(AlbumSongsListQuery.SONGID),
cursor.getInt(AlbumSongsListQuery.TRACK),
cursor.getString(AlbumSongsListQuery.TITLE),
cursor.getString(AlbumSongsListQuery.FILE));
cursor.getInt(SongsListQuery.SONGID),
cursor.getInt(SongsListQuery.TRACK),
cursor.getString(SongsListQuery.TITLE),
cursor.getString(SongsListQuery.FILE));
}
return songInfo;
}
@ -457,7 +457,7 @@ public class ArtistOverviewFragment extends AbstractDetailsFragment
/**
* Song list query parameters.
*/
private interface AlbumSongsListQuery {
private interface SongsListQuery {
String[] PROJECTION = {
MediaDatabase.Tables.SONGS + "." + BaseColumns._ID,
MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.TITLE,

View File

@ -49,6 +49,8 @@ import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.MediaPlayerUtils;
import org.xbmc.kore.utils.UIUtils;
import java.util.ArrayList;
/**
* Fragment that presents the songs list
*/
@ -94,7 +96,7 @@ public class SongsListFragment extends AbstractCursorListFragment {
}
return new CursorLoader(getActivity(), uri,
SongsAlbumsListQuery.PROJECTION, selection, selectionArgs, SongsAlbumsListQuery.SORT);
SongsListQuery.PROJECTION, selection, selectionArgs, SongsListQuery.SORT);
}
@Override
@ -129,35 +131,10 @@ public class SongsListFragment extends AbstractCursorListFragment {
super.onCreateOptionsMenu(menu, inflater);
}
/**
* Song list query parameters.
*/
private 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.SONGS + "." + MediaContract.Songs.ALBUMID,
};
String SORT = MediaContract.Songs.TRACK + " ASC";
int ID = 0;
int TITLE = 1;
int TRACK = 2;
int DURATION = 3;
int FILE = 4;
int SONGID = 5;
int ALBUMID = 6;
}
/**
* Album songs list query parameters.
*/
private interface SongsAlbumsListQuery {
private interface SongsListQuery {
String[] PROJECTION = {
MediaDatabase.Tables.SONGS + "." + BaseColumns._ID,
MediaDatabase.Tables.SONGS + "." + MediaContract.Songs.TITLE,
@ -170,6 +147,7 @@ public class SongsListFragment extends AbstractCursorListFragment {
MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.GENRE,
MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.YEAR,
MediaDatabase.Tables.ALBUMS + "." + MediaContract.Albums.THUMBNAIL,
MediaDatabase.Tables.ARTISTS + "." + MediaContract.Artists.ARTIST
};
String SORT = MediaDatabase.sortCommonTokens(MediaDatabase.Tables.SONGS
@ -187,6 +165,7 @@ public class SongsListFragment extends AbstractCursorListFragment {
int GENRE = 8;
int YEAR = 9;
int THUMBNAIL = 10;
int ARTIST = 11;
}
private class SongsAdapter extends CursorAdapter {
@ -229,25 +208,30 @@ public class SongsListFragment extends AbstractCursorListFragment {
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder viewHolder = (ViewHolder)view.getTag();
String title = cursor.getString(SongsAlbumsListQuery.TITLE);
viewHolder.songId = cursor.getInt(SongsAlbumsListQuery.SONGID);
String title = cursor.getString(SongsListQuery.TITLE);
viewHolder.songId = cursor.getInt(SongsListQuery.SONGID);
viewHolder.title.setText(title);
viewHolder.artist.setText(String.valueOf(cursor.getString(SongsAlbumsListQuery.ALBUMARTIST)));
int year = cursor.getInt(SongsAlbumsListQuery.YEAR);
String artist = cursor.getString(SongsListQuery.ALBUMARTIST);
if (TextUtils.isEmpty(artist))
artist = cursor.getString(SongsListQuery.ARTIST);
viewHolder.artist.setText(artist);
int year = cursor.getInt(SongsListQuery.YEAR);
if (year > 0) {
setDetails(viewHolder.details,
cursor.getString(SongsAlbumsListQuery.ALBUMTITLE),
cursor.getString(SongsListQuery.ALBUMTITLE),
String.valueOf(year),
cursor.getString(SongsAlbumsListQuery.GENRE));
cursor.getString(SongsListQuery.GENRE));
} else {
setDetails(viewHolder.details,
cursor.getString(SongsAlbumsListQuery.ALBUMTITLE),
cursor.getString(SongsAlbumsListQuery.GENRE));
cursor.getString(SongsListQuery.ALBUMTITLE),
cursor.getString(SongsListQuery.GENRE));
}
String thumbnail = cursor.getString(SongsAlbumsListQuery.THUMBNAIL);
String thumbnail = cursor.getString(SongsListQuery.THUMBNAIL);
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
thumbnail, title,
viewHolder.art, artWidth, artHeight);
@ -306,21 +290,12 @@ public class SongsListFragment extends AbstractCursorListFragment {
return;
}
StringBuilder stringBuilder = new StringBuilder();
int i = 0;
int size = elements.length - 1;
for (; i < size; i++) {
if (!TextUtils.isEmpty(elements[i])) {
stringBuilder.append(elements[i]);
stringBuilder.append(" | ");
}
ArrayList<String> details = new ArrayList<>();
for (int i = 0; i < elements.length; i++) {
if (!TextUtils.isEmpty(elements[i]))
details.add(elements[i]);
}
if (elements.length > 0) {
stringBuilder.append(elements[i]);
}
textView.setText(stringBuilder.toString());
textView.setText(TextUtils.join(" | ", details.toArray()));
}
}

View File

@ -45,7 +45,7 @@ public class SelectionBuilder {
private Map<String, String> mProjectionMap = new HashMap<String, String>();
private StringBuilder mSelection = new StringBuilder();
private ArrayList<String> mSelectionArgs = new ArrayList<String>();
private StringBuilder mGroupBy = new StringBuilder();
/**
* Reset any internal state, allowing this builder to be recycled.
*/
@ -56,6 +56,20 @@ public class SelectionBuilder {
return this;
}
public SelectionBuilder groupBy(String... groupByArgs) {
if (groupByArgs != null) {
if (mGroupBy.length() > 0)
mGroupBy.append(", ");
int size = groupByArgs.length - 1;
for (int i = 0; i < size; i++) {
mGroupBy.append(groupByArgs[i] + ", ");
}
mGroupBy.append(groupByArgs[size]);
}
return this;
}
/**
* Append the given selection clause to the internal state. Each clause is
* surrounded with parenthesis and combined using {@code AND}.
@ -141,7 +155,7 @@ public class SelectionBuilder {
* Execute query using the current internal state as {@code WHERE} clause.
*/
public Cursor query(SQLiteDatabase db, String[] columns, String orderBy) {
return query(db, columns, null, null, orderBy, null);
return query(db, columns, mGroupBy.toString(), null, orderBy, null);
}
/**