Initial commit retro music app
This commit is contained in:
parent
ab332473bc
commit
fe890632fd
932 changed files with 83126 additions and 0 deletions
|
@ -0,0 +1,104 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.MediaStore.Audio.AudioColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import code.name.monkey.retromusic.model.Album;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import io.reactivex.Observable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 11/08/17.
|
||||
*/
|
||||
|
||||
public class AlbumLoader {
|
||||
|
||||
public static Observable<ArrayList<Album>> getAllAlbums(@NonNull Context context) {
|
||||
Observable<ArrayList<Song>> songs = SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||
context,
|
||||
null,
|
||||
null,
|
||||
getSongLoaderSortOrder(context))
|
||||
);
|
||||
|
||||
return splitIntoAlbums(songs);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Album>> getAlbums(@NonNull final Context context,
|
||||
String query) {
|
||||
Observable<ArrayList<Song>> songs = SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||
context,
|
||||
AudioColumns.ALBUM + " LIKE ?",
|
||||
new String[]{"%" + query + "%"},
|
||||
getSongLoaderSortOrder(context))
|
||||
);
|
||||
return splitIntoAlbums(songs);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<Album> getAlbum(@NonNull final Context context, int albumId) {
|
||||
return Observable.create(e -> {
|
||||
Observable<ArrayList<Song>> songs = SongLoader.getSongs(SongLoader
|
||||
.makeSongCursor(context, AudioColumns.ALBUM_ID + "=?",
|
||||
new String[]{String.valueOf(albumId)}, getSongLoaderSortOrder(context)));
|
||||
songs.subscribe(songs1 -> {
|
||||
e.onNext(new Album(songs1));
|
||||
e.onComplete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Album>> splitIntoAlbums(
|
||||
@Nullable final Observable<ArrayList<Song>> songs) {
|
||||
return Observable.create(e -> {
|
||||
ArrayList<Album> albums = new ArrayList<>();
|
||||
if (songs != null) {
|
||||
songs.subscribe(songs1 -> {
|
||||
for (Song song : songs1) {
|
||||
getOrCreateAlbum(albums, song.albumId).subscribe(album -> album.songs.add(song));
|
||||
}
|
||||
});
|
||||
}
|
||||
e.onNext(albums);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static ArrayList<Album> splitIntoAlbums(@Nullable final ArrayList<Song> songs) {
|
||||
ArrayList<Album> albums = new ArrayList<>();
|
||||
if (songs != null) {
|
||||
for (Song song : songs) {
|
||||
getOrCreateAlbum(albums, song.albumId).subscribe(album -> album.songs.add(song));
|
||||
}
|
||||
}
|
||||
return albums;
|
||||
}
|
||||
|
||||
private static Observable<Album> getOrCreateAlbum(ArrayList<Album> albums, int albumId) {
|
||||
return Observable.create(e -> {
|
||||
for (Album album : albums) {
|
||||
if (!album.songs.isEmpty() && album.songs.get(0).albumId == albumId) {
|
||||
e.onNext(album);
|
||||
e.onComplete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
Album album = new Album();
|
||||
albums.add(album);
|
||||
e.onNext(album);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
public static String getSongLoaderSortOrder(Context context) {
|
||||
return PreferenceUtil.getInstance(context).getAlbumSortOrder() + ", " +
|
||||
//PreferenceUtil.getInstance(context).getAlbumSongSortOrder() + "," +
|
||||
PreferenceUtil.getInstance(context).getAlbumDetailSongSortOrder();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.MediaStore.Audio.AudioColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import code.name.monkey.retromusic.model.Album;
|
||||
import code.name.monkey.retromusic.model.Artist;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
public class ArtistLoader {
|
||||
public static String getSongLoaderSortOrder(Context context) {
|
||||
return PreferenceUtil.getInstance(context).getArtistSortOrder() + ", " +
|
||||
PreferenceUtil.getInstance(context).getArtistAlbumSortOrder() + ", " +
|
||||
PreferenceUtil.getInstance(context).getAlbumDetailSongSortOrder() + ", " +
|
||||
PreferenceUtil.getInstance(context).getArtistDetailSongSortOrder();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<Artist> getArtist(@NonNull final Context context, int artistId) {
|
||||
return Observable.create(e -> SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||
context,
|
||||
AudioColumns.ARTIST_ID + "=?",
|
||||
new String[]{String.valueOf(artistId)},
|
||||
getSongLoaderSortOrder(context)))
|
||||
.subscribe(songs -> {
|
||||
Artist artist = new Artist(AlbumLoader.splitIntoAlbums(songs));
|
||||
e.onNext(artist);
|
||||
e.onComplete();
|
||||
}));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Artist>> getAllArtists(@NonNull final Context context) {
|
||||
return Observable.create(e -> SongLoader
|
||||
.getSongs(SongLoader.makeSongCursor(
|
||||
context,
|
||||
null,
|
||||
null,
|
||||
getSongLoaderSortOrder(context))
|
||||
).subscribe(songs -> {
|
||||
e.onNext(splitIntoArtists(AlbumLoader.splitIntoAlbums(songs)));
|
||||
e.onComplete();
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Artist>> getArtists(@NonNull final Context context, String query) {
|
||||
return Observable.create(e -> SongLoader.getSongs(SongLoader.makeSongCursor(
|
||||
context,
|
||||
AudioColumns.ARTIST + " LIKE ?",
|
||||
new String[]{"%" + query + "%"},
|
||||
getSongLoaderSortOrder(context))
|
||||
).subscribe(songs -> {
|
||||
e.onNext(splitIntoArtists(AlbumLoader.splitIntoAlbums(songs)));
|
||||
e.onComplete();
|
||||
}));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static ArrayList<Artist> splitIntoArtists(@Nullable final ArrayList<Album> albums) {
|
||||
ArrayList<Artist> artists = new ArrayList<>();
|
||||
if (albums != null) {
|
||||
for (Album album : albums) {
|
||||
getOrCreateArtist(artists, album.getArtistId()).albums.add(album);
|
||||
}
|
||||
}
|
||||
return artists;
|
||||
}
|
||||
|
||||
private static Artist getOrCreateArtist(ArrayList<Artist> artists, int artistId) {
|
||||
for (Artist artist : artists) {
|
||||
if (!artist.albums.isEmpty() && !artist.albums.get(0).songs.isEmpty() && artist.albums.get(0).songs.get(0).artistId == artistId) {
|
||||
return artist;
|
||||
}
|
||||
}
|
||||
Artist album = new Artist();
|
||||
artists.add(album);
|
||||
return album;
|
||||
}
|
||||
|
||||
public static Observable<ArrayList<Artist>> splitIntoArtists(Observable<ArrayList<Album>> albums) {
|
||||
return Observable.create(e -> {
|
||||
ArrayList<Artist> artists = new ArrayList<>();
|
||||
albums.subscribe(localAlbums -> {
|
||||
if (localAlbums != null) {
|
||||
for (Album album : localAlbums) {
|
||||
getOrCreateArtist(artists, album.getArtistId()).albums.add(album);
|
||||
}
|
||||
}
|
||||
e.onNext(artists);
|
||||
e.onComplete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* public static Observable<ArrayList<Artist>> getAllArtists(Context context) {
|
||||
return getArtistsForCursor(makeArtistCursor(context, null, null));
|
||||
}
|
||||
|
||||
public static Observable<Artist> getArtist(Context context, long id) {
|
||||
return getArtist(makeArtistCursor(context, "_id=?", new String[]{String.valueOf(id)}));
|
||||
}
|
||||
|
||||
public static Observable<ArrayList<Artist>> getArtists(Context context, String paramString) {
|
||||
return getArtistsForCursor(makeArtistCursor(context, "artist LIKE ?", new String[]{"%" + paramString + "%"}));
|
||||
}
|
||||
|
||||
private static Cursor makeArtistCursor(Context context, String selection, String[] paramArrayOfString) {
|
||||
final String artistSortOrder = PreferenceUtil.getInstance(context).getArtistSortOrder();
|
||||
Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, new String[]{"_id", "artist", "number_of_albums", "number_of_tracks"}, selection, paramArrayOfString, artistSortOrder);
|
||||
return cursor;
|
||||
}*/
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
|
||||
public class ArtistSongLoader extends SongLoader {
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getArtistSongList(@NonNull final Context context, final int artistId) {
|
||||
return getSongs(makeArtistSongCursor(context, artistId));
|
||||
}
|
||||
|
||||
public static Cursor makeArtistSongCursor(@NonNull final Context context, final int artistId) {
|
||||
try {
|
||||
return makeSongCursor(
|
||||
context,
|
||||
MediaStore.Audio.AudioColumns.ARTIST_ID + "=?",
|
||||
new String[]{
|
||||
String.valueOf(artistId)
|
||||
},
|
||||
PreferenceUtil.getInstance(context).getArtistSongSortOrder()
|
||||
);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore.Audio.Genres;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import code.name.monkey.retromusic.model.Genre;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
public class GenreLoader {
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Genre>> getAllGenres(@NonNull final Context context) {
|
||||
return getGenresFromCursor(context, makeGenreCursor(context));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getSongs(@NonNull final Context context, final int genreId) {
|
||||
// The genres table only stores songs that have a genre specified,
|
||||
// so we need to get songs without a genre a different way.
|
||||
if (genreId == -1) {
|
||||
return getSongsWithNoGenre(context);
|
||||
}
|
||||
|
||||
return SongLoader.getSongs(makeGenreSongCursor(context, genreId));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Genre getGenreFromCursor(@NonNull Context context, @NonNull final Cursor cursor) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String name = cursor.getString(1);
|
||||
final int songCount = getSongs(context, id).blockingFirst().size();
|
||||
return new Genre(id, name, songCount);
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Observable<ArrayList<Song>> getSongsWithNoGenre(@NonNull final Context context) {
|
||||
String selection = BaseColumns._ID + " NOT IN " +
|
||||
"(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)";
|
||||
return SongLoader.getSongs(SongLoader.makeSongCursor(context, selection, null));
|
||||
}
|
||||
|
||||
private static boolean hasSongsWithNoGenre(@NonNull final Context context) {
|
||||
final Cursor allSongsCursor = SongLoader.makeSongCursor(context, null, null);
|
||||
final Cursor allSongsWithGenreCursor = makeAllSongsWithGenreCursor(context);
|
||||
|
||||
if (allSongsCursor == null || allSongsWithGenreCursor == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean hasSongsWithNoGenre = allSongsCursor.getCount() > allSongsWithGenreCursor.getCount();
|
||||
allSongsCursor.close();
|
||||
allSongsWithGenreCursor.close();
|
||||
return hasSongsWithNoGenre;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Cursor makeAllSongsWithGenreCursor(@NonNull final Context context) {
|
||||
try {
|
||||
return context.getContentResolver().query(
|
||||
Uri.parse("content://media/external/audio/genres/all/members"),
|
||||
new String[]{Genres.Members.AUDIO_ID}, null, null, null);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Cursor makeGenreSongCursor(@NonNull final Context context, int genreId) {
|
||||
try {
|
||||
return context.getContentResolver().query(
|
||||
Genres.Members.getContentUri("external", genreId),
|
||||
SongLoader.BASE_PROJECTION, SongLoader.BASE_SELECTION, null, PreferenceUtil.getInstance(context).getSongSortOrder());
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Observable<ArrayList<Genre>> getGenresFromCursor(@NonNull final Context context, @Nullable final Cursor cursor) {
|
||||
return Observable.create(e -> {
|
||||
final ArrayList<Genre> genres = new ArrayList<>();
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
Genre genre = getGenreFromCursor(context, cursor);
|
||||
if (genre.songCount > 0) {
|
||||
genres.add(genre);
|
||||
} else {
|
||||
// try to remove the empty genre from the media store
|
||||
try {
|
||||
context.getContentResolver().delete(Genres.EXTERNAL_CONTENT_URI, Genres._ID + " == " + genre.id, null);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
// nothing we can do then
|
||||
}
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
e.onNext(genres);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static Cursor makeGenreCursor(@NonNull final Context context) {
|
||||
final String[] projection = new String[]{
|
||||
Genres._ID,
|
||||
Genres.NAME
|
||||
};
|
||||
|
||||
try {
|
||||
return context.getContentResolver().query(
|
||||
Genres.EXTERNAL_CONTENT_URI,
|
||||
projection, null, null, PreferenceUtil.getInstance(context).getGenreSortOrder());
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.MediaStore.Audio.AudioColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
public class GenreSongsLoader {
|
||||
|
||||
public static Observable<ArrayList<Song>> getGenreSongsList(@NonNull Context context, @NonNull int genreId) {
|
||||
return Observable.create(e -> {
|
||||
ArrayList<Song> list = new ArrayList<>();
|
||||
Cursor cursor = makeGenreSongCursor(context, genreId);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
list.add(getGenreSongFromCursorImpl(cursor));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
e.onNext((ArrayList<Song>) (List) list);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Song getGenreSongFromCursorImpl(@NonNull Cursor cursor) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String title = cursor.getString(1);
|
||||
final int trackNumber = cursor.getInt(2);
|
||||
final int year = cursor.getInt(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final String data = cursor.getString(5);
|
||||
final int dateModified = cursor.getInt(6);
|
||||
final int albumId = cursor.getInt(7);
|
||||
final String albumName = cursor.getString(8);
|
||||
final int artistId = cursor.getInt(9);
|
||||
final String artistName = cursor.getString(10);
|
||||
|
||||
return new Song(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName);
|
||||
}
|
||||
|
||||
private static Cursor makeGenreSongCursor(Context context, long genreId) {
|
||||
try {
|
||||
return context.getContentResolver().query(
|
||||
MediaStore.Audio.Genres.Members.getContentUri("external", genreId),
|
||||
new String[]{
|
||||
MediaStore.Audio.Playlists.Members.AUDIO_ID,// 0
|
||||
AudioColumns.TITLE,// 1
|
||||
AudioColumns.TRACK,// 2
|
||||
AudioColumns.YEAR,// 3
|
||||
AudioColumns.DURATION,// 4
|
||||
AudioColumns.DATA,// 5
|
||||
AudioColumns.DATE_MODIFIED,// 6
|
||||
AudioColumns.ALBUM_ID,// 7
|
||||
AudioColumns.ALBUM,// 8
|
||||
AudioColumns.ARTIST_ID,// 9
|
||||
AudioColumns.ARTIST,// 10
|
||||
}, SongLoader.BASE_SELECTION, null,
|
||||
MediaStore.Audio.Genres.Members.DEFAULT_SORT_ORDER);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import code.name.monkey.retromusic.model.Playlist;
|
||||
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist;
|
||||
import code.name.monkey.retromusic.model.smartplaylist.HistoryPlaylist;
|
||||
import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist;
|
||||
import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist;
|
||||
import io.reactivex.Observable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class HomeLoader {
|
||||
|
||||
public static Observable<ArrayList<AbsSmartPlaylist>> getRecentAndTopThings(
|
||||
@NonNull Context context) {
|
||||
ArrayList<AbsSmartPlaylist> objects = new ArrayList<>();
|
||||
return Observable.create(e -> {
|
||||
|
||||
new HistoryPlaylist(context).getSongs(context).subscribe(songs -> {
|
||||
if (!songs.isEmpty()) {
|
||||
objects.add(new HistoryPlaylist(context));
|
||||
}
|
||||
});
|
||||
new LastAddedPlaylist(context).getSongs(context).subscribe(songs -> {
|
||||
if (!songs.isEmpty()) {
|
||||
objects.add(new LastAddedPlaylist(context));
|
||||
}
|
||||
});
|
||||
new MyTopTracksPlaylist(context).getSongs(context).subscribe(songs -> {
|
||||
if (!songs.isEmpty()) {
|
||||
objects.add(new MyTopTracksPlaylist(context));
|
||||
}
|
||||
});
|
||||
|
||||
e.onNext(objects);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
public static Observable<ArrayList<Playlist>> getHomeLoader(@NonNull Context context) {
|
||||
ArrayList<Playlist> playlists = new ArrayList<>();
|
||||
PlaylistLoader.getAllPlaylists(context)
|
||||
.subscribe(playlists1 -> {
|
||||
if (playlists1.size() > 0) {
|
||||
for (Playlist playlist : playlists1) {
|
||||
PlaylistSongsLoader.getPlaylistSongList(context, playlist)
|
||||
.subscribe(songs -> {
|
||||
if (songs.size() > 0) {
|
||||
playlists.add(playlist);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return Observable.just(playlists);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import code.name.monkey.retromusic.model.Album;
|
||||
import code.name.monkey.retromusic.model.Artist;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 16/08/17.
|
||||
*/
|
||||
|
||||
public class LastAddedSongsLoader {
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getLastAddedSongs(@NonNull Context context) {
|
||||
return SongLoader.getSongs(makeLastAddedCursor(context));
|
||||
}
|
||||
|
||||
public static Cursor makeLastAddedCursor(@NonNull final Context context) {
|
||||
long cutoff = PreferenceUtil.getInstance(context).getLastAddedCutoff();
|
||||
|
||||
return SongLoader.makeSongCursor(
|
||||
context,
|
||||
MediaStore.Audio.Media.DATE_ADDED + ">?",
|
||||
new String[]{String.valueOf(cutoff)},
|
||||
MediaStore.Audio.Media.DATE_ADDED + " DESC");
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Album>> getLastAddedAlbums(@NonNull Context context) {
|
||||
return AlbumLoader.splitIntoAlbums(getLastAddedSongs(context));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Artist>> getLastAddedArtists(@NonNull Context context) {
|
||||
return ArtistLoader.splitIntoArtists(getLastAddedAlbums(context));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.MediaStore.Audio.PlaylistsColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import code.name.monkey.retromusic.model.Playlist;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 16/08/17.
|
||||
*/
|
||||
|
||||
public class PlaylistLoader {
|
||||
@Nullable
|
||||
public static Cursor makePlaylistCursor(@NonNull final Context context, final String selection, final String[] values) {
|
||||
try {
|
||||
return context.getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
|
||||
new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
PlaylistsColumns.NAME
|
||||
}, selection, values, MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<Playlist> getPlaylist(@Nullable final Cursor cursor) {
|
||||
return Observable.create(e -> {
|
||||
Playlist playlist = new Playlist();
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
playlist = getPlaylistFromCursorImpl(cursor);
|
||||
}
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
|
||||
e.onNext(playlist);
|
||||
e.onComplete();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<Playlist> getPlaylist(@NonNull final Context context, final String playlistName) {
|
||||
return getPlaylist(makePlaylistCursor(
|
||||
context,
|
||||
PlaylistsColumns.NAME + "=?",
|
||||
new String[]{
|
||||
playlistName
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<Playlist> getPlaylist(@NonNull final Context context, final int playlistId) {
|
||||
return getPlaylist(makePlaylistCursor(
|
||||
context,
|
||||
BaseColumns._ID + "=?",
|
||||
new String[]{
|
||||
String.valueOf(playlistId)
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Playlist getPlaylistFromCursorImpl(@NonNull final Cursor cursor) {
|
||||
|
||||
final int id = cursor.getInt(0);
|
||||
final String name = cursor.getString(1);
|
||||
return new Playlist(id, name);
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Playlist>> getAllPlaylists(@Nullable final Cursor cursor) {
|
||||
return Observable.create(e -> {
|
||||
ArrayList<Playlist> playlists = new ArrayList<>();
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
playlists.add(getPlaylistFromCursorImpl(cursor));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
|
||||
e.onNext(playlists);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Playlist>> getAllPlaylists(@NonNull final Context context) {
|
||||
return getAllPlaylists(makePlaylistCursor(context, null, null));
|
||||
}
|
||||
|
||||
public static void deletePlaylists(Context context, long playlistId) {
|
||||
Uri localUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
|
||||
StringBuilder localStringBuilder = new StringBuilder();
|
||||
localStringBuilder.append("_id IN (");
|
||||
localStringBuilder.append((playlistId));
|
||||
localStringBuilder.append(")");
|
||||
context.getContentResolver().delete(localUri, localStringBuilder.toString(), null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.MediaStore.Audio.AudioColumns;
|
||||
|
||||
import code.name.monkey.retromusic.model.AbsCustomPlaylist;
|
||||
import code.name.monkey.retromusic.model.Playlist;
|
||||
import code.name.monkey.retromusic.model.PlaylistSong;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 16/08/17.
|
||||
*/
|
||||
|
||||
public class PlaylistSongsLoader {
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getPlaylistSongList(@NonNull Context context, Playlist playlist) {
|
||||
if (playlist instanceof AbsCustomPlaylist) {
|
||||
return ((AbsCustomPlaylist) playlist).getSongs(context);
|
||||
} else {
|
||||
//noinspection unchecked
|
||||
return getPlaylistSongList(context, playlist.id);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getPlaylistSongList(@NonNull Context context, final int playlistId) {
|
||||
return Observable.create(e -> {
|
||||
ArrayList<PlaylistSong> songs = new ArrayList<>();
|
||||
Cursor cursor = makePlaylistSongCursor(context, playlistId);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
e.onNext((ArrayList<Song>) (List) songs);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static PlaylistSong getPlaylistSongFromCursorImpl(@NonNull Cursor cursor, int playlistId) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String title = cursor.getString(1);
|
||||
final int trackNumber = cursor.getInt(2);
|
||||
final int year = cursor.getInt(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final String data = cursor.getString(5);
|
||||
final int dateModified = cursor.getInt(6);
|
||||
final int albumId = cursor.getInt(7);
|
||||
final String albumName = cursor.getString(8);
|
||||
final int artistId = cursor.getInt(9);
|
||||
final String artistName = cursor.getString(10);
|
||||
final int idInPlaylist = cursor.getInt(11);
|
||||
|
||||
return new PlaylistSong(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, playlistId, idInPlaylist);
|
||||
}
|
||||
|
||||
public static Cursor makePlaylistSongCursor(@NonNull final Context context, final int playlistId) {
|
||||
try {
|
||||
return context.getContentResolver().query(
|
||||
MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
|
||||
new String[]{
|
||||
MediaStore.Audio.Playlists.Members.AUDIO_ID,// 0
|
||||
AudioColumns.TITLE,// 1
|
||||
AudioColumns.TRACK,// 2
|
||||
AudioColumns.YEAR,// 3
|
||||
AudioColumns.DURATION,// 4
|
||||
AudioColumns.DATA,// 5
|
||||
AudioColumns.DATE_MODIFIED,// 6
|
||||
AudioColumns.ALBUM_ID,// 7
|
||||
AudioColumns.ALBUM,// 8
|
||||
AudioColumns.ARTIST_ID,// 9
|
||||
AudioColumns.ARTIST,// 10
|
||||
MediaStore.Audio.Playlists.Members._ID // 11
|
||||
}, SongLoader.BASE_SELECTION, null,
|
||||
MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
public class SearchLoader {
|
||||
|
||||
public static Observable<ArrayList<Object>> searchAll(@NonNull Context context, @NonNull String query) {
|
||||
ArrayList<Object> results = new ArrayList<>();
|
||||
return Observable.create(e -> {
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
SongLoader.getSongs(context, query)
|
||||
.subscribe(songs -> {
|
||||
if (!songs.isEmpty()) {
|
||||
results.add(context.getResources().getString(R.string.songs));
|
||||
results.addAll(songs);
|
||||
}
|
||||
});
|
||||
|
||||
ArtistLoader.getArtists(context, query)
|
||||
.subscribe(artists -> {
|
||||
if (!artists.isEmpty()) {
|
||||
results.add(context.getResources().getString(R.string.artists));
|
||||
results.addAll(artists);
|
||||
}
|
||||
});
|
||||
AlbumLoader.getAlbums(context, query)
|
||||
.subscribe(albums -> {
|
||||
if (!albums.isEmpty()) {
|
||||
results.add(context.getResources().getString(R.string.albums));
|
||||
results.addAll(albums);
|
||||
}
|
||||
});
|
||||
}
|
||||
e.onNext(results);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.MediaStore.Audio.AudioColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import code.name.monkey.retromusic.helper.ShuffleHelper;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.providers.BlacklistStore;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 10/08/17.
|
||||
*/
|
||||
|
||||
public class SongLoader {
|
||||
|
||||
protected static final String BASE_SELECTION =
|
||||
AudioColumns.IS_MUSIC + "=1" + " AND " + AudioColumns.TITLE + " != ''";
|
||||
protected static final String[] BASE_PROJECTION = new String[]{
|
||||
BaseColumns._ID,// 0
|
||||
AudioColumns.TITLE,// 1
|
||||
AudioColumns.TRACK,// 2
|
||||
AudioColumns.YEAR,// 3
|
||||
AudioColumns.DURATION,// 4
|
||||
AudioColumns.DATA,// 5
|
||||
AudioColumns.DATE_MODIFIED,// 6
|
||||
AudioColumns.ALBUM_ID,// 7
|
||||
AudioColumns.ALBUM,// 8
|
||||
AudioColumns.ARTIST_ID,// 9
|
||||
AudioColumns.ARTIST,// 10
|
||||
};
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getAllSongs(@NonNull Context context) {
|
||||
Cursor cursor = makeSongCursor(context, null, null);
|
||||
return getSongs(cursor);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getSongs(@NonNull final Context context,
|
||||
final String query) {
|
||||
Cursor cursor = makeSongCursor(context, AudioColumns.TITLE + " LIKE ?",
|
||||
new String[]{"%" + query + "%"});
|
||||
return getSongs(cursor);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getSongs(@Nullable final Cursor cursor) {
|
||||
return Observable.create(e -> {
|
||||
ArrayList<Song> songs = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
songs.add(getSongFromCursorImpl(cursor));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
e.onNext(songs);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Song getSongFromCursorImpl(@NonNull Cursor cursor) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String title = cursor.getString(1);
|
||||
final int trackNumber = cursor.getInt(2);
|
||||
final int year = cursor.getInt(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final String data = cursor.getString(5);
|
||||
final long dateModified = cursor.getLong(6);
|
||||
final int albumId = cursor.getInt(7);
|
||||
final String albumName = cursor.getString(8);
|
||||
final int artistId = cursor.getInt(9);
|
||||
final String artistName = cursor.getString(10);
|
||||
|
||||
return new Song(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName,
|
||||
artistId, artistName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Cursor makeSongCursor(@NonNull final Context context,
|
||||
@Nullable final String selection, final String[] selectionValues) {
|
||||
return makeSongCursor(context, selection, selectionValues,
|
||||
PreferenceUtil.getInstance(context).getSongSortOrder());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Cursor makeSongCursor(@NonNull final Context context, @Nullable String selection,
|
||||
String[] selectionValues, final String sortOrder) {
|
||||
if (selection != null && !selection.trim().equals("")) {
|
||||
selection = BASE_SELECTION + " AND " + selection;
|
||||
} else {
|
||||
selection = BASE_SELECTION;
|
||||
}
|
||||
|
||||
// Blacklist
|
||||
ArrayList<String> paths = BlacklistStore.getInstance(context).getPaths();
|
||||
if (!paths.isEmpty()) {
|
||||
selection = generateBlacklistSelection(selection, paths.size());
|
||||
selectionValues = addBlacklistSelectionValues(selectionValues, paths);
|
||||
}
|
||||
|
||||
try {
|
||||
return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
BASE_PROJECTION, selection, selectionValues, sortOrder);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateBlacklistSelection(String selection, int pathCount) {
|
||||
StringBuilder newSelection = new StringBuilder(
|
||||
selection != null && !selection.trim().equals("") ? selection + " AND " : "");
|
||||
newSelection.append(AudioColumns.DATA + " NOT LIKE ?");
|
||||
for (int i = 0; i < pathCount - 1; i++) {
|
||||
newSelection.append(" AND " + AudioColumns.DATA + " NOT LIKE ?");
|
||||
}
|
||||
return newSelection.toString();
|
||||
}
|
||||
|
||||
private static String[] addBlacklistSelectionValues(String[] selectionValues,
|
||||
ArrayList<String> paths) {
|
||||
if (selectionValues == null) {
|
||||
selectionValues = new String[0];
|
||||
}
|
||||
String[] newSelectionValues = new String[selectionValues.length + paths.size()];
|
||||
System.arraycopy(selectionValues, 0, newSelectionValues, 0, selectionValues.length);
|
||||
for (int i = selectionValues.length; i < newSelectionValues.length; i++) {
|
||||
newSelectionValues[i] = paths.get(i - selectionValues.length) + "%";
|
||||
}
|
||||
return newSelectionValues;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<Song> getSong(@Nullable Cursor cursor) {
|
||||
return Observable.create(e -> {
|
||||
Song song;
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
song = getSongFromCursorImpl(cursor);
|
||||
} else {
|
||||
song = Song.EMPTY_SONG;
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
e.onNext(song);
|
||||
e.onComplete();
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<Song> getSong(@NonNull final Context context, final int queryId) {
|
||||
Cursor cursor = makeSongCursor(context, AudioColumns._ID + "=?",
|
||||
new String[]{String.valueOf(queryId)});
|
||||
return getSong(cursor);
|
||||
}
|
||||
|
||||
public static Observable<ArrayList<Song>> suggestSongs(@NonNull Context context) {
|
||||
return Observable.create(observer -> {
|
||||
SongLoader.getAllSongs(context)
|
||||
.subscribe(songs -> {
|
||||
ArrayList<Song> list = new ArrayList<>();
|
||||
if (songs.isEmpty()) {
|
||||
observer.onNext(new ArrayList<>());
|
||||
observer.onComplete();
|
||||
return;
|
||||
}
|
||||
ShuffleHelper.makeShuffleList(songs, -1);
|
||||
if (songs.size() > 10) {
|
||||
list.addAll(songs.subList(0, 10));
|
||||
} else {
|
||||
list.addAll(songs);
|
||||
}
|
||||
observer.onNext(list);
|
||||
observer.onComplete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The CyanogenMod Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.database.AbstractCursor;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This cursor basically wraps a song cursor and is given a list of the order of the ids of the
|
||||
* contents of the cursor. It wraps the Cursor and simulates the internal cursor being sorted
|
||||
* by moving the point to the appropriate spot
|
||||
*/
|
||||
public class SortedCursor extends AbstractCursor {
|
||||
// cursor to wrap
|
||||
private final Cursor mCursor;
|
||||
// the map of external indices to internal indices
|
||||
private ArrayList<Integer> mOrderedPositions;
|
||||
// this contains the ids that weren't found in the underlying cursor
|
||||
private ArrayList<String> mMissingValues;
|
||||
// this contains the mapped cursor positions and afterwards the extra ids that weren't found
|
||||
private HashMap<String, Integer> mMapCursorPositions;
|
||||
|
||||
/**
|
||||
* @param cursor to wrap
|
||||
* @param order the list of unique ids in sorted order to display
|
||||
* @param columnName the column name of the id to look up in the internal cursor
|
||||
*/
|
||||
public SortedCursor(@NonNull final Cursor cursor, @Nullable final String[] order, final String columnName) {
|
||||
mCursor = cursor;
|
||||
mMissingValues = buildCursorPositionMapping(order, columnName);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function populates mOrderedPositions with the cursor positions in the order based
|
||||
* on the order passed in
|
||||
*
|
||||
* @param order the target order of the internal cursor
|
||||
* @return returns the ids that aren't found in the underlying cursor
|
||||
*/
|
||||
@NonNull
|
||||
private ArrayList<String> buildCursorPositionMapping(@Nullable final String[] order, final String columnName) {
|
||||
ArrayList<String> missingValues = new ArrayList<>();
|
||||
|
||||
mOrderedPositions = new ArrayList<>(mCursor.getCount());
|
||||
|
||||
mMapCursorPositions = new HashMap<>(mCursor.getCount());
|
||||
final int valueColumnIndex = mCursor.getColumnIndex(columnName);
|
||||
|
||||
if (mCursor.moveToFirst()) {
|
||||
// first figure out where each of the ids are in the cursor
|
||||
do {
|
||||
mMapCursorPositions.put(mCursor.getString(valueColumnIndex), mCursor.getPosition());
|
||||
} while (mCursor.moveToNext());
|
||||
|
||||
if (order != null) {
|
||||
// now create the ordered positions to map to the internal cursor given the
|
||||
// external sort order
|
||||
for (final String value : order) {
|
||||
if (mMapCursorPositions.containsKey(value)) {
|
||||
mOrderedPositions.add(mMapCursorPositions.get(value));
|
||||
mMapCursorPositions.remove(value);
|
||||
} else {
|
||||
missingValues.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mCursor.moveToFirst();
|
||||
}
|
||||
|
||||
return missingValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of ids that weren't found in the underlying cursor
|
||||
*/
|
||||
public ArrayList<String> getMissingValues() {
|
||||
return mMissingValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of ids that were in the underlying cursor but not part of the ordered list
|
||||
*/
|
||||
@NonNull
|
||||
public Collection<String> getExtraValues() {
|
||||
return mMapCursorPositions.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mCursor.close();
|
||||
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mOrderedPositions.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return mCursor.getColumnNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int column) {
|
||||
return mCursor.getString(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int column) {
|
||||
return mCursor.getShort(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int column) {
|
||||
return mCursor.getInt(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int column) {
|
||||
return mCursor.getLong(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int column) {
|
||||
return mCursor.getFloat(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int column) {
|
||||
return mCursor.getDouble(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(int column) {
|
||||
return mCursor.isNull(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
if (newPosition >= 0 && newPosition < getCount()) {
|
||||
mCursor.moveToPosition(mOrderedPositions.get(newPosition));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The CyanogenMod Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.database.AbstractCursor;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This cursor basically wraps a song cursor and is given a list of the order of the ids of the
|
||||
* contents of the cursor. It wraps the Cursor and simulates the internal cursor being sorted
|
||||
* by moving the point to the appropriate spot
|
||||
*/
|
||||
public class SortedLongCursor extends AbstractCursor {
|
||||
// cursor to wrap
|
||||
private final Cursor mCursor;
|
||||
// the map of external indices to internal indices
|
||||
private ArrayList<Integer> mOrderedPositions;
|
||||
// this contains the ids that weren't found in the underlying cursor
|
||||
private ArrayList<Long> mMissingIds;
|
||||
// this contains the mapped cursor positions and afterwards the extra ids that weren't found
|
||||
private HashMap<Long, Integer> mMapCursorPositions;
|
||||
|
||||
/**
|
||||
* @param cursor to wrap
|
||||
* @param order the list of unique ids in sorted order to display
|
||||
* @param columnName the column name of the id to look up in the internal cursor
|
||||
*/
|
||||
public SortedLongCursor(final Cursor cursor, final long[] order, final String columnName) {
|
||||
|
||||
mCursor = cursor;
|
||||
mMissingIds = buildCursorPositionMapping(order, columnName);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function populates mOrderedPositions with the cursor positions in the order based
|
||||
* on the order passed in
|
||||
*
|
||||
* @param order the target order of the internal cursor
|
||||
* @return returns the ids that aren't found in the underlying cursor
|
||||
*/
|
||||
@NonNull
|
||||
private ArrayList<Long> buildCursorPositionMapping(@Nullable final long[] order, final String columnName) {
|
||||
ArrayList<Long> missingIds = new ArrayList<>();
|
||||
|
||||
mOrderedPositions = new ArrayList<>(mCursor.getCount());
|
||||
|
||||
mMapCursorPositions = new HashMap<>(mCursor.getCount());
|
||||
final int idPosition = mCursor.getColumnIndex(columnName);
|
||||
|
||||
if (mCursor.moveToFirst()) {
|
||||
// first figure out where each of the ids are in the cursor
|
||||
do {
|
||||
mMapCursorPositions.put(mCursor.getLong(idPosition), mCursor.getPosition());
|
||||
} while (mCursor.moveToNext());
|
||||
|
||||
// now create the ordered positions to map to the internal cursor given the
|
||||
// external sort order
|
||||
for (int i = 0; order != null && i < order.length; i++) {
|
||||
final long id = order[i];
|
||||
if (mMapCursorPositions.containsKey(id)) {
|
||||
mOrderedPositions.add(mMapCursorPositions.get(id));
|
||||
mMapCursorPositions.remove(id);
|
||||
} else {
|
||||
missingIds.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
mCursor.moveToFirst();
|
||||
}
|
||||
|
||||
return missingIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of ids that weren't found in the underlying cursor
|
||||
*/
|
||||
public ArrayList<Long> getMissingIds() {
|
||||
return mMissingIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of ids that were in the underlying cursor but not part of the ordered list
|
||||
*/
|
||||
@NonNull
|
||||
public Collection<Long> getExtraIds() {
|
||||
return mMapCursorPositions.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mCursor.close();
|
||||
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mOrderedPositions.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return mCursor.getColumnNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int column) {
|
||||
return mCursor.getString(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int column) {
|
||||
return mCursor.getShort(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int column) {
|
||||
return mCursor.getInt(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int column) {
|
||||
return mCursor.getLong(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int column) {
|
||||
return mCursor.getFloat(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int column) {
|
||||
return mCursor.getDouble(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(int column) {
|
||||
return mCursor.isNull(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
if (newPosition >= 0 && newPosition < getCount()) {
|
||||
mCursor.moveToPosition(mOrderedPositions.get(newPosition));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package code.name.monkey.retromusic.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import code.name.monkey.retromusic.model.Album;
|
||||
import code.name.monkey.retromusic.model.Artist;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.providers.HistoryStore;
|
||||
import code.name.monkey.retromusic.providers.SongPlayCountStore;
|
||||
import io.reactivex.Observable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 16/08/17.
|
||||
*/
|
||||
|
||||
public class TopAndRecentlyPlayedTracksLoader {
|
||||
|
||||
private static final int NUMBER_OF_TOP_TRACKS = 99;
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getRecentlyPlayedTracks(@NonNull Context context) {
|
||||
return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Song>> getTopTracks(@NonNull Context context) {
|
||||
return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Cursor makeRecentTracksCursorAndClearUpDatabase(@NonNull final Context context) {
|
||||
SortedLongCursor retCursor = makeRecentTracksCursorImpl(context);
|
||||
|
||||
// clean up the databases with any ids not found
|
||||
if (retCursor != null) {
|
||||
ArrayList<Long> missingIds = retCursor.getMissingIds();
|
||||
if (missingIds != null && missingIds.size() > 0) {
|
||||
for (long id : missingIds) {
|
||||
HistoryStore.getInstance(context).removeSongId(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retCursor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Cursor makeTopTracksCursorAndClearUpDatabase(@NonNull final Context context) {
|
||||
SortedLongCursor retCursor = makeTopTracksCursorImpl(context);
|
||||
|
||||
// clean up the databases with any ids not found
|
||||
if (retCursor != null) {
|
||||
ArrayList<Long> missingIds = retCursor.getMissingIds();
|
||||
if (missingIds != null && missingIds.size() > 0) {
|
||||
for (long id : missingIds) {
|
||||
SongPlayCountStore.getInstance(context).removeItem(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retCursor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static SortedLongCursor makeRecentTracksCursorImpl(@NonNull final Context context) {
|
||||
// first get the top results ids from the internal database
|
||||
Cursor songs = HistoryStore.getInstance(context).queryRecentIds();
|
||||
|
||||
try {
|
||||
return makeSortedCursor(context, songs,
|
||||
songs.getColumnIndex(HistoryStore.RecentStoreColumns.ID));
|
||||
} finally {
|
||||
if (songs != null) {
|
||||
songs.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static SortedLongCursor makeTopTracksCursorImpl(@NonNull final Context context) {
|
||||
// first get the top results ids from the internal database
|
||||
Cursor songs = SongPlayCountStore.getInstance(context)
|
||||
.getTopPlayedResults(NUMBER_OF_TOP_TRACKS);
|
||||
|
||||
try {
|
||||
return makeSortedCursor(context, songs,
|
||||
songs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID));
|
||||
} finally {
|
||||
if (songs != null) {
|
||||
songs.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static SortedLongCursor makeSortedCursor(@NonNull final Context context,
|
||||
@Nullable final Cursor cursor, final int idColumn) {
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
// create the list of ids to select against
|
||||
StringBuilder selection = new StringBuilder();
|
||||
selection.append(BaseColumns._ID);
|
||||
selection.append(" IN (");
|
||||
|
||||
// this tracks the order of the ids
|
||||
long[] order = new long[cursor.getCount()];
|
||||
|
||||
long id = cursor.getLong(idColumn);
|
||||
selection.append(id);
|
||||
order[cursor.getPosition()] = id;
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
selection.append(",");
|
||||
|
||||
id = cursor.getLong(idColumn);
|
||||
order[cursor.getPosition()] = id;
|
||||
selection.append(String.valueOf(id));
|
||||
}
|
||||
|
||||
selection.append(")");
|
||||
|
||||
// get a list of songs with the data given the selection statement
|
||||
Cursor songCursor = SongLoader.makeSongCursor(context, selection.toString(), null);
|
||||
if (songCursor != null) {
|
||||
// now return the wrapped TopTracksCursor to handle sorting given order
|
||||
return new SortedLongCursor(songCursor, order, BaseColumns._ID);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Album>> getTopAlbums(@NonNull Context context) {
|
||||
return Observable.create(e -> {
|
||||
getTopTracks(context).subscribe(songs -> {
|
||||
if (songs.size() > 0) {
|
||||
e.onNext(AlbumLoader.splitIntoAlbums(songs));
|
||||
}
|
||||
e.onComplete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Observable<ArrayList<Artist>> getTopArtists(@NonNull Context context) {
|
||||
return Observable.create(e -> {
|
||||
getTopAlbums(context).subscribe(albums -> {
|
||||
if (albums.size() > 0) {
|
||||
e.onNext(ArtistLoader.splitIntoArtists(albums));
|
||||
}
|
||||
e.onComplete();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue