Initial commit retro music app

This commit is contained in:
h4h13 2018-07-27 18:37:33 +05:30
parent ab332473bc
commit fe890632fd
932 changed files with 83126 additions and 0 deletions

View file

@ -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();
}
}

View file

@ -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;
}*/
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}

View file

@ -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));
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}
}

View file

@ -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();
});
}
}

View file

@ -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();
});
});
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
});
});
}
}