Code refactor

main
h4h13 2020-01-29 22:55:43 +05:30
parent 3f27463281
commit 1096cea0b4
26 changed files with 353 additions and 486 deletions

View File

@ -16,9 +16,9 @@ android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 21
targetSdkVersion 28
targetSdkVersion 29
renderscriptTargetApi 28 //must match target sdk and build tools
renderscriptTargetApi 29 //must match target sdk and build tools
vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic"
@ -70,7 +70,7 @@ android {
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/rxjava.properties'
exclude 'META-INF/java.properties'
}
lintOptions {
disable 'MissingTranslation'
@ -138,7 +138,6 @@ dependencies {
def retrofit_version = "2.6.2"
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

View File

@ -9,7 +9,8 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.WRITE_SETTINGS"
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.vending.BILLING" />
@ -17,11 +18,11 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:requestLegacyExternalStorage="true"
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.RetroMusic.FollowSystem"
@ -30,8 +31,8 @@
tools:targetApi="m">
<activity
android:name=".activities.MainActivity"
android:theme="@style/SplashTheme"
android:label="@string/app_name" >
android:label="@string/app_name"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.MUSIC_PLAYER" />

View File

@ -25,7 +25,6 @@ import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.AppRater
import code.name.monkey.retromusic.util.PreferenceUtil
import io.reactivex.disposables.CompositeDisposable
import java.util.ArrayList
class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
@ -33,7 +32,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
private lateinit var currentFragment: MainActivityFragmentCallbacks
private var blockRequestPermissions: Boolean = false
private val disposable = CompositeDisposable()
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
@ -107,7 +106,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
override fun onDestroy() {
super.onDestroy()
disposable.clear()
unregisterReceiver(broadcastReceiver)
PreferenceUtil.getInstance(this).unregisterOnSharedPreferenceChangedListener(this)
}

View File

@ -28,7 +28,6 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.activity_album_tag_editor.albumArtistContainer
import kotlinx.android.synthetic.main.activity_album_tag_editor.albumArtistText
import kotlinx.android.synthetic.main.activity_album_tag_editor.albumText
@ -92,7 +91,6 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
private var albumArtBitmap: Bitmap? = null
private var deleteAlbumArt: Boolean = false
private var lastFMRestClient: LastFMRestClient? = null
private val disposable = CompositeDisposable()
private fun setupToolbar() {
applyToolbar(toolbar)
@ -145,7 +143,6 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
override fun onPause() {
super.onPause()
disposable.clear()
}
private fun toastLoadingFailed() {
@ -178,7 +175,8 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
fieldKeyValueMap[FieldKey.GENRE] = genreTitle.text.toString()
fieldKeyValueMap[FieldKey.YEAR] = yearTitle.text.toString()
writeValuesToFiles(fieldKeyValueMap,
writeValuesToFiles(
fieldKeyValueMap,
if (deleteAlbumArt) ArtworkInfo(id, null)
else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!)
)

View File

@ -25,7 +25,6 @@ import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.util.NavigationUtil
import com.bumptech.glide.Glide
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.fragment_full.artistImage
import kotlinx.android.synthetic.main.fragment_full.nextSong
import kotlinx.android.synthetic.main.fragment_full.nextSongLabel
@ -226,11 +225,8 @@ class FullPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca
override fun onDestroyView() {
super.onDestroyView()
progressViewUpdateHelper.stop()
compositeDisposable.dispose()
}
private val compositeDisposable = CompositeDisposable()
private fun updateArtistImage() {
CoroutineScope(Dispatchers.IO).launch {
val artist = ArtistLoader.getArtist(requireContext(), MusicPlayerRemote.currentSong.artistId)

View File

@ -23,9 +23,7 @@ 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 io.reactivex.Observable
import java.util.*
import java.util.ArrayList
/**
* Created by hemanths on 16/08/17.
@ -33,37 +31,12 @@ import java.util.*
object PlaylistSongsLoader {
fun getPlaylistSongListFlowable(
context: Context,
playlist: Playlist
): Observable<ArrayList<Song>> {
return (playlist as? AbsCustomPlaylist)?.getSongsFlowable(context)
?: getPlaylistSongListFlowable(context, playlist.id)
}
fun getPlaylistSongList(
context: Context,
playlist: Playlist
context: Context,
playlist: Playlist
): ArrayList<Song> {
return (playlist as? AbsCustomPlaylist)?.getSongs(context)
?: getPlaylistSongList(context, playlist.id)
}
fun getPlaylistSongListFlowable(context: Context, playlistId: Int): Observable<ArrayList<Song>> {
return Observable.create { e ->
val songs = ArrayList<Song>()
val cursor = makePlaylistSongCursor(context, playlistId)
if (cursor != null && cursor.moveToFirst()) {
do {
songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId))
} while (cursor.moveToNext())
}
cursor?.close()
e.onNext(songs)
e.onComplete()
}
?: getPlaylistSongList(context, playlist.id)
}
fun getPlaylistSongList(context: Context, playlistId: Int): ArrayList<Song> {
@ -79,7 +52,6 @@ object PlaylistSongsLoader {
return songs
}
private fun getPlaylistSongFromCursorImpl(cursor: Cursor, playlistId: Int): PlaylistSong {
val id = cursor.getInt(0)
val title = cursor.getString(1)
@ -95,28 +67,46 @@ object PlaylistSongsLoader {
val idInPlaylist = cursor.getInt(11)
val composer = cursor.getString(12)
return PlaylistSong(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, playlistId, idInPlaylist, composer)
return PlaylistSong(
id,
title,
trackNumber,
year,
duration,
data,
dateModified,
albumId,
albumName,
artistId,
artistName,
playlistId,
idInPlaylist,
composer
)
}
private fun makePlaylistSongCursor(context: Context, playlistId: Int): Cursor? {
try {
return context.contentResolver.query(
MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId.toLong()),
arrayOf(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
AudioColumns.COMPOSER)// 12
, BASE_SELECTION, null,
MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER)
MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId.toLong()),
arrayOf(
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
AudioColumns.COMPOSER
)// 12
, BASE_SELECTION, null,
MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER
)
} catch (e: SecurityException) {
return null
}

View File

@ -20,52 +20,27 @@ import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns
import code.name.monkey.retromusic.Constants.BASE_SELECTION
import code.name.monkey.retromusic.Constants.baseProjection
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
import java.util.*
import java.util.ArrayList
/**
* Created by hemanths on 10/08/17.
*/
object SongLoader {
fun getAllSongsFlowable(
context: Context
): Observable<ArrayList<Song>> {
val cursor = makeSongCursor(context, null, null)
return getSongsFlowable(cursor)
}
fun getAllSongs(
context: Context
context: Context
): ArrayList<Song> {
val cursor = makeSongCursor(context, null, null)
return getSongs(cursor)
}
fun getSongsFlowable(
cursor: Cursor?
): Observable<ArrayList<Song>> {
return Observable.create { e ->
val songs = ArrayList<Song>()
if (cursor != null && cursor.moveToFirst()) {
do {
songs.add(getSongFromCursorImpl(cursor))
} while (cursor.moveToNext())
}
cursor?.close()
e.onNext(songs)
e.onComplete()
}
}
fun getSongs(
cursor: Cursor?
cursor: Cursor?
): ArrayList<Song> {
val songs = arrayListOf<Song>()
if (cursor != null && cursor.moveToFirst()) {
@ -78,40 +53,16 @@ object SongLoader {
return songs
}
fun getSongsFlowable(
context: Context,
query: String
): Observable<ArrayList<Song>> {
val cursor = makeSongCursor(context, AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%"))
return getSongsFlowable(cursor)
}
fun getSongs(
context: Context,
query: String
context: Context,
query: String
): ArrayList<Song> {
val cursor = makeSongCursor(context, AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%"))
return getSongs(cursor)
}
private fun getSongFlowable(
cursor: Cursor?
): Observable<Song> {
return Observable.create { e ->
val song: Song = if (cursor != null && cursor.moveToFirst()) {
getSongFromCursorImpl(cursor)
} else {
Song.emptySong
}
cursor?.close()
e.onNext(song)
e.onComplete()
}
}
fun getSong(
cursor: Cursor?
cursor: Cursor?
): Song {
val song: Song
if (cursor != null && cursor.moveToFirst()) {
@ -123,36 +74,13 @@ object SongLoader {
return song
}
fun getSongFlowable(
context: Context,
queryId: Int
): Observable<Song> {
val cursor = makeSongCursor(context, AudioColumns._ID + "=?",
arrayOf(queryId.toString()))
return getSongFlowable(cursor)
}
fun getSong(context: Context, queryId: Int): Song {
val cursor = makeSongCursor(context, AudioColumns._ID + "=?", arrayOf(queryId.toString()))
return getSong(cursor)
}
fun suggestSongs(
context: Context
): Observable<ArrayList<Song>> {
return SongLoader.getAllSongsFlowable(context)
.flatMap {
val list = ArrayList<Song>()
ShuffleHelper.makeShuffleList(it, -1)
if (it.size >= 7) {
list.addAll(it.subList(0, 7))
}
return@flatMap Observable.just(list)
}
}
private fun getSongFromCursorImpl(
cursor: Cursor
cursor: Cursor
): Song {
val id = cursor.getInt(0)
val title = cursor.getString(1)
@ -167,17 +95,18 @@ object SongLoader {
val artistName = cursor.getString(10)
val composer = cursor.getString(11)
return Song(id, title, trackNumber, year, duration, data, dateModified, albumId,
albumName ?: "", artistId, artistName, composer ?: "")
return Song(
id, title, trackNumber, year, duration, data, dateModified, albumId,
albumName ?: "", artistId, artistName, composer ?: ""
)
}
@JvmOverloads
fun makeSongCursor(
context: Context,
selection: String?,
selectionValues: Array<String>?,
sortOrder: String = PreferenceUtil.getInstance(context).songSortOrder
context: Context,
selection: String?,
selectionValues: Array<String>?,
sortOrder: String = PreferenceUtil.getInstance(context).songSortOrder
): Cursor? {
var selectionFinal = selection
var selectionValuesFinal = selectionValues
@ -195,20 +124,26 @@ object SongLoader {
}
try {
return context.contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
baseProjection, selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.getInstance(context).filterLength * 1000), selectionValuesFinal, sortOrder)
return context.contentResolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
baseProjection,
selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.getInstance(
context
).filterLength * 1000),
selectionValuesFinal,
sortOrder
)
} catch (e: SecurityException) {
return null
}
}
private fun generateBlacklistSelection(
selection: String?,
pathCount: Int
selection: String?,
pathCount: Int
): String {
val newSelection = StringBuilder(
if (selection != null && selection.trim { it <= ' ' } != "") "$selection AND " else "")
if (selection != null && selection.trim { it <= ' ' } != "") "$selection AND " else "")
newSelection.append(AudioColumns.DATA + " NOT LIKE ?")
for (i in 0 until pathCount - 1) {
newSelection.append(" AND " + AudioColumns.DATA + " NOT LIKE ?")
@ -217,8 +152,8 @@ object SongLoader {
}
private fun addBlacklistSelectionValues(
selectionValues: Array<String>?,
paths: ArrayList<String>
selectionValues: Array<String>?,
paths: ArrayList<String>
): Array<String>? {
var selectionValuesFinal = selectionValues
if (selectionValuesFinal == null) {

View File

@ -23,8 +23,7 @@ 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.*
import java.util.ArrayList
/**
* Created by hemanths on 16/08/17.
@ -32,18 +31,10 @@ import java.util.*
object TopAndRecentlyPlayedTracksLoader {
fun getRecentlyPlayedTracksFlowable(context: Context): Observable<ArrayList<Song>> {
return SongLoader.getSongsFlowable(makeRecentTracksCursorAndClearUpDatabase(context))
}
fun getRecentlyPlayedTracks(context: Context): ArrayList<Song> {
return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context))
}
fun getTopTracksFlowable(context: Context): Observable<ArrayList<Song>> {
return SongLoader.getSongsFlowable(makeTopTracksCursorAndClearUpDatabase(context))
}
fun getTopTracks(context: Context): ArrayList<Song> {
return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context))
}
@ -83,8 +74,10 @@ object TopAndRecentlyPlayedTracksLoader {
val songs = HistoryStore.getInstance(context).queryRecentIds()
try {
return makeSortedCursor(context, songs,
songs!!.getColumnIndex(HistoryStore.RecentStoreColumns.ID))
return makeSortedCursor(
context, songs,
songs!!.getColumnIndex(HistoryStore.RecentStoreColumns.ID)
)
} finally {
songs?.close()
}
@ -93,16 +86,20 @@ object TopAndRecentlyPlayedTracksLoader {
private fun makeTopTracksCursorImpl(context: Context): SortedLongCursor? {
// first get the top results ids from the internal database
val songs = SongPlayCountStore.getInstance(context)
.getTopPlayedResults(NUMBER_OF_TOP_TRACKS)
.getTopPlayedResults(NUMBER_OF_TOP_TRACKS)
songs.use { localSongs ->
return makeSortedCursor(context, localSongs,
localSongs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID))
return makeSortedCursor(
context, localSongs,
localSongs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID)
)
}
}
private fun makeSortedCursor(context: Context,
cursor: Cursor?, idColumn: Int): SortedLongCursor? {
private fun makeSortedCursor(
context: Context,
cursor: Cursor?, idColumn: Int
): SortedLongCursor? {
if (cursor != null && cursor.moveToFirst()) {
// create the list of ids to select against
@ -138,37 +135,13 @@ object TopAndRecentlyPlayedTracksLoader {
return null
}
fun getTopAlbumsFlowable(
context: Context
): Observable<ArrayList<Album>> {
return Observable.create { e ->
getTopTracksFlowable(context).subscribe { songs ->
if (songs.size > 0) {
e.onNext(AlbumLoader.splitIntoAlbums(songs))
}
e.onComplete()
}
}
}
fun getTopAlbums(
context: Context
context: Context
): ArrayList<Album> {
arrayListOf<Album>()
return AlbumLoader.splitIntoAlbums(getTopTracks(context))
}
fun getTopArtistsFlowable(context: Context): Observable<ArrayList<Artist>> {
return Observable.create { e ->
getTopAlbumsFlowable(context).subscribe { albums ->
if (albums.size > 0) {
e.onNext(ArtistLoader.splitIntoArtists(albums))
}
e.onComplete()
}
}
}
fun getTopArtists(context: Context): ArrayList<Artist> {
return ArtistLoader.splitIntoArtists(getTopAlbums(context))
}

View File

@ -16,18 +16,16 @@ package code.name.monkey.retromusic.model;
import android.content.Context;
import android.os.Parcel;
import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public abstract class AbsCustomPlaylist extends Playlist {
public AbsCustomPlaylist(int id, String name) {
super(id, name);
}

View File

@ -14,8 +14,7 @@
package code.name.monkey.retromusic.model
import java.util.*
import java.util.ArrayList
class Album {
val songs: ArrayList<Song>?

View File

@ -15,8 +15,7 @@
package code.name.monkey.retromusic.model
import code.name.monkey.retromusic.util.MusicUtil
import java.util.*
import java.util.ArrayList
class Artist {
val albums: ArrayList<Album>?

View File

@ -16,4 +16,8 @@ package code.name.monkey.retromusic.model
import com.google.gson.annotations.SerializedName
class Contributor(val name: String, val summary: String, val link: String, @SerializedName("profile_image") val profileImage: String)
class Contributor(
val name: String,
val summary: String,
val link: String, @SerializedName("profile_image") val profileImage: String
)

View File

@ -18,10 +18,12 @@ import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import code.name.monkey.retromusic.adapter.HomeAdapter.Companion.HomeSection
class Home(val priority: Int,
@StringRes val title: Int,
val arrayList: ArrayList<*>,
@HomeSection
val homeSection: Int,
@DrawableRes
val icon: Int)
class Home(
val priority: Int,
@StringRes val title: Int,
val arrayList: ArrayList<*>,
@HomeSection
val homeSection: Int,
@DrawableRes
val icon: Int
)

View File

@ -17,16 +17,13 @@ package code.name.monkey.retromusic.model;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader;
import io.reactivex.Observable;
import java.util.ArrayList;
public class Playlist implements Parcelable {
public static final Creator<Playlist> CREATOR = new Creator<Playlist>() {
public Playlist createFromParcel(Parcel source) {
return new Playlist(source);
@ -36,7 +33,9 @@ public class Playlist implements Parcelable {
return new Playlist[size];
}
};
public final int id;
public final String name;
public Playlist(final int id, final String name) {
@ -54,30 +53,35 @@ public class Playlist implements Parcelable {
this.name = in.readString();
}
@NonNull
public Observable<ArrayList<Song>> getSongsFlowable(@NonNull Context context) {
// this default implementation covers static playlists
return PlaylistSongsLoader.INSTANCE.getPlaylistSongListFlowable(context, id);
}
@NonNull
public ArrayList<Song> getSongs(@NonNull Context context) {
// this default implementation covers static playlists
return PlaylistSongsLoader.INSTANCE.getPlaylistSongList(context, id);
@Override
public int describeContents() {
return 0;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Playlist playlist = (Playlist) o;
if (id != playlist.id) return false;
if (id != playlist.id) {
return false;
}
return name != null ? name.equals(playlist.name) : playlist.name == null;
}
@NonNull
public ArrayList<Song> getSongs(@NonNull Context context) {
// this default implementation covers static playlists
return PlaylistSongsLoader.INSTANCE.getPlaylistSongList(context, id);
}
@Override
public int hashCode() {
int result = id;
@ -93,11 +97,6 @@ public class Playlist implements Parcelable {
'}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.id);

View File

@ -14,42 +14,44 @@
package code.name.monkey.retromusic.model;
import org.jetbrains.annotations.NotNull;
import kotlinx.android.parcel.Parcelize;
import org.jetbrains.annotations.NotNull;
/**
* Created by hemanths on 3/4/19
*/
@Parcelize
public class PlaylistSong extends Song {
final int playlistId;
final int idInPlayList;
final int playlistId;
public PlaylistSong(int id,
@NotNull String title,
int trackNumber,
int year,
long duration,
@NotNull String data,
long dateModified,
int albumId,
@NotNull String albumName,
int artistId,
@NotNull String artistName,
int playlistId,
int idInPlayList,
@NotNull String composer) {
super(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, composer);
@NotNull String title,
int trackNumber,
int year,
long duration,
@NotNull String data,
long dateModified,
int albumId,
@NotNull String albumName,
int artistId,
@NotNull String artistName,
int playlistId,
int idInPlayList,
@NotNull String composer) {
super(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName,
composer);
this.playlistId = playlistId;
this.idInPlayList = idInPlayList;
}
public int getPlaylistId() {
return playlistId;
}
public int getIdInPlayList() {
return idInPlayList;
}
public int getPlaylistId() {
return playlistId;
}
}

View File

@ -18,36 +18,36 @@ import kotlinx.android.parcel.Parcelize
@Parcelize
open class Song(
val id: Int,
val title: String,
val trackNumber: Int,
val year: Int,
val duration: Long,
val data: String,
val dateModified: Long,
val albumId: Int,
val albumName: String,
val artistId: Int,
val artistName: String,
val composer: String?
val id: Int,
val title: String,
val trackNumber: Int,
val year: Int,
val duration: Long,
val data: String,
val dateModified: Long,
val albumId: Int,
val albumName: String,
val artistId: Int,
val artistName: String,
val composer: String?
) : Parcelable {
companion object {
@JvmStatic
val emptySong = Song(
-1,
"",
-1,
-1,
-1,
"",
-1,
-1,
"",
-1,
"",
""
-1,
"",
-1,
-1,
-1,
"",
-1,
-1,
"",
-1,
"",
""
)
}
}

View File

@ -17,9 +17,11 @@ package code.name.monkey.retromusic.model.lyrics;
import android.util.SparseArray;
public abstract class AbsSynchronizedLyrics extends Lyrics {
private static final int TIME_OFFSET_MS = 500; // time adjustment to display line before it actually starts
protected final SparseArray<String> lines = new SparseArray<>();
protected int offset = 0;
public String getLine(int time) {
@ -40,15 +42,6 @@ public abstract class AbsSynchronizedLyrics extends Lyrics {
return lines.get(lastLineTime);
}
public boolean isSynchronized() {
return true;
}
public boolean isValid() {
parse(true);
return valid;
}
@Override
public String getText() {
parse(false);
@ -66,4 +59,13 @@ public abstract class AbsSynchronizedLyrics extends Lyrics {
return super.getText();
}
public boolean isSynchronized() {
return true;
}
public boolean isValid() {
parse(true);
return valid;
}
}

View File

@ -15,39 +15,28 @@
package code.name.monkey.retromusic.model.lyrics;
import code.name.monkey.retromusic.model.Song;
import java.util.ArrayList;
import code.name.monkey.retromusic.model.Song;
public class Lyrics {
private static final ArrayList<Class<? extends Lyrics>> FORMATS = new ArrayList<>();
static {
Lyrics.FORMATS.add(SynchronizedLyricsLRC.class);
}
public String data;
public Song song;
public String data;
protected boolean parsed = false;
protected boolean valid = false;
public static Lyrics parse(Song song, String data) {
for (Class<? extends Lyrics> format : Lyrics.FORMATS) {
try {
Lyrics lyrics = format.newInstance().setData(song, data);
if (lyrics.isValid()) return lyrics.parse(false);
} catch (Exception e) {
e.printStackTrace();
}
}
return new Lyrics().setData(song, data).parse(false);
}
protected boolean parsed = false;
protected boolean valid = false;
public static boolean isSynchronized(String data) {
for (Class<? extends Lyrics> format : Lyrics.FORMATS) {
try {
Lyrics lyrics = format.newInstance().setData(null, data);
if (lyrics.isValid()) return true;
if (lyrics.isValid()) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
@ -55,16 +44,22 @@ public class Lyrics {
return false;
}
public Lyrics setData(Song song, String data) {
this.song = song;
this.data = data;
return this;
public static Lyrics parse(Song song, String data) {
for (Class<? extends Lyrics> format : Lyrics.FORMATS) {
try {
Lyrics lyrics = format.newInstance().setData(song, data);
if (lyrics.isValid()) {
return lyrics.parse(false);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return new Lyrics().setData(song, data).parse(false);
}
public Lyrics parse(boolean check) {
this.valid = true;
this.parsed = true;
return this;
public String getText() {
return this.data.trim().replaceAll("(\r?\n){3,}", "\r\n\r\n");
}
public boolean isSynchronized() {
@ -76,7 +71,19 @@ public class Lyrics {
return this.valid;
}
public String getText() {
return this.data.trim().replaceAll("(\r?\n){3,}", "\r\n\r\n");
public Lyrics parse(boolean check) {
this.valid = true;
this.parsed = true;
return this;
}
public Lyrics setData(Song song, String data) {
this.song = song;
this.data = data;
return this;
}
static {
Lyrics.FORMATS.add(SynchronizedLyricsLRC.class);
}
}

View File

@ -18,11 +18,15 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
class SynchronizedLyricsLRC extends AbsSynchronizedLyrics {
private static final Pattern LRC_LINE_PATTERN = Pattern.compile("((?:\\[.*?\\])+)(.*)");
private static final Pattern LRC_TIME_PATTERN = Pattern.compile("\\[(\\d+):(\\d{2}(?:\\.\\d+)?)\\]");
private static final Pattern LRC_ATTRIBUTE_PATTERN = Pattern.compile("\\[(\\D+):(.+)\\]");
private static final float LRC_SECONDS_TO_MS_MULTIPLIER = 1000f;
private static final int LRC_MINUTES_TO_MS_MULTIPLIER = 60000;
@Override
@ -71,7 +75,9 @@ class SynchronizedLyricsLRC extends AbsSynchronizedLyrics {
int ms = (int) (s * LRC_SECONDS_TO_MS_MULTIPLIER) + m * LRC_MINUTES_TO_MS_MULTIPLIER;
this.valid = true;
if (check) return this;
if (check) {
return this;
}
this.lines.append(ms, text);
}

View File

@ -16,7 +16,6 @@ package code.name.monkey.retromusic.model.smartplaylist;
import android.content.Context;
import android.os.Parcel;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import code.name.monkey.retromusic.R;
@ -24,6 +23,7 @@ import code.name.monkey.retromusic.model.AbsCustomPlaylist;
public abstract class AbsSmartPlaylist extends AbsCustomPlaylist {
@DrawableRes
public final int iconRes;
@ -37,18 +37,16 @@ public abstract class AbsSmartPlaylist extends AbsCustomPlaylist {
this.iconRes = R.drawable.ic_queue_music_white_24dp;
}
public abstract void clear(Context context);
public boolean isClearable() {
return true;
protected AbsSmartPlaylist(Parcel in) {
super(in);
this.iconRes = in.readInt();
}
public abstract void clear(Context context);
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + iconRes;
return result;
public int describeContents() {
return 0;
}
@Override
@ -63,10 +61,16 @@ public abstract class AbsSmartPlaylist extends AbsCustomPlaylist {
return false;
}
@Override
public int describeContents() {
return 0;
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + iconRes;
return result;
}
public boolean isClearable() {
return true;
}
@Override
@ -74,9 +78,4 @@ public abstract class AbsSmartPlaylist extends AbsCustomPlaylist {
super.writeToParcel(dest, flags);
dest.writeInt(this.iconRes);
}
protected AbsSmartPlaylist(Parcel in) {
super(in);
this.iconRes = in.readInt();
}
}

View File

@ -16,17 +16,13 @@ package code.name.monkey.retromusic.model.smartplaylist;
import android.content.Context;
import android.os.Parcel;
import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader;
import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.providers.HistoryStore;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
/**
* @author Karim Abou Zeid (kabouzeid)
@ -51,12 +47,6 @@ public class HistoryPlaylist extends AbsSmartPlaylist {
super(in);
}
@NonNull
@Override
public ArrayList<Song> getSongs(@NotNull @NonNull Context context) {
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getRecentlyPlayedTracks(context);
}
@Override
public void clear(@NonNull Context context) {
HistoryStore.getInstance(context).clear();
@ -66,4 +56,10 @@ public class HistoryPlaylist extends AbsSmartPlaylist {
public int describeContents() {
return 0;
}
@NonNull
@Override
public ArrayList<Song> getSongs(@NotNull @NonNull Context context) {
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getRecentlyPlayedTracks(context);
}
}

View File

@ -16,16 +16,12 @@ package code.name.monkey.retromusic.model.smartplaylist;
import android.content.Context;
import android.os.Parcel;
import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.loaders.LastAddedSongsLoader;
import code.name.monkey.retromusic.model.Song;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
public class LastAddedPlaylist extends AbsSmartPlaylist {
@ -48,23 +44,23 @@ public class LastAddedPlaylist extends AbsSmartPlaylist {
super(in);
}
@Override
public void clear(@NonNull Context context) {
}
@Override
public int describeContents() {
return 0;
}
@NonNull
@Override
public ArrayList<Song> getSongs(@NotNull @NonNull Context context) {
return LastAddedSongsLoader.INSTANCE.getLastAddedSongs(context);
}
@Override
public void clear(@NonNull Context context) {
}
@Override
public boolean isClearable() {
return false;
}
@Override
public int describeContents() {
return 0;
}
}

View File

@ -16,17 +16,13 @@ package code.name.monkey.retromusic.model.smartplaylist;
import android.content.Context;
import android.os.Parcel;
import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader;
import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.providers.SongPlayCountStore;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
/**
* @author Karim Abou Zeid (kabouzeid)
@ -51,13 +47,6 @@ public class MyTopTracksPlaylist extends AbsSmartPlaylist {
super(in);
}
@NonNull
@Override
public ArrayList<Song> getSongs(@NotNull @NonNull Context context) {
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopTracks(context);
}
@Override
public void clear(@NonNull Context context) {
SongPlayCountStore.getInstance(context).clear();
@ -67,4 +56,10 @@ public class MyTopTracksPlaylist extends AbsSmartPlaylist {
public int describeContents() {
return 0;
}
@NonNull
@Override
public ArrayList<Song> getSongs(@NotNull @NonNull Context context) {
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopTracks(context);
}
}

View File

@ -16,17 +16,12 @@ package code.name.monkey.retromusic.model.smartplaylist;
import android.content.Context;
import android.os.Parcel;
import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.loaders.SongLoader;
import code.name.monkey.retromusic.model.Song;
import io.reactivex.Observable;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
public class ShuffleAllPlaylist extends AbsSmartPlaylist {
@ -48,18 +43,6 @@ public class ShuffleAllPlaylist extends AbsSmartPlaylist {
super(in);
}
@NonNull
@Override
public Observable<ArrayList<Song>> getSongsFlowable(@NotNull @NonNull Context context) {
return SongLoader.INSTANCE.getAllSongsFlowable(context);
}
@NonNull
@Override
public ArrayList<Song> getSongs(@NotNull Context context) {
return SongLoader.INSTANCE.getAllSongs(context);
}
@Override
public void clear(@NonNull Context context) {
// Shuffle all is not a real "Smart Playlist"
@ -69,4 +52,10 @@ public class ShuffleAllPlaylist extends AbsSmartPlaylist {
public int describeContents() {
return 0;
}
@NonNull
@Override
public ArrayList<Song> getSongs(@NotNull Context context) {
return SongLoader.INSTANCE.getAllSongs(context);
}
}

View File

@ -20,16 +20,14 @@ import code.name.monkey.retromusic.mvp.BaseView
import code.name.monkey.retromusic.mvp.Presenter
import code.name.monkey.retromusic.mvp.PresenterImpl
import code.name.monkey.retromusic.providers.interfaces.Repository
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
operator fun CompositeDisposable.plusAssign(disposable: Disposable) {
add(disposable)
}
interface HomeView : BaseView {
fun sections(sections: ArrayList<Home>)
}
@ -38,8 +36,9 @@ interface HomePresenter : Presenter<HomeView> {
fun loadSections()
class HomePresenterImpl @Inject constructor(
private val repository: Repository
private val repository: Repository
) : PresenterImpl<HomeView>(), HomePresenter, CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
@ -54,11 +53,11 @@ interface HomePresenter : Presenter<HomeView> {
launch {
val list = ArrayList<Home>()
val recentArtistResult = listOf(
repository.topArtists(),
repository.topAlbums(),
repository.recentArtists(),
repository.recentAlbums(),
repository.favoritePlaylist()
repository.topArtists(),
repository.topAlbums(),
repository.recentArtists(),
repository.recentAlbums(),
repository.favoritePlaylist()
)
for (r in recentArtistResult) {
when (r) {

View File

@ -20,15 +20,11 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import android.provider.MediaStore.Audio.AudioColumns;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import code.name.monkey.retromusic.loaders.SongLoader;
import code.name.monkey.retromusic.model.Song;
import io.reactivex.Observable;
import java.util.ArrayList;
/**
* @author Andrew Neal, modified for Phonograph by Karim Abou Zeid
@ -36,22 +32,18 @@ import io.reactivex.Observable;
* This keeps track of the music playback and history state of the playback service
*/
public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "music_playback_state.db";
public static final String PLAYING_QUEUE_TABLE_NAME = "playing_queue";
public static final String ORIGINAL_PLAYING_QUEUE_TABLE_NAME = "original_playing_queue";
private static final int VERSION = 10;
@Nullable
private static MusicPlaybackQueueStore sInstance = null;
/**
* Constructor of <code>MusicPlaybackState</code>
*
* @param context The {@link Context} to use
*/
public MusicPlaybackQueueStore(final @NonNull Context context) {
super(context, DATABASE_NAME, null, VERSION);
}
/**
* @param context The {@link Context} to use
* @return A new instance of this class.
@ -64,12 +56,53 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
return sInstance;
}
/**
* Constructor of <code>MusicPlaybackState</code>
*
* @param context The {@link Context} to use
*/
public MusicPlaybackQueueStore(final @NonNull Context context) {
super(context, DATABASE_NAME, null, VERSION);
}
@Override
public void onCreate(@NonNull final SQLiteDatabase db) {
createTable(db, PLAYING_QUEUE_TABLE_NAME);
createTable(db, ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
}
@NonNull
public ArrayList<Song> getSavedOriginalPlayingQueue() {
return getQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
}
@NonNull
public ArrayList<Song> getSavedPlayingQueue() {
return getQueue(PLAYING_QUEUE_TABLE_NAME);
}
@Override
public void onDowngrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) {
// If we ever have downgrade, drop the table to be safe
db.execSQL("DROP TABLE IF EXISTS " + PLAYING_QUEUE_TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS " + ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
onCreate(db);
}
@Override
public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion, final int newVersion) {
// not necessary yet
db.execSQL("DROP TABLE IF EXISTS " + PLAYING_QUEUE_TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS " + ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
onCreate(db);
}
public synchronized void saveQueues(@NonNull final ArrayList<Song> playingQueue,
@NonNull final ArrayList<Song> originalPlayingQueue) {
saveQueue(PLAYING_QUEUE_TABLE_NAME, playingQueue);
saveQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME, originalPlayingQueue);
}
private void createTable(@NonNull final SQLiteDatabase db, final String tableName) {
//noinspection StringBufferReplaceableByString
StringBuilder builder = new StringBuilder();
@ -116,25 +149,11 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
db.execSQL(builder.toString());
}
@Override
public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion, final int newVersion) {
// not necessary yet
db.execSQL("DROP TABLE IF EXISTS " + PLAYING_QUEUE_TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS " + ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
onCreate(db);
}
@Override
public void onDowngrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) {
// If we ever have downgrade, drop the table to be safe
db.execSQL("DROP TABLE IF EXISTS " + PLAYING_QUEUE_TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS " + ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
onCreate(db);
}
public synchronized void saveQueues(@NonNull final ArrayList<Song> playingQueue, @NonNull final ArrayList<Song> originalPlayingQueue) {
saveQueue(PLAYING_QUEUE_TABLE_NAME, playingQueue);
saveQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME, originalPlayingQueue);
@NonNull
private ArrayList<Song> getQueue(@NonNull final String tableName) {
Cursor cursor = getReadableDatabase().query(tableName, null,
null, null, null, null, null);
return SongLoader.INSTANCE.getSongs(cursor);
}
/**
@ -185,38 +204,4 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
}
}
}
@NonNull
public Observable<ArrayList<Song>> getSavedPlayingQueueFlowable() {
return getQueueFlowable(PLAYING_QUEUE_TABLE_NAME);
}
@NonNull
public Observable<ArrayList<Song>> getSavedOriginalPlayingQueueFlowable() {
return getQueueFlowable(ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
}
@NonNull
public ArrayList<Song> getSavedPlayingQueue() {
return getQueue(PLAYING_QUEUE_TABLE_NAME);
}
@NonNull
public ArrayList<Song> getSavedOriginalPlayingQueue() {
return getQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
}
@NonNull
private Observable<ArrayList<Song>> getQueueFlowable(@NonNull final String tableName) {
Cursor cursor = getReadableDatabase().query(tableName, null,
null, null, null, null, null);
return SongLoader.INSTANCE.getSongsFlowable(cursor);
}
@NonNull
private ArrayList<Song> getQueue(@NonNull final String tableName) {
Cursor cursor = getReadableDatabase().query(tableName, null,
null, null, null, null, null);
return SongLoader.INSTANCE.getSongs(cursor);
}
}