Code refactor

This commit is contained in:
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 compileSdkVersion 29
defaultConfig { defaultConfig {
minSdkVersion 21 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 vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic" applicationId "code.name.monkey.retromusic"
@ -70,7 +70,7 @@ android {
packagingOptions { packagingOptions {
exclude 'META-INF/LICENSE' exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE' exclude 'META-INF/NOTICE'
exclude 'META-INF/rxjava.properties' exclude 'META-INF/java.properties'
} }
lintOptions { lintOptions {
disable 'MissingTranslation' disable 'MissingTranslation'
@ -138,7 +138,6 @@ dependencies {
def retrofit_version = "2.6.2" def retrofit_version = "2.6.2"
implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$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' 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.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" /> <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" /> tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.vending.BILLING" /> <uses-permission android:name="com.android.vending.BILLING" />
@ -17,11 +18,11 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application <application
android:requestLegacyExternalStorage="true"
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.RetroMusic.FollowSystem" android:theme="@style/Theme.RetroMusic.FollowSystem"
@ -30,8 +31,8 @@
tools:targetApi="m"> tools:targetApi="m">
<activity <activity
android:name=".activities.MainActivity" android:name=".activities.MainActivity"
android:theme="@style/SplashTheme" android:label="@string/app_name"
android:label="@string/app_name" > android:theme="@style/SplashTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.MUSIC_PLAYER" /> <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.service.MusicService
import code.name.monkey.retromusic.util.AppRater import code.name.monkey.retromusic.util.AppRater
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import io.reactivex.disposables.CompositeDisposable
import java.util.ArrayList import java.util.ArrayList
class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener { class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
@ -33,7 +32,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
private lateinit var currentFragment: MainActivityFragmentCallbacks private lateinit var currentFragment: MainActivityFragmentCallbacks
private var blockRequestPermissions: Boolean = false private var blockRequestPermissions: Boolean = false
private val disposable = CompositeDisposable()
private val broadcastReceiver = object : BroadcastReceiver() { private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
val action = intent.action val action = intent.action
@ -107,7 +106,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
disposable.clear()
unregisterReceiver(broadcastReceiver) unregisterReceiver(broadcastReceiver)
PreferenceUtil.getInstance(this).unregisterOnSharedPreferenceChangedListener(this) 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.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.animation.GlideAnimation import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget 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.albumArtistContainer
import kotlinx.android.synthetic.main.activity_album_tag_editor.albumArtistText import kotlinx.android.synthetic.main.activity_album_tag_editor.albumArtistText
import kotlinx.android.synthetic.main.activity_album_tag_editor.albumText 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 albumArtBitmap: Bitmap? = null
private var deleteAlbumArt: Boolean = false private var deleteAlbumArt: Boolean = false
private var lastFMRestClient: LastFMRestClient? = null private var lastFMRestClient: LastFMRestClient? = null
private val disposable = CompositeDisposable()
private fun setupToolbar() { private fun setupToolbar() {
applyToolbar(toolbar) applyToolbar(toolbar)
@ -145,7 +143,6 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
disposable.clear()
} }
private fun toastLoadingFailed() { private fun toastLoadingFailed() {
@ -178,7 +175,8 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
fieldKeyValueMap[FieldKey.GENRE] = genreTitle.text.toString() fieldKeyValueMap[FieldKey.GENRE] = genreTitle.text.toString()
fieldKeyValueMap[FieldKey.YEAR] = yearTitle.text.toString() fieldKeyValueMap[FieldKey.YEAR] = yearTitle.text.toString()
writeValuesToFiles(fieldKeyValueMap, writeValuesToFiles(
fieldKeyValueMap,
if (deleteAlbumArt) ArtworkInfo(id, null) if (deleteAlbumArt) ArtworkInfo(id, null)
else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!) 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.model.lyrics.Lyrics
import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.NavigationUtil
import com.bumptech.glide.Glide 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.artistImage
import kotlinx.android.synthetic.main.fragment_full.nextSong import kotlinx.android.synthetic.main.fragment_full.nextSong
import kotlinx.android.synthetic.main.fragment_full.nextSongLabel import kotlinx.android.synthetic.main.fragment_full.nextSongLabel
@ -226,11 +225,8 @@ class FullPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Ca
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
progressViewUpdateHelper.stop() progressViewUpdateHelper.stop()
compositeDisposable.dispose()
} }
private val compositeDisposable = CompositeDisposable()
private fun updateArtistImage() { private fun updateArtistImage() {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val artist = ArtistLoader.getArtist(requireContext(), MusicPlayerRemote.currentSong.artistId) 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.Playlist
import code.name.monkey.retromusic.model.PlaylistSong import code.name.monkey.retromusic.model.PlaylistSong
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import io.reactivex.Observable import java.util.ArrayList
import java.util.*
/** /**
* Created by hemanths on 16/08/17. * Created by hemanths on 16/08/17.
@ -33,14 +31,6 @@ import java.util.*
object PlaylistSongsLoader { object PlaylistSongsLoader {
fun getPlaylistSongListFlowable(
context: Context,
playlist: Playlist
): Observable<ArrayList<Song>> {
return (playlist as? AbsCustomPlaylist)?.getSongsFlowable(context)
?: getPlaylistSongListFlowable(context, playlist.id)
}
fun getPlaylistSongList( fun getPlaylistSongList(
context: Context, context: Context,
playlist: Playlist playlist: Playlist
@ -49,23 +39,6 @@ object PlaylistSongsLoader {
?: getPlaylistSongList(context, playlist.id) ?: 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()
}
}
fun getPlaylistSongList(context: Context, playlistId: Int): ArrayList<Song> { fun getPlaylistSongList(context: Context, playlistId: Int): ArrayList<Song> {
val songs = arrayListOf<Song>() val songs = arrayListOf<Song>()
val cursor = makePlaylistSongCursor(context, playlistId) val cursor = makePlaylistSongCursor(context, playlistId)
@ -79,7 +52,6 @@ object PlaylistSongsLoader {
return songs return songs
} }
private fun getPlaylistSongFromCursorImpl(cursor: Cursor, playlistId: Int): PlaylistSong { private fun getPlaylistSongFromCursorImpl(cursor: Cursor, playlistId: Int): PlaylistSong {
val id = cursor.getInt(0) val id = cursor.getInt(0)
val title = cursor.getString(1) val title = cursor.getString(1)
@ -95,14 +67,30 @@ object PlaylistSongsLoader {
val idInPlaylist = cursor.getInt(11) val idInPlaylist = cursor.getInt(11)
val composer = cursor.getString(12) 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? { private fun makePlaylistSongCursor(context: Context, playlistId: Int): Cursor? {
try { try {
return context.contentResolver.query( return context.contentResolver.query(
MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId.toLong()), MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId.toLong()),
arrayOf(MediaStore.Audio.Playlists.Members.AUDIO_ID, // 0 arrayOf(
MediaStore.Audio.Playlists.Members.AUDIO_ID, // 0
AudioColumns.TITLE, // 1 AudioColumns.TITLE, // 1
AudioColumns.TRACK, // 2 AudioColumns.TRACK, // 2
AudioColumns.YEAR, // 3 AudioColumns.YEAR, // 3
@ -114,9 +102,11 @@ object PlaylistSongsLoader {
AudioColumns.ARTIST_ID, // 9 AudioColumns.ARTIST_ID, // 9
AudioColumns.ARTIST, // 10 AudioColumns.ARTIST, // 10
MediaStore.Audio.Playlists.Members._ID,//11 MediaStore.Audio.Playlists.Members._ID,//11
AudioColumns.COMPOSER)// 12 AudioColumns.COMPOSER
)// 12
, BASE_SELECTION, null, , BASE_SELECTION, null,
MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER) MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER
)
} catch (e: SecurityException) { } catch (e: SecurityException) {
return null return null
} }

View file

@ -20,25 +20,17 @@ import android.provider.MediaStore
import android.provider.MediaStore.Audio.AudioColumns import android.provider.MediaStore.Audio.AudioColumns
import code.name.monkey.retromusic.Constants.BASE_SELECTION import code.name.monkey.retromusic.Constants.BASE_SELECTION
import code.name.monkey.retromusic.Constants.baseProjection 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.model.Song
import code.name.monkey.retromusic.providers.BlacklistStore import code.name.monkey.retromusic.providers.BlacklistStore
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import io.reactivex.Observable import java.util.ArrayList
import java.util.*
/** /**
* Created by hemanths on 10/08/17. * Created by hemanths on 10/08/17.
*/ */
object SongLoader { object SongLoader {
fun getAllSongsFlowable(
context: Context
): Observable<ArrayList<Song>> {
val cursor = makeSongCursor(context, null, null)
return getSongsFlowable(cursor)
}
fun getAllSongs( fun getAllSongs(
context: Context context: Context
@ -47,23 +39,6 @@ object SongLoader {
return getSongs(cursor) 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( fun getSongs(
cursor: Cursor? cursor: Cursor?
): ArrayList<Song> { ): ArrayList<Song> {
@ -78,14 +53,6 @@ object SongLoader {
return songs 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( fun getSongs(
context: Context, context: Context,
query: String query: String
@ -94,22 +61,6 @@ object SongLoader {
return getSongs(cursor) 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( fun getSong(
cursor: Cursor? cursor: Cursor?
): Song { ): Song {
@ -123,34 +74,11 @@ object SongLoader {
return song 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 { fun getSong(context: Context, queryId: Int): Song {
val cursor = makeSongCursor(context, AudioColumns._ID + "=?", arrayOf(queryId.toString())) val cursor = makeSongCursor(context, AudioColumns._ID + "=?", arrayOf(queryId.toString()))
return getSong(cursor) 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( private fun getSongFromCursorImpl(
cursor: Cursor cursor: Cursor
): Song { ): Song {
@ -167,11 +95,12 @@ object SongLoader {
val artistName = cursor.getString(10) val artistName = cursor.getString(10)
val composer = cursor.getString(11) val composer = cursor.getString(11)
return Song(id, title, trackNumber, year, duration, data, dateModified, albumId, return Song(
albumName ?: "", artistId, artistName, composer ?: "") id, title, trackNumber, year, duration, data, dateModified, albumId,
albumName ?: "", artistId, artistName, composer ?: ""
)
} }
@JvmOverloads @JvmOverloads
fun makeSongCursor( fun makeSongCursor(
context: Context, context: Context,
@ -195,12 +124,18 @@ object SongLoader {
} }
try { try {
return context.contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, return context.contentResolver.query(
baseProjection, selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.getInstance(context).filterLength * 1000), selectionValuesFinal, sortOrder) MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
baseProjection,
selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.getInstance(
context
).filterLength * 1000),
selectionValuesFinal,
sortOrder
)
} catch (e: SecurityException) { } catch (e: SecurityException) {
return null return null
} }
} }
private fun generateBlacklistSelection( private fun generateBlacklistSelection(

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.model.Song
import code.name.monkey.retromusic.providers.HistoryStore import code.name.monkey.retromusic.providers.HistoryStore
import code.name.monkey.retromusic.providers.SongPlayCountStore import code.name.monkey.retromusic.providers.SongPlayCountStore
import io.reactivex.Observable import java.util.ArrayList
import java.util.*
/** /**
* Created by hemanths on 16/08/17. * Created by hemanths on 16/08/17.
@ -32,18 +31,10 @@ import java.util.*
object TopAndRecentlyPlayedTracksLoader { object TopAndRecentlyPlayedTracksLoader {
fun getRecentlyPlayedTracksFlowable(context: Context): Observable<ArrayList<Song>> {
return SongLoader.getSongsFlowable(makeRecentTracksCursorAndClearUpDatabase(context))
}
fun getRecentlyPlayedTracks(context: Context): ArrayList<Song> { fun getRecentlyPlayedTracks(context: Context): ArrayList<Song> {
return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context)) return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context))
} }
fun getTopTracksFlowable(context: Context): Observable<ArrayList<Song>> {
return SongLoader.getSongsFlowable(makeTopTracksCursorAndClearUpDatabase(context))
}
fun getTopTracks(context: Context): ArrayList<Song> { fun getTopTracks(context: Context): ArrayList<Song> {
return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context)) return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context))
} }
@ -83,8 +74,10 @@ object TopAndRecentlyPlayedTracksLoader {
val songs = HistoryStore.getInstance(context).queryRecentIds() val songs = HistoryStore.getInstance(context).queryRecentIds()
try { try {
return makeSortedCursor(context, songs, return makeSortedCursor(
songs!!.getColumnIndex(HistoryStore.RecentStoreColumns.ID)) context, songs,
songs!!.getColumnIndex(HistoryStore.RecentStoreColumns.ID)
)
} finally { } finally {
songs?.close() songs?.close()
} }
@ -96,13 +89,17 @@ object TopAndRecentlyPlayedTracksLoader {
.getTopPlayedResults(NUMBER_OF_TOP_TRACKS) .getTopPlayedResults(NUMBER_OF_TOP_TRACKS)
songs.use { localSongs -> songs.use { localSongs ->
return makeSortedCursor(context, localSongs, return makeSortedCursor(
localSongs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID)) context, localSongs,
localSongs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID)
)
} }
} }
private fun makeSortedCursor(context: Context, private fun makeSortedCursor(
cursor: Cursor?, idColumn: Int): SortedLongCursor? { context: Context,
cursor: Cursor?, idColumn: Int
): SortedLongCursor? {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
// create the list of ids to select against // create the list of ids to select against
@ -138,19 +135,6 @@ object TopAndRecentlyPlayedTracksLoader {
return null 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( fun getTopAlbums(
context: Context context: Context
): ArrayList<Album> { ): ArrayList<Album> {
@ -158,17 +142,6 @@ object TopAndRecentlyPlayedTracksLoader {
return AlbumLoader.splitIntoAlbums(getTopTracks(context)) 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> { fun getTopArtists(context: Context): ArrayList<Artist> {
return ArtistLoader.splitIntoArtists(getTopAlbums(context)) return ArtistLoader.splitIntoArtists(getTopAlbums(context))
} }

View file

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

View file

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

View file

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

View file

@ -16,4 +16,8 @@ package code.name.monkey.retromusic.model
import com.google.gson.annotations.SerializedName 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 androidx.annotation.StringRes
import code.name.monkey.retromusic.adapter.HomeAdapter.Companion.HomeSection import code.name.monkey.retromusic.adapter.HomeAdapter.Companion.HomeSection
class Home(val priority: Int, class Home(
val priority: Int,
@StringRes val title: Int, @StringRes val title: Int,
val arrayList: ArrayList<*>, val arrayList: ArrayList<*>,
@HomeSection @HomeSection
val homeSection: Int, val homeSection: Int,
@DrawableRes @DrawableRes
val icon: Int) val icon: Int
)

View file

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

View file

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

View file

@ -17,9 +17,11 @@ package code.name.monkey.retromusic.model.lyrics;
import android.util.SparseArray; import android.util.SparseArray;
public abstract class AbsSynchronizedLyrics extends Lyrics { public abstract class AbsSynchronizedLyrics extends Lyrics {
private static final int TIME_OFFSET_MS = 500; // time adjustment to display line before it actually starts 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 final SparseArray<String> lines = new SparseArray<>();
protected int offset = 0; protected int offset = 0;
public String getLine(int time) { public String getLine(int time) {
@ -40,15 +42,6 @@ public abstract class AbsSynchronizedLyrics extends Lyrics {
return lines.get(lastLineTime); return lines.get(lastLineTime);
} }
public boolean isSynchronized() {
return true;
}
public boolean isValid() {
parse(true);
return valid;
}
@Override @Override
public String getText() { public String getText() {
parse(false); parse(false);
@ -66,4 +59,13 @@ public abstract class AbsSynchronizedLyrics extends Lyrics {
return super.getText(); 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; package code.name.monkey.retromusic.model.lyrics;
import code.name.monkey.retromusic.model.Song;
import java.util.ArrayList; import java.util.ArrayList;
import code.name.monkey.retromusic.model.Song;
public class Lyrics { public class Lyrics {
private static final ArrayList<Class<? extends Lyrics>> FORMATS = new ArrayList<>(); private static final ArrayList<Class<? extends Lyrics>> FORMATS = new ArrayList<>();
static { public String data;
Lyrics.FORMATS.add(SynchronizedLyricsLRC.class);
}
public Song song; public Song song;
public String data;
protected boolean parsed = false;
protected boolean valid = false;
public static Lyrics parse(Song song, String data) { protected boolean parsed = false;
for (Class<? extends Lyrics> format : Lyrics.FORMATS) {
try { protected boolean valid = false;
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 static boolean isSynchronized(String data) { public static boolean isSynchronized(String data) {
for (Class<? extends Lyrics> format : Lyrics.FORMATS) { for (Class<? extends Lyrics> format : Lyrics.FORMATS) {
try { try {
Lyrics lyrics = format.newInstance().setData(null, data); Lyrics lyrics = format.newInstance().setData(null, data);
if (lyrics.isValid()) return true; if (lyrics.isValid()) {
return true;
}
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -55,16 +44,22 @@ public class Lyrics {
return false; return false;
} }
public Lyrics setData(Song song, String data) { public static Lyrics parse(Song song, String data) {
this.song = song; for (Class<? extends Lyrics> format : Lyrics.FORMATS) {
this.data = data; try {
return this; 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) { public String getText() {
this.valid = true; return this.data.trim().replaceAll("(\r?\n){3,}", "\r\n\r\n");
this.parsed = true;
return this;
} }
public boolean isSynchronized() { public boolean isSynchronized() {
@ -76,7 +71,19 @@ public class Lyrics {
return this.valid; return this.valid;
} }
public String getText() { public Lyrics parse(boolean check) {
return this.data.trim().replaceAll("(\r?\n){3,}", "\r\n\r\n"); 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; import java.util.regex.Pattern;
class SynchronizedLyricsLRC extends AbsSynchronizedLyrics { class SynchronizedLyricsLRC extends AbsSynchronizedLyrics {
private static final Pattern LRC_LINE_PATTERN = Pattern.compile("((?:\\[.*?\\])+)(.*)"); 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_TIME_PATTERN = Pattern.compile("\\[(\\d+):(\\d{2}(?:\\.\\d+)?)\\]");
private static final Pattern LRC_ATTRIBUTE_PATTERN = Pattern.compile("\\[(\\D+):(.+)\\]"); private static final Pattern LRC_ATTRIBUTE_PATTERN = Pattern.compile("\\[(\\D+):(.+)\\]");
private static final float LRC_SECONDS_TO_MS_MULTIPLIER = 1000f; private static final float LRC_SECONDS_TO_MS_MULTIPLIER = 1000f;
private static final int LRC_MINUTES_TO_MS_MULTIPLIER = 60000; private static final int LRC_MINUTES_TO_MS_MULTIPLIER = 60000;
@Override @Override
@ -71,7 +75,9 @@ class SynchronizedLyricsLRC extends AbsSynchronizedLyrics {
int ms = (int) (s * LRC_SECONDS_TO_MS_MULTIPLIER) + m * LRC_MINUTES_TO_MS_MULTIPLIER; int ms = (int) (s * LRC_SECONDS_TO_MS_MULTIPLIER) + m * LRC_MINUTES_TO_MS_MULTIPLIER;
this.valid = true; this.valid = true;
if (check) return this; if (check) {
return this;
}
this.lines.append(ms, text); this.lines.append(ms, text);
} }

View file

@ -16,7 +16,6 @@ package code.name.monkey.retromusic.model.smartplaylist;
import android.content.Context; import android.content.Context;
import android.os.Parcel; import android.os.Parcel;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
@ -24,6 +23,7 @@ import code.name.monkey.retromusic.model.AbsCustomPlaylist;
public abstract class AbsSmartPlaylist extends AbsCustomPlaylist { public abstract class AbsSmartPlaylist extends AbsCustomPlaylist {
@DrawableRes @DrawableRes
public final int iconRes; public final int iconRes;
@ -37,18 +37,16 @@ public abstract class AbsSmartPlaylist extends AbsCustomPlaylist {
this.iconRes = R.drawable.ic_queue_music_white_24dp; this.iconRes = R.drawable.ic_queue_music_white_24dp;
} }
public abstract void clear(Context context); protected AbsSmartPlaylist(Parcel in) {
super(in);
public boolean isClearable() { this.iconRes = in.readInt();
return true;
} }
public abstract void clear(Context context);
@Override @Override
public int hashCode() { public int describeContents() {
final int prime = 31; return 0;
int result = super.hashCode();
result = prime * result + iconRes;
return result;
} }
@Override @Override
@ -63,10 +61,16 @@ public abstract class AbsSmartPlaylist extends AbsCustomPlaylist {
return false; return false;
} }
@Override @Override
public int describeContents() { public int hashCode() {
return 0; final int prime = 31;
int result = super.hashCode();
result = prime * result + iconRes;
return result;
}
public boolean isClearable() {
return true;
} }
@Override @Override
@ -74,9 +78,4 @@ public abstract class AbsSmartPlaylist extends AbsCustomPlaylist {
super.writeToParcel(dest, flags); super.writeToParcel(dest, flags);
dest.writeInt(this.iconRes); 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.content.Context;
import android.os.Parcel; import android.os.Parcel;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader; import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader;
import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.providers.HistoryStore; import code.name.monkey.retromusic.providers.HistoryStore;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
/** /**
* @author Karim Abou Zeid (kabouzeid) * @author Karim Abou Zeid (kabouzeid)
@ -51,12 +47,6 @@ public class HistoryPlaylist extends AbsSmartPlaylist {
super(in); super(in);
} }
@NonNull
@Override
public ArrayList<Song> getSongs(@NotNull @NonNull Context context) {
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getRecentlyPlayedTracks(context);
}
@Override @Override
public void clear(@NonNull Context context) { public void clear(@NonNull Context context) {
HistoryStore.getInstance(context).clear(); HistoryStore.getInstance(context).clear();
@ -66,4 +56,10 @@ public class HistoryPlaylist extends AbsSmartPlaylist {
public int describeContents() { public int describeContents() {
return 0; 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.content.Context;
import android.os.Parcel; import android.os.Parcel;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.loaders.LastAddedSongsLoader; import code.name.monkey.retromusic.loaders.LastAddedSongsLoader;
import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.model.Song;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
public class LastAddedPlaylist extends AbsSmartPlaylist { public class LastAddedPlaylist extends AbsSmartPlaylist {
@ -48,23 +44,23 @@ public class LastAddedPlaylist extends AbsSmartPlaylist {
super(in); super(in);
} }
@Override
public void clear(@NonNull Context context) {
}
@Override
public int describeContents() {
return 0;
}
@NonNull @NonNull
@Override @Override
public ArrayList<Song> getSongs(@NotNull @NonNull Context context) { public ArrayList<Song> getSongs(@NotNull @NonNull Context context) {
return LastAddedSongsLoader.INSTANCE.getLastAddedSongs(context); return LastAddedSongsLoader.INSTANCE.getLastAddedSongs(context);
} }
@Override
public void clear(@NonNull Context context) {
}
@Override @Override
public boolean isClearable() { public boolean isClearable() {
return false; 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.content.Context;
import android.os.Parcel; import android.os.Parcel;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader; import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader;
import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.providers.SongPlayCountStore; import code.name.monkey.retromusic.providers.SongPlayCountStore;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
/** /**
* @author Karim Abou Zeid (kabouzeid) * @author Karim Abou Zeid (kabouzeid)
@ -51,13 +47,6 @@ public class MyTopTracksPlaylist extends AbsSmartPlaylist {
super(in); super(in);
} }
@NonNull
@Override
public ArrayList<Song> getSongs(@NotNull @NonNull Context context) {
return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopTracks(context);
}
@Override @Override
public void clear(@NonNull Context context) { public void clear(@NonNull Context context) {
SongPlayCountStore.getInstance(context).clear(); SongPlayCountStore.getInstance(context).clear();
@ -67,4 +56,10 @@ public class MyTopTracksPlaylist extends AbsSmartPlaylist {
public int describeContents() { public int describeContents() {
return 0; 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.content.Context;
import android.os.Parcel; import android.os.Parcel;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.loaders.SongLoader; import code.name.monkey.retromusic.loaders.SongLoader;
import code.name.monkey.retromusic.model.Song; 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 { public class ShuffleAllPlaylist extends AbsSmartPlaylist {
@ -48,18 +43,6 @@ public class ShuffleAllPlaylist extends AbsSmartPlaylist {
super(in); 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 @Override
public void clear(@NonNull Context context) { public void clear(@NonNull Context context) {
// Shuffle all is not a real "Smart Playlist" // Shuffle all is not a real "Smart Playlist"
@ -69,4 +52,10 @@ public class ShuffleAllPlaylist extends AbsSmartPlaylist {
public int describeContents() { public int describeContents() {
return 0; 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.Presenter
import code.name.monkey.retromusic.mvp.PresenterImpl import code.name.monkey.retromusic.mvp.PresenterImpl
import code.name.monkey.retromusic.providers.interfaces.Repository import code.name.monkey.retromusic.providers.interfaces.Repository
import io.reactivex.disposables.CompositeDisposable import kotlinx.coroutines.CoroutineScope
import io.reactivex.disposables.Disposable import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.* import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
operator fun CompositeDisposable.plusAssign(disposable: Disposable) {
add(disposable)
}
interface HomeView : BaseView { interface HomeView : BaseView {
fun sections(sections: ArrayList<Home>) fun sections(sections: ArrayList<Home>)
} }
@ -40,6 +38,7 @@ interface HomePresenter : Presenter<HomeView> {
class HomePresenterImpl @Inject constructor( class HomePresenterImpl @Inject constructor(
private val repository: Repository private val repository: Repository
) : PresenterImpl<HomeView>(), HomePresenter, CoroutineScope { ) : PresenterImpl<HomeView>(), HomePresenter, CoroutineScope {
private val job = Job() private val job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext

View file

@ -20,15 +20,11 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns; import android.provider.BaseColumns;
import android.provider.MediaStore.Audio.AudioColumns; import android.provider.MediaStore.Audio.AudioColumns;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.util.ArrayList;
import code.name.monkey.retromusic.loaders.SongLoader; import code.name.monkey.retromusic.loaders.SongLoader;
import code.name.monkey.retromusic.model.Song; 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 * @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 * This keeps track of the music playback and history state of the playback service
*/ */
public class MusicPlaybackQueueStore extends SQLiteOpenHelper { public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "music_playback_state.db"; public static final String DATABASE_NAME = "music_playback_state.db";
public static final String PLAYING_QUEUE_TABLE_NAME = "playing_queue"; public static final String PLAYING_QUEUE_TABLE_NAME = "playing_queue";
public static final String ORIGINAL_PLAYING_QUEUE_TABLE_NAME = "original_playing_queue"; public static final String ORIGINAL_PLAYING_QUEUE_TABLE_NAME = "original_playing_queue";
private static final int VERSION = 10; private static final int VERSION = 10;
@Nullable @Nullable
private static MusicPlaybackQueueStore sInstance = null; 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 * @param context The {@link Context} to use
* @return A new instance of this class. * @return A new instance of this class.
@ -64,12 +56,53 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
return sInstance; 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 @Override
public void onCreate(@NonNull final SQLiteDatabase db) { public void onCreate(@NonNull final SQLiteDatabase db) {
createTable(db, PLAYING_QUEUE_TABLE_NAME); createTable(db, PLAYING_QUEUE_TABLE_NAME);
createTable(db, ORIGINAL_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) { private void createTable(@NonNull final SQLiteDatabase db, final String tableName) {
//noinspection StringBufferReplaceableByString //noinspection StringBufferReplaceableByString
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@ -116,25 +149,11 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
db.execSQL(builder.toString()); db.execSQL(builder.toString());
} }
@Override @NonNull
public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion, final int newVersion) { private ArrayList<Song> getQueue(@NonNull final String tableName) {
// not necessary yet Cursor cursor = getReadableDatabase().query(tableName, null,
db.execSQL("DROP TABLE IF EXISTS " + PLAYING_QUEUE_TABLE_NAME); null, null, null, null, null);
db.execSQL("DROP TABLE IF EXISTS " + ORIGINAL_PLAYING_QUEUE_TABLE_NAME); return SongLoader.INSTANCE.getSongs(cursor);
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);
} }
/** /**
@ -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);
}
} }