package com.simplecity.amp_library.model;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.util.Pair;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import com.crashlytics.android.core.CrashlyticsCore;
import com.simplecity.amp_library.R;
import com.simplecity.amp_library.ShuttleApplication;
import com.simplecity.amp_library.sql.SqlUtils;
import com.simplecity.amp_library.sql.providers.PlayCountTable;
import com.simplecity.amp_library.sql.sqlbrite.SqlBriteUtils;
import com.simplecity.amp_library.utils.ComparisonUtils;
import com.simplecity.amp_library.utils.DataManager;
import com.simplecity.amp_library.utils.MusicUtils;
import com.simplecity.amp_library.utils.PlaylistUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import rx.Observable;
public class Playlist implements Serializable {
private static final String TAG = "Playlist";
public @interface Type {
int PODCAST = 0;
int RECENTLY_ADDED = 1;
int MOST_PLAYED = 2;
int RECENTLY_PLAYED = 3;
int FAVORITES = 4;
int USER_CREATED = 5;
}
@Type
public int type;
public long id;
public String name;
public boolean canEdit = true;
public boolean canClear = false;
public boolean canDelete = true;
public boolean canRename = true;
public boolean canSort = true;
// These are the Playlist rows that we will retrieve.
public static final String[] PROJECTION = new String[]{
MediaStore.Audio.Playlists._ID,
MediaStore.Audio.Playlists.NAME
};
public static Query getQuery() {
return new Query.Builder()
.uri(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI)
.projection(PROJECTION)
.selection(null)
.sort(null)
.build();
}
public Playlist(@Type int type, long id, String name, boolean canEdit, boolean canClear, boolean canDelete, boolean canRename, boolean canSort) {
this.type = type;
this.id = id;
this.name = name;
this.canEdit = canEdit;
this.canClear = canClear;
this.canDelete = canDelete;
this.canRename = canRename;
this.canSort = canSort;
}
public Playlist(Cursor cursor) {
id = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Playlists._ID));
name = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Playlists.NAME));
type = Type.USER_CREATED;
if (ShuttleApplication.getInstance().getString(R.string.fav_title).equals(name)) {
type = Type.FAVORITES;
canDelete = false;
canRename = false;
}
}
public static Playlist podcastPlaylist() {
// Check if there are any podcasts
Query query = new Query.Builder()
.uri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI)
.projection(new String[]{"count(*)", "is_podcast=1"})
.build();
return SqlUtils.createSingleQuery(ShuttleApplication.getInstance(), cursor -> new Playlist(
Type.PODCAST, MusicUtils.PlaylistIds.PODCASTS_PLAYLIST,
ShuttleApplication.getInstance().getString(R.string.podcasts_title),
false, false, false, false, false),
query);
}
public static Playlist recentlyAddedPlaylist() {
return new Playlist(
Type.RECENTLY_ADDED, MusicUtils.PlaylistIds.RECENTLY_ADDED_PLAYLIST,
ShuttleApplication.getInstance().getString(R.string.recentlyadded),
false, false, false, false, false);
}
public static Playlist mostPlayedPlaylist() {
return new Playlist(Type.MOST_PLAYED, MusicUtils.PlaylistIds.MOST_PLAYED_PLAYLIST,
ShuttleApplication.getInstance().getString(R.string.mostplayed),
false, true, false, false, false);
}
public static Playlist recentlyPlayedPlaylist() {
return new Playlist(Type.RECENTLY_PLAYED, MusicUtils.PlaylistIds.RECENTLY_PLAYED_PLAYLIST,
ShuttleApplication.getInstance().getString(R.string.suggested_recent_title),
false, false, false, false, false);
}
public static Playlist favoritesPlaylist() {
Query query = new Query.Builder()
.uri(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI)
.projection(new String[]{BaseColumns._ID})
.selection(MediaStore.Audio.PlaylistsColumns.NAME + "='" + ShuttleApplication.getInstance().getResources().getString(R.string.fav_title) + "'")
.build();
Playlist playlist = SqlUtils.createSingleQuery(ShuttleApplication.getInstance(), cursor -> new Playlist(
Type.FAVORITES, cursor.getInt(cursor.getColumnIndexOrThrow(BaseColumns._ID)),
ShuttleApplication.getInstance().getString(R.string.fav_title),
true, true, false, false, true),
query);
if (playlist == null) {
playlist = PlaylistUtils.createPlaylist(ShuttleApplication.getInstance(), ShuttleApplication.getInstance().getString(R.string.fav_title));
if (playlist != null) {
playlist.canDelete = false;
playlist.canRename = false;
}
}
if (playlist == null) {
CrashlyticsCore.getInstance().log("favoritesPlaylist() returned null..");
}
return playlist;
}
public void delete(Context context) {
Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, id);
if (uri != null) {
context.getContentResolver().delete(uri, null, null);
}
}
public Observable<List<Song>> getSongsObservable(Context context) {
if (id == MusicUtils.PlaylistIds.RECENTLY_ADDED_PLAYLIST) {
int numWeeks = MusicUtils.getIntPref(context, "numweeks", 2) * (3600 * 24 * 7);
return DataManager.getInstance().getSongsRelay()
.first()
.map(allSongs -> {
List<Song> songs = Stream.of(allSongs)
.filter(song -> song.dateAdded > (System.currentTimeMillis() / 1000 - numWeeks))
.collect(Collectors.toList());
Collections.sort(songs, (a, b) -> ComparisonUtils.compare(a.albumArtistName, b.albumArtistName));
Collections.sort(songs, (a, b) -> ComparisonUtils.compareInt(b.year, a.year));
Collections.sort(songs, (a, b) -> ComparisonUtils.compareInt(a.track, b.track));
Collections.sort(songs, (a, b) -> ComparisonUtils.compareInt(a.discNumber, b.discNumber));
Collections.sort(songs, (a, b) -> ComparisonUtils.compare(a.albumName, b.albumName));
Collections.sort(songs, (a, b) -> ComparisonUtils.compareLong(b.dateAdded, a.dateAdded));
return songs;
});
} else if (id == MusicUtils.PlaylistIds.PODCASTS_PLAYLIST) {
return DataManager.getInstance().getSongsRelay()
.first()
.map(allSongs -> {
List<Song> songs = Stream.of(allSongs)
.filter(song -> song.isPodcast)
.collect(Collectors.toList());
Collections.sort(songs, (a, b) -> ComparisonUtils.compareLong(a.playlistSongPlayOrder, b.playlistSongPlayOrder));
return songs;
});
} else if (id == MusicUtils.PlaylistIds.MOST_PLAYED_PLAYLIST) {
Query query = new Query.Builder()
.uri(PlayCountTable.URI)
.projection(new String[]{PlayCountTable.COLUMN_ID, PlayCountTable.COLUMN_PLAY_COUNT})
.sort(PlayCountTable.COLUMN_PLAY_COUNT + " DESC")
.build();
return SqlBriteUtils.createQuery(context, cursor ->
new Pair<>(
cursor.getInt(cursor.getColumnIndexOrThrow(PlayCountTable.COLUMN_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(PlayCountTable.COLUMN_PLAY_COUNT))
), query)
.flatMap(pairs -> {
Query songsQuery = Song.getQuery();
songsQuery.selection += " AND " + MediaStore.Audio.Media._ID + " IN (" +
Stream.of(pairs)
.map(value -> String.valueOf(value.first))
.collect(Collectors.joining(",")) +
")";
return SqlBriteUtils.createQuery(context, Song::new, songsQuery)
.map(songs -> {
Stream.of(songs)
.forEach(song ->
Stream.of(pairs)
.forEach(pair -> {
if (pair.first == song.id) {
song.playCount = pair.second;
}
}));
return songs;
});
}).map(songs -> {
Collections.sort(songs, (a, b) -> ComparisonUtils.compareInt(b.playCount, a.playCount));
return songs;
});
} else if (id == MusicUtils.PlaylistIds.RECENTLY_PLAYED_PLAYLIST) {
Query query = new Query.Builder()
.uri(PlayCountTable.URI)
.projection(new String[]{PlayCountTable.COLUMN_ID, PlayCountTable.COLUMN_TIME_PLAYED})
.sort(PlayCountTable.COLUMN_TIME_PLAYED + " DESC")
.build();
return SqlBriteUtils.createQuery(context, cursor ->
new Pair<>(
cursor.getLong(cursor.getColumnIndexOrThrow(PlayCountTable.COLUMN_ID)),
cursor.getLong(cursor.getColumnIndexOrThrow(PlayCountTable.COLUMN_TIME_PLAYED))
), query)
.flatMap(pairs -> {
Query songsQuery = Song.getQuery();
songsQuery.selection += " AND " + MediaStore.Audio.Media._ID + " IN (" +
Stream.of(pairs)
.map(value -> String.valueOf(value.first))
.collect(Collectors.joining(",")) +
")";
return SqlBriteUtils.createQuery(context, Song::new, songsQuery)
.map(songs -> {
Stream.of(songs)
.forEach(song -> Stream.of(pairs)
.forEach(pair -> {
if (pair.first == song.id) {
song.lastPlayed = pair.second;
}
}));
return songs;
});
})
.map(songs -> {
Collections.sort(songs, (a, b) -> ComparisonUtils.compareLong(b.lastPlayed, a.lastPlayed));
return songs;
});
} else {
Query query = Song.getQuery();
query.uri = MediaStore.Audio.Playlists.Members.getContentUri("external", id);
List<String> projection = new ArrayList<>(Arrays.asList(Song.getProjection()));
projection.add(MediaStore.Audio.Playlists.Members._ID);
projection.add(MediaStore.Audio.Playlists.Members.AUDIO_ID);
projection.add(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
query.projection = projection.toArray(new String[projection.size()]);
return SqlBriteUtils.createQuery(context, Playlist::createSongFromPlaylistCursor, query).map(songs -> {
Collections.sort(songs, (a, b) -> ComparisonUtils.compareLong(a.playlistSongPlayOrder, b.playlistSongPlayOrder));
return songs;
});
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Playlist playlist = (Playlist) o;
if (id != playlist.id) return false;
return name != null ? name.equals(playlist.name) : playlist.name == null;
}
@Override
public int hashCode() {
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Playlist{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
private static Song createSongFromPlaylistCursor(Cursor cursor) {
Song song = new Song(cursor);
song.id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.AUDIO_ID));
song.playlistSongId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members._ID));
song.playlistSongPlayOrder = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.PLAY_ORDER));
return song;
}
}