diff --git a/app/src/main/java/code/name/monkey/retromusic/App.kt b/app/src/main/java/code/name/monkey/retromusic/App.kt index a4caf9d1..de916793 100644 --- a/app/src/main/java/code/name/monkey/retromusic/App.kt +++ b/app/src/main/java/code/name/monkey/retromusic/App.kt @@ -20,7 +20,6 @@ import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager -import com.amitshekhar.DebugDB import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.TransactionDetails import org.koin.android.ext.koin.androidContext @@ -33,7 +32,7 @@ class App : MultiDexApplication() { override fun onCreate() { super.onCreate() instance = this - DebugDB.getAddressLog(); + startKoin { androidContext(this@App) modules(appModules) diff --git a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt index 25aa04a5..10a2118f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt +++ b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt @@ -1,9 +1,8 @@ package code.name.monkey.retromusic -import code.name.monkey.retromusic.db.PlaylistDatabase +import androidx.room.Room import code.name.monkey.retromusic.db.PlaylistWithSongs -import code.name.monkey.retromusic.repository.RealRoomPlaylistRepository -import code.name.monkey.retromusic.repository.RoomPlaylistRepository +import code.name.monkey.retromusic.db.RetroDatabase import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.albums.AlbumDetailsViewModel import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel @@ -18,6 +17,22 @@ import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.bind import org.koin.dsl.module +private val roomModule = module { + single { + Room.databaseBuilder(androidContext(), RetroDatabase::class.java, "playlist.db") + .allowMainThreadQueries() + .fallbackToDestructiveMigration() + .build() + } + + factory { + get().playlistDao() + } + + single { + RealRoomRepository(get()) + } bind RoomPlaylistRepository::class +} private val mainModule = module { single { androidContext().contentResolver @@ -70,14 +85,6 @@ private val dataModule = module { get() ) } - - single { - PlaylistDatabase.getDatabase(get()).playlistDao() - } - - single { - RealRoomPlaylistRepository(get()) - } bind RoomPlaylistRepository::class } private val viewModules = module { @@ -119,4 +126,4 @@ private val viewModules = module { } } -val appModules = listOf(mainModule, dataModule, viewModules, networkModule) \ No newline at end of file +val appModules = listOf(mainModule, dataModule, viewModules, networkModule, roomModule) \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDao.kt b/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDao.kt index f2266037..dcd90602 100644 --- a/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDao.kt +++ b/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDao.kt @@ -26,8 +26,8 @@ interface PlaylistDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertSongs(songEntities: List) - @Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistName AND id = :songId") - suspend fun checkSongExistsWithPlaylistName(playlistName: String, songId: Int): List + @Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId") + suspend fun checkSongExistsWithPlaylistId(playlistId: Int, songId: Int): List @Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistId") suspend fun getSongs(playlistId: Int): List @@ -41,4 +41,6 @@ interface PlaylistDao { @Delete suspend fun removeSongsFromPlaylist(songs: List) + @Query("DELETE FROM SongEntity WHERE playlist_creator_id = :playlistId AND id = :songId") + fun removeSong(playlistId: Int, songId: Int) } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDatabase.kt b/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDatabase.kt deleted file mode 100644 index f1149c4f..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDatabase.kt +++ /dev/null @@ -1,34 +0,0 @@ -package code.name.monkey.retromusic.db - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase - -@Database( - entities = [PlaylistEntity::class, SongEntity::class], - version = 8, - exportSchema = false -) -abstract class PlaylistDatabase : RoomDatabase() { - abstract fun playlistDao(): PlaylistDao - - companion object { - @Volatile - private var INSTANCE: PlaylistDatabase? = null - - fun getDatabase( - context: Context - ): PlaylistDatabase { - return INSTANCE ?: synchronized(this) { - val instance = Room.databaseBuilder( - context.applicationContext, - PlaylistDatabase::class.java, - "playlists.db" - ).fallbackToDestructiveMigration().build() - INSTANCE = instance - instance - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/db/RetroDatabase.kt b/app/src/main/java/code/name/monkey/retromusic/db/RetroDatabase.kt new file mode 100644 index 00000000..dc1c51a9 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/db/RetroDatabase.kt @@ -0,0 +1,13 @@ +package code.name.monkey.retromusic.db + +import androidx.room.Database +import androidx.room.RoomDatabase + +@Database( + entities = [PlaylistEntity::class, SongEntity::class], + version = 8, + exportSchema = false +) +abstract class RetroDatabase : RoomDatabase() { + abstract fun playlistDao(): PlaylistDao +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveSongFromPlaylistDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveSongFromPlaylistDialog.kt index 7193a5b3..f8e93980 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveSongFromPlaylistDialog.kt +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/RemoveSongFromPlaylistDialog.kt @@ -65,7 +65,7 @@ class RemoveSongFromPlaylistDialog : DialogFragment() { .setMessage(pair.second) .setPositiveButton(R.string.remove_action) { _, _ -> lifecycleScope.launch(Dispatchers.IO) { - repository.removeSongFromPlaylist(songs) + repository.removeSongsFromPlaylist(songs) } /* PlaylistsUtil.removeFromPlaylist( requireContext(), diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt index ed4e7f91..b64dba16 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.content.ContentUris import android.content.Intent import android.media.MediaMetadataRetriever -import android.os.AsyncTask import android.os.Build import android.os.Bundle import android.provider.MediaStore @@ -22,8 +21,10 @@ import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity +import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.dialogs.* import code.name.monkey.retromusic.extensions.hide +import code.name.monkey.retromusic.extensions.whichFragment import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -31,20 +32,22 @@ import code.name.monkey.retromusic.interfaces.PaletteColorHolder import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.lyrics.Lyrics import code.name.monkey.retromusic.repository.RealRepository +import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.* import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.* import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.android.ext.android.get +import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.sharedViewModel import java.io.FileNotFoundException abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout), Toolbar.OnMenuItemClickListener, PaletteColorHolder, PlayerAlbumCoverFragment.Callbacks { - private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null - private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null private var playerAlbumCoverFragment: PlayerAlbumCoverFragment? = null protected val libraryViewModel by sharedViewModel() @@ -70,7 +73,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme return true } R.id.action_add_to_playlist -> { - lifecycleScope.launch(Dispatchers.IO) { + lifecycleScope.launch(IO) { val playlists = get().roomPlaylists() withContext(Dispatchers.Main) { AddToRetroPlaylist.create(playlists, song) @@ -159,7 +162,16 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme } protected open fun toggleFavorite(song: Song) { - MusicUtil.toggleFavorite(requireActivity(), song) + lifecycleScope.launch(IO) { + val playlist: PlaylistEntity = repository.favoritePlaylist().first() + val songEntity = song.toSongEntity(playlist.playListId) + if (repository.isFavoriteSong(songEntity).isNotEmpty()) { + repository.removeSongFromPlaylist(songEntity) + } else { + repository.insertSongs(listOf(song.toSongEntity(playlist.playListId))) + } + requireContext().sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED)) + } } abstract fun playerToolbar(): Toolbar? @@ -181,85 +193,65 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme updateIsFavorite() updateLyrics() } - - override fun onDestroyView() { - if (updateIsFavoriteTask != null && !updateIsFavoriteTask!!.isCancelled) { - updateIsFavoriteTask!!.cancel(true) - } - if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask!!.isCancelled) { - updateLyricsAsyncTask!!.cancel(true) - } - super.onDestroyView() - } - + @SuppressLint("StaticFieldLeak") fun updateIsFavorite() { - if (updateIsFavoriteTask != null) { - updateIsFavoriteTask!!.cancel(false) - } - updateIsFavoriteTask = object : AsyncTask() { - override fun doInBackground(vararg params: Song): Boolean { - return MusicUtil.isFavorite(requireActivity(), params[0]) - } - - override fun onPostExecute(isFavorite: Boolean) { - val res = if (isFavorite) - R.drawable.ic_favorite - else - R.drawable.ic_favorite_border - + lifecycleScope.launch(IO) { + val playlist: PlaylistEntity = repository.favoritePlaylist().first() + val song = MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId) + val isFavorite = repository.isFavoriteSong(song).isNotEmpty() + withContext(Dispatchers.Main) { + val icon = if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border val drawable = - RetroUtil.getTintedVectorDrawable(requireContext(), res, toolbarIconColor()) - if (playerToolbar() != null && playerToolbar()!!.menu.findItem(R.id.action_toggle_favorite) != null) - playerToolbar()!!.menu.findItem(R.id.action_toggle_favorite).setIcon(drawable) - .title = - if (isFavorite) getString(R.string.action_remove_from_favorites) else getString( - R.string.action_add_to_favorites - ) + RetroUtil.getTintedVectorDrawable(requireContext(), icon, toolbarIconColor()) + if (playerToolbar() != null) { + playerToolbar()?.menu?.findItem(R.id.action_toggle_favorite) + ?.setIcon(drawable)?.title = + if (isFavorite) getString(R.string.action_remove_from_favorites) + else getString(R.string.action_add_to_favorites) + } } - }.execute(MusicPlayerRemote.currentSong) + } } @SuppressLint("StaticFieldLeak") private fun updateLyrics() { - if (updateLyricsAsyncTask != null) updateLyricsAsyncTask!!.cancel(false) - - updateLyricsAsyncTask = object : AsyncTask() { - override fun onPreExecute() { - super.onPreExecute() - setLyrics(null) + setLyrics(null) + lifecycleScope.launch(IO) { + val song = MusicPlayerRemote.currentSong + val lyrics = try { + var data: String? = LyricUtil.getStringFromFile(song.title, song.artistName) + if (TextUtils.isEmpty(data)) { + data = MusicUtil.getLyrics(song) + if (TextUtils.isEmpty(data)) { + null + } else { + Lyrics.parse(song, data) + } + } else Lyrics.parse(song, data!!) + } catch (err: FileNotFoundException) { + null } - - override fun doInBackground(vararg params: Song): Lyrics? { - try { - var data: String? = - LyricUtil.getStringFromFile(params[0].title, params[0].artistName) - return if (TextUtils.isEmpty(data)) { - data = MusicUtil.getLyrics(params[0]) - return if (TextUtils.isEmpty(data)) { - null - } else { - Lyrics.parse(params[0], data) - } - } else Lyrics.parse(params[0], data!!) - } catch (err: FileNotFoundException) { - return null - } + withContext(Main) { + setLyrics(lyrics) } - - override fun onPostExecute(l: Lyrics?) { - setLyrics(l) - } - - override fun onCancelled(s: Lyrics?) { - onPostExecute(null) - } - }.execute(MusicPlayerRemote.currentSong) + } } open fun setLyrics(l: Lyrics?) { } + private val repository by inject() + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + lifecycleScope.launch(IO) { + if (repository.checkPlaylistExists(getString(R.string.favorites)).isEmpty()) { + repository.createPlaylist(PlaylistEntity(getString(R.string.favorites))) + } + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) if (PreferenceUtil.isFullScreenMode && @@ -267,8 +259,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme ) { view.findViewById(R.id.status_bar).visibility = View.GONE } - playerAlbumCoverFragment = - childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment? + playerAlbumCoverFragment = whichFragment(R.id.playerAlbumCoverFragment) playerAlbumCoverFragment?.setCallbacks(this) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt index dab9e818..8fd90bfb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt @@ -114,9 +114,15 @@ interface Repository { suspend fun renameRoomPlaylist(playlistId: Int, name: String) - suspend fun removeSongFromPlaylist(songs: List) + suspend fun removeSongsFromPlaylist(songs: List) + + suspend fun removeSongFromPlaylist(songEntity: SongEntity) suspend fun deleteSongsFromPlaylist(playlists: List) + + suspend fun favoritePlaylist(): List + + suspend fun isFavoriteSong(songEntity: SongEntity): List } class RealRepository( @@ -215,11 +221,11 @@ class RealRepository( override suspend fun homeSections(): List { val homeSections = mutableListOf() val sections = listOf( + suggestionsHome(), topArtistsHome(), topAlbumsHome(), recentArtistsHome(), recentAlbumsHome(), - suggestionsHome(), favoritePlaylistHome() ) for (section in sections) { @@ -264,18 +270,27 @@ class RealRepository( override suspend fun renameRoomPlaylist(playlistId: Int, name: String) = roomPlaylistRepository.renamePlaylistEntity(playlistId, name) - override suspend fun removeSongFromPlaylist(songs: List) = + override suspend fun removeSongsFromPlaylist(songs: List) = roomPlaylistRepository.removeSongsFromPlaylist(songs) + override suspend fun removeSongFromPlaylist(songEntity: SongEntity) = + roomPlaylistRepository.removeSongFromPlaylist(songEntity) + + override suspend fun deleteSongsFromPlaylist(playlists: List) = roomPlaylistRepository.deleteSongsFromPlaylist(playlists) + override suspend fun favoritePlaylist(): List = + roomPlaylistRepository.favoritePlaylist(context.getString(R.string.favorites)) + + override suspend fun isFavoriteSong(songEntity: SongEntity): List = + roomPlaylistRepository.isFavoriteSong(songEntity) + override suspend fun suggestionsHome(): Home { val songs = NotPlayedPlaylist().songs().shuffled().takeIf { it.size > 9 } ?: emptyList() - println(songs.size) return Home(songs, SUGGESTIONS) } diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/RoomPlaylistRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/RoomPlaylistRepository.kt index b7b8e009..4aff0741 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/RoomPlaylistRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/RoomPlaylistRepository.kt @@ -18,9 +18,12 @@ interface RoomPlaylistRepository { suspend fun renamePlaylistEntity(playlistId: Int, name: String) suspend fun removeSongsFromPlaylist(songs: List) suspend fun deleteSongsFromPlaylist(playlists: List) + suspend fun favoritePlaylist(favorite: String): List + suspend fun isFavoriteSong(songEntity: SongEntity): List + suspend fun removeSongFromPlaylist(songEntity: SongEntity) } -class RealRoomPlaylistRepository( +class RealRoomRepository( private val playlistDao: PlaylistDao ) : RoomPlaylistRepository { @WorkerThread @@ -68,4 +71,16 @@ class RealRoomPlaylistRepository( } } + override suspend fun favoritePlaylist(favorite: String): List = + playlistDao.checkPlaylistExists(favorite) + + override suspend fun isFavoriteSong(songEntity: SongEntity): List = + playlistDao.checkSongExistsWithPlaylistId( + songEntity.playlistCreatorId, + songEntity.id + ) + + override suspend fun removeSongFromPlaylist(songEntity: SongEntity) = + playlistDao.removeSong(songEntity.playlistCreatorId, songEntity.id) + } \ No newline at end of file