diff --git a/README.md b/README.md index 147dc995..66b64c0d 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ favorite songs. No other music player has this feature. We are trying our best to bring you the best user experience. The app is regulary being updated for bug fixes and new features. -### FAQ +### ❓ FAQ Please read the FAQ here: https://del.dog/RetroFaq In any case, you find or notice any bugs please report them by 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 25367b22..40a296ba 100644 --- a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt +++ b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt @@ -3,8 +3,8 @@ package code.name.monkey.retromusic import androidx.room.Room import androidx.room.RoomDatabase import androidx.sqlite.db.SupportSQLiteDatabase +import code.name.monkey.retromusic.db.BlackListStoreDao import code.name.monkey.retromusic.db.BlackListStoreEntity -import code.name.monkey.retromusic.db.PlaylistDao import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.db.RetroDatabase import code.name.monkey.retromusic.fragments.LibraryViewModel @@ -13,6 +13,7 @@ import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel import code.name.monkey.retromusic.fragments.genres.GenreDetailsViewModel import code.name.monkey.retromusic.fragments.playlists.PlaylistDetailsViewModel import code.name.monkey.retromusic.fragments.search.SearchViewModel +import code.name.monkey.retromusic.fragments.songs.SongsViewModel import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.network.networkModule import code.name.monkey.retromusic.repository.* @@ -35,7 +36,7 @@ private val roomModule = module { super.onOpen(db) GlobalScope.launch(IO) { FilePathUtil.blacklistFilePaths().map { - get().insertBlacklistPath(BlackListStoreEntity(it)) + get().insertBlacklistPath(BlackListStoreEntity(it)) } } } @@ -48,9 +49,17 @@ private val roomModule = module { get().playlistDao() } + factory { + get().blackListStore() + } + + factory { + get().playCountDao() + } + single { - RealRoomRepository(get()) - } bind RoomPlaylistRepository::class + RealRoomRepository(get(), get(), get()) + } bind RoomRepository::class } private val mainModule = module { single { @@ -143,6 +152,10 @@ private val viewModules = module { viewModel { SearchViewModel(get()) } + + viewModel { + SongsViewModel(get()) + } } 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/activities/base/AbsMusicServiceActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsMusicServiceActivity.kt index 9057625f..1e9efee8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsMusicServiceActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsMusicServiceActivity.kt @@ -4,17 +4,23 @@ import android.Manifest import android.content.* import android.os.Bundle import android.os.IBinder +import androidx.lifecycle.lifecycleScope import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.db.toPlayCount import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.interfaces.MusicServiceEventListener +import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.service.MusicService.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.koin.android.ext.android.inject import java.lang.ref.WeakReference import java.util.* abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventListener { private val mMusicServiceEventListeners = ArrayList() - + private val repository: RealRepository by inject() private var serviceToken: MusicPlayerRemote.ServiceToken? = null private var musicStateReceiver: MusicStateReceiver? = null private var receiverRegistered: Boolean = false @@ -93,6 +99,22 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventLis for (listener in mMusicServiceEventListeners) { listener.onPlayingMetaChanged() } + lifecycleScope.launch(Dispatchers.IO) { + val entity = repository.songPresentInHistory(MusicPlayerRemote.currentSong) + if (entity != null) { + repository.updateHistorySong(MusicPlayerRemote.currentSong) + } else { + repository.addSongToHistory(MusicPlayerRemote.currentSong) + } + val songs = repository.checkSongExistInPlayCount(MusicPlayerRemote.currentSong.id) + if (songs.isNotEmpty()) { + repository.updateSongInPlayCount(songs.first().apply { + playCount += 1 + }) + } else { + repository.insertSongInPlayCount(MusicPlayerRemote.currentSong.toPlayCount()) + } + } } override fun onQueueChanged() { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt index d81485a0..d21b209d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt @@ -73,7 +73,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { setContentView(createContentView()) chooseFragmentForTheme() setupSlidingUpPanel() - addMusicServiceEventListener(libraryViewModel) setupBottomSheet() diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt index d9f006e7..1571e07a 100755 --- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt @@ -14,7 +14,6 @@ import android.view.MenuItem import android.view.View import android.view.animation.OvershootInterpolator import androidx.appcompat.app.AlertDialog -import androidx.lifecycle.lifecycleScope import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil @@ -182,11 +181,9 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { saveFab = findViewById(R.id.saveTags) getIntentExtras() - lifecycleScope.launchWhenCreated { - songPaths = getSongPaths() - if (songPaths!!.isEmpty()) { - finish() - } + songPaths = getSongPaths() + if (songPaths!!.isEmpty()) { + finish() } setUpViews() } @@ -258,7 +255,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { } } - protected abstract suspend fun getSongPaths(): List + protected abstract fun getSongPaths(): List protected fun searchWebFor(vararg keys: String) { val stringBuilder = StringBuilder() diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt index 8c3e142b..8a908dd6 100755 --- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt @@ -167,7 +167,7 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher { ) } - override suspend fun getSongPaths(): List { + override fun getSongPaths(): List { val songs = repository.albumById(id).songs val paths = ArrayList(songs!!.size) for (song in songs) { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt index 5e3d5909..35b02bda 100755 --- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/SongTagEditorActivity.kt @@ -88,7 +88,7 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher { writeValuesToFiles(fieldKeyValueMap, null) } - override suspend fun getSongPaths(): List { + override fun getSongPaths(): List { val paths = ArrayList(1) paths.add(songRepository.song(id).data) return paths diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt index 7f1af392..17f0d78d 100755 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt @@ -154,13 +154,11 @@ class PlaylistAdapter( inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { init { - image?.apply { val iconPadding = activity.resources.getDimensionPixelSize(R.dimen.list_item_image_icon_padding) setPadding(iconPadding, iconPadding, iconPadding, iconPadding) } - menu?.setOnClickListener { view -> val popupMenu = PopupMenu(activity, view) popupMenu.inflate(R.menu.menu_item_playlist) diff --git a/app/src/main/java/code/name/monkey/retromusic/db/BlackListStoreDao.kt b/app/src/main/java/code/name/monkey/retromusic/db/BlackListStoreDao.kt new file mode 100644 index 00000000..db0dd0f6 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/db/BlackListStoreDao.kt @@ -0,0 +1,21 @@ +package code.name.monkey.retromusic.db + +import androidx.room.* + +@Dao +interface BlackListStoreDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertBlacklistPath(blackListStoreEntity: BlackListStoreEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertBlacklistPath(blackListStoreEntities: List) + + @Delete + suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity) + + @Query("DELETE FROM BlackListStoreEntity") + suspend fun clearBlacklist() + + @Query("SELECT * FROM BlackListStoreEntity") + fun blackListPaths(): List +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/db/PlayCountDao.kt b/app/src/main/java/code/name/monkey/retromusic/db/PlayCountDao.kt new file mode 100644 index 00000000..9c22d4da --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/db/PlayCountDao.kt @@ -0,0 +1,21 @@ +package code.name.monkey.retromusic.db + +import androidx.room.* + +@Dao +interface PlayCountDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertSongInPlayCount(playCountEntity: PlayCountEntity) + + @Update + fun updateSongInPlayCount(playCountEntity: PlayCountEntity) + + @Delete + fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) + + @Query("SELECT * FROM PlayCountEntity WHERE id =:songId") + fun checkSongExistInPlayCount(songId: Int): List + + @Query("SELECT * FROM PlayCountEntity ORDER BY play_count DESC") + fun playCountSongs(): List +} \ 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 1e4356c9..c3b7f801 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 @@ -63,30 +63,6 @@ interface PlaylistDao { @Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId") fun favoritesSongs(playlistId: Int): List - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertSongInPlayCount(playCountEntity: PlayCountEntity) - @Update - fun updateSongInPlayCount(playCountEntity: PlayCountEntity) - @Delete - fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) - - @Query("SELECT * FROM PlayCountEntity WHERE id =:songId") - fun checkSongExistInPlayCount(songId: Int): List - - @Query("SELECT * FROM PlayCountEntity ORDER BY play_count DESC") - fun playCountSongs(): List - - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertBlacklistPath(blackListStoreEntity: BlackListStoreEntity) - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertBlacklistPath(blackListStoreEntities: List) - - @Delete - suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity) - - @Query("DELETE FROM BlackListStoreEntity") - suspend fun clearBlacklist() } \ 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 index 8ef2f0d2..02cb6586 100644 --- a/app/src/main/java/code/name/monkey/retromusic/db/RetroDatabase.kt +++ b/app/src/main/java/code/name/monkey/retromusic/db/RetroDatabase.kt @@ -10,4 +10,6 @@ import androidx.room.RoomDatabase ) abstract class RetroDatabase : RoomDatabase() { abstract fun playlistDao(): PlaylistDao + abstract fun blackListStore(): BlackListStoreDao + abstract fun playCountDao(): PlayCountDao } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToRetroPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToRetroPlaylist.kt index e00ec73b..52859a57 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToRetroPlaylist.kt +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/AddToRetroPlaylist.kt @@ -51,7 +51,7 @@ class AddToRetroPlaylist : BottomSheetDialogFragment() { return materialDialog(R.string.add_playlist_title) .setItems(playlistNames.toTypedArray()) { _, which -> if (which == 0) { - CreateRetroPlaylist().show(requireActivity().supportFragmentManager, "Dialog") + CreateRetroPlaylist.create(songs).show(requireActivity().supportFragmentManager, "Dialog") } else { lifecycleScope.launch(Dispatchers.IO) { val songEntities = songs.toSongEntity(playlistEntities[which - 1]) diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/CreateRetroPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/CreateRetroPlaylist.kt index 6d237ebb..bb975366 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/CreateRetroPlaylist.kt +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/CreateRetroPlaylist.kt @@ -5,14 +5,18 @@ import android.os.Bundle import android.text.TextUtils import android.view.LayoutInflater import android.widget.Toast +import androidx.core.os.bundleOf import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope +import code.name.monkey.retromusic.EXTRA_SONG import code.name.monkey.retromusic.R import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.extensions.colorButtons +import code.name.monkey.retromusic.extensions.extraNotNull import code.name.monkey.retromusic.extensions.materialDialog import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.ReloadType.Playlists +import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.repository.RealRepository import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout @@ -25,8 +29,24 @@ import org.koin.androidx.viewmodel.ext.android.sharedViewModel class CreateRetroPlaylist : DialogFragment() { private val repository by inject() private val libraryViewModel by sharedViewModel() + + companion object { + fun create(song: Song): CreateRetroPlaylist { + val list = mutableListOf() + list.add(song) + return create(list) + } + + fun create(songs: List): CreateRetroPlaylist { + return CreateRetroPlaylist().apply { + arguments = bundleOf(EXTRA_SONG to songs) + } + } + } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val view = LayoutInflater.from(requireActivity()).inflate(R.layout.dialog_playlist, null) + val songs = extraNotNull>(EXTRA_SONG).value val playlistView: TextInputEditText = view.actionNewPlaylist val playlistContainer: TextInputLayout = view.actionNewPlaylistContainer return materialDialog(R.string.new_playlist_title) @@ -38,13 +58,14 @@ class CreateRetroPlaylist : DialogFragment() { if (!TextUtils.isEmpty(playlistName)) { lifecycleScope.launch(Dispatchers.IO) { if (repository.checkPlaylistExists(playlistName).isEmpty()) { - repository.createPlaylist(PlaylistEntity(playlistName)) + val playlistId = repository.createPlaylist(PlaylistEntity(playlistName)) + println(playlistId) + repository.insertSongs(songs.map { it.toSongEntity(playlistId.toInt()) }) libraryViewModel.forceReload(Playlists) } else { Toast.makeText(requireContext(), "Playlist exists", Toast.LENGTH_SHORT) .show() } - } } else { playlistContainer.error = "Playlist is can't be empty" diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/CoroutineViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/CoroutineViewModel.kt new file mode 100644 index 00000000..9b6a5ca4 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/CoroutineViewModel.kt @@ -0,0 +1,23 @@ +package code.name.monkey.retromusic.fragments + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.* +import kotlin.coroutines.CoroutineContext + +open class CoroutineViewModel( + private val mainDispatcher: CoroutineDispatcher +) : ViewModel() { + private val job = Job() + protected val scope = CoroutineScope(job + mainDispatcher) + + protected fun launch( + context: CoroutineContext = mainDispatcher, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit + ) = scope.launch(context, start, block) + + override fun onCleared() { + super.onCleared() + job.cancel() + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt index 57e11cdd..c71bcceb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt @@ -85,7 +85,9 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de layoutManager = linearLayoutManager() } lifecycleScope.launch(IO) { - val songs = repository.recentSongs() + val songs = repository.playCountSongs().map { + it.toSong() + } withContext(Main) { songAdapter.swapDataSet(songs) } } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt index e531b510..8a4c8cac 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt @@ -7,7 +7,6 @@ import androidx.lifecycle.viewModelScope import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.db.SongEntity -import code.name.monkey.retromusic.db.toPlayCount import code.name.monkey.retromusic.fragments.ReloadType.* import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.interfaces.MusicServiceEventListener @@ -88,7 +87,6 @@ class LibraryViewModel( fun forceReload(reloadType: ReloadType) = viewModelScope.launch { - println(reloadType) when (reloadType) { Songs -> songs.value = loadSongs.await() Albums -> albums.value = loadAlbums.await() @@ -123,22 +121,7 @@ class LibraryViewModel( override fun onPlayingMetaChanged() { println("onPlayingMetaChanged") - viewModelScope.launch(IO) { - val entity = repository.songPresentInHistory(MusicPlayerRemote.currentSong) - if (entity != null) { - repository.updateHistorySong(MusicPlayerRemote.currentSong) - } else { - repository.addSongToHistory(MusicPlayerRemote.currentSong) - } - val songs = repository.checkSongExistInPlayCount(MusicPlayerRemote.currentSong.id) - if (songs.isNotEmpty()) { - repository.updateSongInPlayCount(songs.first().apply { - playCount += playCount + 1 - }) - } else { - repository.insertSongInPlayCount(MusicPlayerRemote.currentSong.toPlayCount()) - } - } + } override fun onPlayStateChanged() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt index 95b0a33e..bd9516fb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt @@ -14,6 +14,7 @@ import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager +import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.EXTRA_ALBUM_ID @@ -32,6 +33,7 @@ import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.glide.AlbumGlideRequest import code.name.monkey.retromusic.glide.ArtistGlideRequest import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SingleColorTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.model.Album @@ -77,9 +79,9 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det toolbar.title = null postponeEnterTransition() - detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer { - showAlbum(it) + detailsViewModel.getAlbum2().observe(viewLifecycleOwner, Observer { startPostponedEnterTransition() + showAlbum(it) }) detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { loadArtistImage(it) @@ -232,16 +234,18 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det .build() .dontAnimate() .dontTransform() - .into(object : RetroMusicColoredTarget(image) { - override fun onColorReady(colors: MediaNotificationProcessor) { - setColors(colors) + .into(object : SingleColorTarget(image) { + override fun onColorReady(color: Int) { + setColors(color) } }) } - private fun setColors(color: MediaNotificationProcessor) { - shuffleAction.applyColor(color.backgroundColor) - playAction.applyOutlineColor(color.backgroundColor) + private fun setColors(color: Int) { + val finalColor = + if (PreferenceUtil.isAdaptiveColor) color else ThemeStore.accentColor(requireContext()) + shuffleAction.applyColor(finalColor) + playAction.applyOutlineColor(finalColor) } override fun onAlbumClick(albumId: Int, view: View) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt index a951531f..f698f882 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt @@ -1,16 +1,13 @@ package code.name.monkey.retromusic.fragments.albums -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.* import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.repository.RealRepository import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.async import kotlinx.coroutines.launch @@ -24,7 +21,11 @@ class AlbumDetailsViewModel( private val _lastFmAlbum = MutableLiveData() private val _moreAlbums = MutableLiveData>() - fun getAlbum(): LiveData = _album + fun getAlbum(): LiveData = liveData(IO) { + val album = realRepository.albumByIdAsync(albumId) + emit(album) + } + fun getArtist(): LiveData = _artist fun getAlbumInfo(): LiveData = _lastFmAlbum fun getMoreAlbums(): LiveData> = _moreAlbums @@ -33,17 +34,22 @@ class AlbumDetailsViewModel( loadAlbumDetails() } - private fun loadAlbumDetails() = viewModelScope.launch { + fun getAlbum2() = liveData(context = viewModelScope.coroutineContext + IO) { + val album = realRepository.albumByIdAsync(albumId) + emit(album) + } + + private fun loadAlbumDetails() = viewModelScope.launch(IO) { val album = loadAlbumAsync.await() ?: throw NullPointerException("Album couldn't found") _album.postValue(album) } - fun loadAlbumInfo(album: Album) = viewModelScope.launch(Dispatchers.IO) { + fun loadAlbumInfo(album: Album) = viewModelScope.launch(IO) { val lastFmAlbum = realRepository.albumInfo(album.artistName ?: "-", album.title ?: "-") _lastFmAlbum.postValue(lastFmAlbum) } - fun loadArtist(artistId: Int) = viewModelScope.launch(Dispatchers.IO) { + fun loadArtist(artistId: Int) = viewModelScope.launch(IO) { val artist = realRepository.artistById(artistId) _artist.postValue(artist) @@ -53,8 +59,8 @@ class AlbumDetailsViewModel( } private val loadAlbumAsync: Deferred - get() = viewModelScope.async(Dispatchers.IO) { - realRepository.albumById(albumId) + get() = viewModelScope.async(IO) { + realRepository.albumByIdAsync(albumId) } override fun onMediaStoreChanged() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt index 5b4a30b6..be1da592 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt @@ -1,7 +1,7 @@ package code.name.monkey.retromusic.fragments.albums import android.os.Bundle -import android.view.View +import android.view.* import androidx.core.os.bundleOf import androidx.lifecycle.Observer import androidx.navigation.fragment.FragmentNavigatorExtras @@ -12,7 +12,11 @@ import code.name.monkey.retromusic.adapter.album.AlbumAdapter import code.name.monkey.retromusic.extensions.findActivityNavController import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment +import code.name.monkey.retromusic.helper.SortOrder +import code.name.monkey.retromusic.helper.SortOrder.AlbumSortOrder import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil + class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment(), AlbumClickListener { @@ -103,6 +107,181 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment subMenu.findItem(R.id.action_layout_card).isChecked = true + R.layout.item_grid -> subMenu.findItem(R.id.action_layout_normal).isChecked = true + R.layout.item_card_color -> + subMenu.findItem(R.id.action_layout_colored_card).isChecked = true + R.layout.item_grid_circle -> + subMenu.findItem(R.id.action_layout_circular).isChecked = true + R.layout.image -> subMenu.findItem(R.id.action_layout_image).isChecked = true + R.layout.item_image_gradient -> + subMenu.findItem(R.id.action_layout_gradient_image).isChecked = true + } + } + + private fun setUpGridSizeMenu( + gridSizeMenu: SubMenu + ) { + when (getGridSize()) { + 1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = + true + 2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true + 3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true + 4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true + 5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true + 6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true + 7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true + 8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true + } + val gridSize: Int = maxGridSize + if (gridSize < 8) { + gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false + } + if (gridSize < 7) { + gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false + } + if (gridSize < 6) { + gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false + } + if (gridSize < 5) { + gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false + } + if (gridSize < 4) { + gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false + } + if (gridSize < 3) { + gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (handleGridSizeMenuItem(item)) { + return true + } + if (handleLayoutResType(item)) { + return true + } + if (handleSortOrderMenuItem(item)) { + return true + } + return super.onOptionsItemSelected(item) + } + + private fun handleSortOrderMenuItem( + item: MenuItem + ): Boolean { + var sortOrder: String? = null + + when (item.itemId) { + R.id.action_album_sort_order_asc -> sortOrder = AlbumSortOrder.ALBUM_A_Z + R.id.action_album_sort_order_desc -> sortOrder = AlbumSortOrder.ALBUM_Z_A + R.id.action_album_sort_order_artist -> sortOrder = AlbumSortOrder.ALBUM_ARTIST + R.id.action_album_sort_order_year -> sortOrder = AlbumSortOrder.ALBUM_YEAR + } + if (sortOrder != null) { + item.isChecked = true + setAndSaveSortOrder(sortOrder) + return true + } + return false + } + + private fun handleLayoutResType( + item: MenuItem + ): Boolean { + var layoutRes = -1 + when (item.itemId) { + R.id.action_layout_normal -> layoutRes = R.layout.item_grid + R.id.action_layout_card -> layoutRes = R.layout.item_card + R.id.action_layout_colored_card -> layoutRes = R.layout.item_card_color + R.id.action_layout_circular -> layoutRes = R.layout.item_grid_circle + R.id.action_layout_image -> layoutRes = R.layout.image + R.id.action_layout_gradient_image -> layoutRes = R.layout.item_image_gradient + } + if (layoutRes != -1) { + item.isChecked = true + setAndSaveLayoutRes(layoutRes) + return true + } + return false + } + + private fun handleGridSizeMenuItem( + item: MenuItem + ): Boolean { + var gridSize = 0 + when (item.itemId) { + R.id.action_grid_size_1 -> gridSize = 1 + R.id.action_grid_size_2 -> gridSize = 2 + R.id.action_grid_size_3 -> gridSize = 3 + R.id.action_grid_size_4 -> gridSize = 4 + R.id.action_grid_size_5 -> gridSize = 5 + R.id.action_grid_size_6 -> gridSize = 6 + R.id.action_grid_size_7 -> gridSize = 7 + R.id.action_grid_size_8 -> gridSize = 8 + } + if (gridSize > 0) { + item.isChecked = true + setAndSaveGridSize(gridSize) + return true + } + return false + } + } interface AlbumClickListener { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt index 90049d7c..4d540e02 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt @@ -17,6 +17,7 @@ import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager +import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter @@ -28,15 +29,15 @@ import code.name.monkey.retromusic.extensions.showToast import code.name.monkey.retromusic.fragments.albums.AlbumClickListener import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.glide.ArtistGlideRequest -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.glide.SingleColorTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.util.CustomArtistImageUtil import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroUtil -import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.bumptech.glide.Glide import kotlinx.android.synthetic.main.fragment_artist_content.* import kotlinx.android.synthetic.main.fragment_artist_details.* @@ -181,19 +182,22 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d private fun loadArtistImage(artist: Artist) { ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist) .generatePalette(requireContext()).build() - .dontAnimate().into(object : RetroMusicColoredTarget(image) { - override fun onColorReady(colors: MediaNotificationProcessor) { - startPostponedEnterTransition() - setColors(colors) + .dontAnimate() + .into(object : SingleColorTarget(image) { + override fun onColorReady(color: Int) { + setColors(color) } }) } - private fun setColors(color: MediaNotificationProcessor) { - shuffleAction.applyColor(color.backgroundColor) - playAction.applyOutlineColor(color.backgroundColor) + private fun setColors(color: Int) { + val finalColor = if (PreferenceUtil.isAdaptiveColor) color + else ThemeStore.accentColor(requireContext()) + shuffleAction.applyColor(finalColor) + playAction.applyOutlineColor(finalColor) } + override fun onAlbumClick(albumId: Int, view: View) { findNavController().navigate( R.id.albumDetailsFragment, diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt index 30e188e6..f0889914 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt @@ -1,7 +1,7 @@ package code.name.monkey.retromusic.fragments.artists import android.os.Bundle -import android.view.View +import android.view.* import android.widget.ImageView import androidx.core.os.bundleOf import androidx.lifecycle.Observer @@ -12,7 +12,10 @@ import code.name.monkey.retromusic.adapter.artist.ArtistAdapter import code.name.monkey.retromusic.extensions.findActivityNavController import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment +import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil + class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment(), ArtistClickListener { @@ -97,6 +100,161 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment subMenu.findItem(R.id.action_layout_card).isChecked = true + R.layout.item_grid -> subMenu.findItem(R.id.action_layout_normal).isChecked = true + R.layout.item_card_color -> + subMenu.findItem(R.id.action_layout_colored_card).isChecked = true + R.layout.item_grid_circle -> + subMenu.findItem(R.id.action_layout_circular).isChecked = true + R.layout.image -> subMenu.findItem(R.id.action_layout_image).isChecked = true + R.layout.item_image_gradient -> + subMenu.findItem(R.id.action_layout_gradient_image).isChecked = true + } + } + + private fun setUpGridSizeMenu( + gridSizeMenu: SubMenu + ) { + when (getGridSize()) { + 1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = + true + 2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true + 3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true + 4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true + 5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true + 6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true + 7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true + 8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true + } + val gridSize: Int = maxGridSize + if (gridSize < 8) { + gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false + } + if (gridSize < 7) { + gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false + } + if (gridSize < 6) { + gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false + } + if (gridSize < 5) { + gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false + } + if (gridSize < 4) { + gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false + } + if (gridSize < 3) { + gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (handleGridSizeMenuItem(item)) { + return true + } + if (handleLayoutResType(item)) { + return true + } + if (handleSortOrderMenuItem(item)) { + return true + } + return super.onOptionsItemSelected(item) + } + + private fun handleSortOrderMenuItem( + item: MenuItem + ): Boolean { + var sortOrder: String? = null + + when (item.itemId) { + R.id.action_artist_sort_order_asc -> sortOrder = ArtistSortOrder.ARTIST_A_Z + R.id.action_artist_sort_order_desc -> sortOrder = ArtistSortOrder.ARTIST_Z_A + } + if (sortOrder != null) { + item.isChecked = true + setAndSaveSortOrder(sortOrder) + return true + } + return false + } + + private fun handleLayoutResType( + item: MenuItem + ): Boolean { + var layoutRes = -1 + when (item.itemId) { + R.id.action_layout_normal -> layoutRes = R.layout.item_grid + R.id.action_layout_card -> layoutRes = R.layout.item_card + R.id.action_layout_colored_card -> layoutRes = R.layout.item_card_color + R.id.action_layout_circular -> layoutRes = R.layout.item_grid_circle + R.id.action_layout_image -> layoutRes = R.layout.image + R.id.action_layout_gradient_image -> layoutRes = R.layout.item_image_gradient + } + if (layoutRes != -1) { + item.isChecked = true + setAndSaveLayoutRes(layoutRes) + return true + } + return false + } + + private fun handleGridSizeMenuItem( + item: MenuItem + ): Boolean { + var gridSize = 0 + when (item.itemId) { + R.id.action_grid_size_1 -> gridSize = 1 + R.id.action_grid_size_2 -> gridSize = 2 + R.id.action_grid_size_3 -> gridSize = 3 + R.id.action_grid_size_4 -> gridSize = 4 + R.id.action_grid_size_5 -> gridSize = 5 + R.id.action_grid_size_6 -> gridSize = 6 + R.id.action_grid_size_7 -> gridSize = 7 + R.id.action_grid_size_8 -> gridSize = 8 + } + if (gridSize > 0) { + item.isChecked = true + setAndSaveGridSize(gridSize) + return true + } + return false + } } interface ArtistClickListener { 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 55405bd9..c7998b64 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 @@ -2,6 +2,7 @@ package code.name.monkey.retromusic.fragments.base import android.content.ContentUris import android.content.Intent +import android.graphics.drawable.Drawable import android.media.MediaMetadataRetriever import android.os.Build import android.os.Bundle @@ -21,6 +22,7 @@ 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.db.SongEntity import code.name.monkey.retromusic.dialogs.* import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.whichFragment @@ -85,7 +87,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme return true } R.id.action_save_playing_queue -> { - CreatePlaylistDialog.create(ArrayList(MusicPlayerRemote.playingQueue)) + CreateRetroPlaylist.create(ArrayList(MusicPlayerRemote.playingQueue)) .show(childFragmentManager, "ADD_TO_PLAYLIST") return true } @@ -182,29 +184,33 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme protected open fun toggleFavorite(song: Song) { lifecycleScope.launch(IO) { - val playlist: PlaylistEntity = libraryViewModel.favoritePlaylist().first() - val songEntity = song.toSongEntity(playlist.playListId) - val isFavorite = libraryViewModel.isFavoriteSong(songEntity).isNotEmpty() - if (isFavorite) { - libraryViewModel.removeSongFromPlaylist(songEntity) - } else { - libraryViewModel.insertSongs(listOf(song.toSongEntity(playlist.playListId))) - libraryViewModel.forceReload(ReloadType.Playlists) + val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist() + if (playlist != null) { + val songEntity = song.toSongEntity(playlist.playListId) + val isFavorite = libraryViewModel.isFavoriteSong(songEntity).isNotEmpty() + if (isFavorite) { + libraryViewModel.removeSongFromPlaylist(songEntity) + } else { + libraryViewModel.insertSongs(listOf(song.toSongEntity(playlist.playListId))) + } } + libraryViewModel.forceReload(ReloadType.Playlists) requireContext().sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED)) } } fun updateIsFavorite() { lifecycleScope.launch(IO) { - val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist().firstOrNull() + val playlist: PlaylistEntity? = libraryViewModel.favoritePlaylist() if (playlist != null) { - val song = MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId) - val isFavorite = libraryViewModel.isFavoriteSong(song).isNotEmpty() + val song: SongEntity = + MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId) + val isFavorite: Boolean = libraryViewModel.isFavoriteSong(song).isNotEmpty() withContext(Main) { val icon = if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border - val drawable = + val drawable: Drawable? = + RetroUtil.getTintedVectorDrawable( requireContext(), icon, diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt index 1532b895..cc13bad2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import code.name.monkey.retromusic.db.PlaylistWithSongs +import code.name.monkey.retromusic.db.toSongs import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.repository.RealRepository @@ -33,7 +34,7 @@ class PlaylistDetailsViewModel( private fun loadPlaylistSongs(playlist: PlaylistWithSongs) = viewModelScope.launch(Dispatchers.IO) { - val songs: List = realRepository.playlistSongs(playlist) + val songs: List = playlist.songs.toSongs() withContext(Main) { _playListSongs.postValue(songs) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt index 51dd9d8c..06d7f45c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt @@ -1,7 +1,7 @@ package code.name.monkey.retromusic.fragments.songs import android.os.Bundle -import android.view.View +import android.view.* import androidx.annotation.LayoutRes import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager @@ -9,9 +9,13 @@ import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment +import code.name.monkey.retromusic.helper.SortOrder.SongSortOrder import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil + class SongsFragment : AbsRecyclerViewCustomGridSizeFragment() { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) libraryViewModel.songsLiveData.observe(viewLifecycleOwner, Observer { @@ -34,7 +38,7 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment subMenu.findItem(R.id.action_layout_card).isChecked = true + R.layout.item_grid -> subMenu.findItem(R.id.action_layout_normal).isChecked = true + R.layout.item_card_color -> + subMenu.findItem(R.id.action_layout_colored_card).isChecked = true + R.layout.item_grid_circle -> + subMenu.findItem(R.id.action_layout_circular).isChecked = true + R.layout.image -> subMenu.findItem(R.id.action_layout_image).isChecked = true + R.layout.item_image_gradient -> + subMenu.findItem(R.id.action_layout_gradient_image).isChecked = true + } + } + + private fun setUpGridSizeMenu( + gridSizeMenu: SubMenu + ) { + when (getGridSize()) { + 1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = + true + 2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true + 3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true + 4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true + 5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true + 6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true + 7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true + 8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true + } + val gridSize: Int = maxGridSize + if (gridSize < 8) { + gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false + } + if (gridSize < 7) { + gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false + } + if (gridSize < 6) { + gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false + } + if (gridSize < 5) { + gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false + } + if (gridSize < 4) { + gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false + } + if (gridSize < 3) { + gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (handleGridSizeMenuItem(item)) { + return true + } + if (handleLayoutResType(item)) { + return true + } + if (handleSortOrderMenuItem(item)) { + return true + } + return super.onOptionsItemSelected(item) + } + + private fun handleSortOrderMenuItem( + item: MenuItem + ): Boolean { + var sortOrder: String? = null + + when (item.itemId) { + R.id.action_song_sort_order_asc -> sortOrder = SongSortOrder.SONG_A_Z + R.id.action_song_sort_order_desc -> sortOrder = SongSortOrder.SONG_Z_A + R.id.action_song_sort_order_artist -> sortOrder = SongSortOrder.SONG_ARTIST + R.id.action_song_sort_order_album -> sortOrder = SongSortOrder.SONG_ALBUM + R.id.action_song_sort_order_year -> sortOrder = SongSortOrder.SONG_YEAR + R.id.action_song_sort_order_date -> sortOrder = SongSortOrder.SONG_DATE + R.id.action_song_sort_order_composer -> sortOrder = SongSortOrder.COMPOSER + R.id.action_song_sort_order_date_modified -> sortOrder = + SongSortOrder.SONG_DATE_MODIFIED + } + if (sortOrder != null) { + item.isChecked = true + setAndSaveSortOrder(sortOrder) + return true + } + return false + } + + private fun handleLayoutResType( + item: MenuItem + ): Boolean { + var layoutRes = -1 + when (item.itemId) { + R.id.action_layout_normal -> layoutRes = R.layout.item_grid + R.id.action_layout_card -> layoutRes = R.layout.item_card + R.id.action_layout_colored_card -> layoutRes = R.layout.item_card_color + R.id.action_layout_circular -> layoutRes = R.layout.item_grid_circle + R.id.action_layout_image -> layoutRes = R.layout.image + R.id.action_layout_gradient_image -> layoutRes = R.layout.item_image_gradient + } + if (layoutRes != -1) { + item.isChecked = true + setAndSaveLayoutRes(layoutRes) + return true + } + return false + } + + private fun handleGridSizeMenuItem( + item: MenuItem + ): Boolean { + var gridSize = 0 + when (item.itemId) { + R.id.action_grid_size_1 -> gridSize = 1 + R.id.action_grid_size_2 -> gridSize = 2 + R.id.action_grid_size_3 -> gridSize = 3 + R.id.action_grid_size_4 -> gridSize = 4 + R.id.action_grid_size_5 -> gridSize = 5 + R.id.action_grid_size_6 -> gridSize = 6 + R.id.action_grid_size_7 -> gridSize = 7 + R.id.action_grid_size_8 -> gridSize = 8 + } + if (gridSize > 0) { + item.isChecked = true + setAndSaveGridSize(gridSize) + return true + } + return false + } + companion object { @JvmField var TAG: String = SongsFragment::class.java.simpleName diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsViewModel.kt new file mode 100644 index 00000000..3ec068fc --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsViewModel.kt @@ -0,0 +1,31 @@ +package code.name.monkey.retromusic.fragments.songs + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.repository.SongRepository +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.launch + +class SongsViewModel( + private val songRepository: SongRepository +) : ViewModel() { + init { + update() + } + + private val songsData = MutableLiveData>().apply { value = mutableListOf() } + + fun getSongList(): LiveData> { + return songsData + } + + fun update() { + viewModelScope.launch(IO) { + val songs = songRepository.songs() + songsData.postValue(songs) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/SingleColorTarget.kt b/app/src/main/java/code/name/monkey/retromusic/glide/SingleColorTarget.kt new file mode 100644 index 00000000..d4b2db1c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/glide/SingleColorTarget.kt @@ -0,0 +1,39 @@ +package code.name.monkey.retromusic.glide + +import android.graphics.drawable.Drawable +import android.widget.ImageView +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget +import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper +import code.name.monkey.retromusic.util.ColorUtil +import com.bumptech.glide.request.animation.GlideAnimation + + +abstract class SingleColorTarget(view: ImageView) : BitmapPaletteTarget(view) { + + protected val defaultFooterColor: Int + get() = ATHUtil.resolveColor(view.context, R.attr.colorControlNormal) + + abstract fun onColorReady(color: Int) + + override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) { + super.onLoadFailed(e, errorDrawable) + onColorReady(defaultFooterColor) + } + + override fun onResourceReady( + resource: BitmapPaletteWrapper?, + glideAnimation: GlideAnimation? + ) { + super.onResourceReady(resource, glideAnimation) + resource?.let { + onColorReady( + ColorUtil.getColor( + it.palette, + ATHUtil.resolveColor(view.context, R.attr.colorPrimary) + ) + ) + } + } +} \ No newline at end of file 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 5bf16d0f..a3575a6e 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 @@ -35,9 +35,11 @@ interface Repository { fun artistsFlow(): Flow>> fun playlistsFlow(): Flow>> fun genresFlow(): Flow>> - + fun historySong(): LiveData> + fun favorites(): LiveData> suspend fun allAlbums(): List - suspend fun albumById(albumId: Int): Album + suspend fun albumByIdAsync(albumId: Int): Album + fun albumById(albumId: Int): Album suspend fun allSongs(): List suspend fun allArtists(): List suspend fun albumArtists(): List @@ -75,7 +77,7 @@ interface Repository { suspend fun deleteSongsInPlaylist(songs: List) suspend fun removeSongFromPlaylist(songEntity: SongEntity) suspend fun deleteSongsFromPlaylist(playlists: List) - suspend fun favoritePlaylist(): List + suspend fun favoritePlaylist(): PlaylistEntity suspend fun isFavoriteSong(songEntity: SongEntity): List suspend fun addSongToHistory(currentSong: Song) suspend fun songPresentInHistory(currentSong: Song): HistoryEntity? @@ -88,8 +90,7 @@ interface Repository { suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun checkSongExistInPlayCount(songId: Int): List suspend fun playCountSongs(): List - fun historySong(): LiveData> - fun favorites(): LiveData> + suspend fun blackListPaths(): List } class RealRepository( @@ -103,13 +104,13 @@ class RealRepository( private val playlistRepository: PlaylistRepository, private val searchRepository: RealSearchRepository, private val topPlayedRepository: TopPlayedRepository, - private val roomRepository: RoomPlaylistRepository + private val roomRepository: RoomRepository ) : Repository { override suspend fun allAlbums(): List = albumRepository.albums() - override suspend fun albumById(albumId: Int): Album = albumRepository.album(albumId) - + override suspend fun albumByIdAsync(albumId: Int): Album = albumRepository.album(albumId) + override fun albumById(albumId: Int): Album = albumRepository.album(albumId) override suspend fun allArtists(): List = artistRepository.artists() override suspend fun albumArtists(): List = artistRepository.albumArtists() @@ -130,28 +131,24 @@ class RealRepository( override suspend fun allSongs(): List = songRepository.songs() - override suspend fun search(query: String?): MutableList = searchRepository.searchAll(context, query) - override suspend fun getPlaylistSongs(playlist: Playlist): List { - return if (playlist is AbsCustomPlaylist) { + override suspend fun getPlaylistSongs(playlist: Playlist): List = + if (playlist is AbsCustomPlaylist) { playlist.songs() } else { PlaylistSongsLoader.getPlaylistSongList(context, playlist.id) } - } override suspend fun getGenre(genreId: Int): List = genreRepository.songs(genreId) - override suspend fun artistInfo( name: String, lang: String?, cache: String? ): LastFmArtist = lastFMService.artistInfo(name, lang, cache) - override suspend fun albumInfo( artist: String, album: String @@ -193,8 +190,8 @@ class RealRepository( topAlbumsHome(), recentArtistsHome(), recentAlbumsHome(), - favoritePlaylistHome(), - genresHome() + favoritePlaylistHome() + // genresHome() ) for (section in sections) { if (section.arrayList.isNotEmpty()) { @@ -211,11 +208,10 @@ class RealRepository( override suspend fun playlistWithSongs(): List = roomRepository.playlistWithSongs() - override suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List { - return playlistWithSongs.songs.map { + override suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List = + playlistWithSongs.songs.map { it.toSong() } - } override suspend fun insertSongs(songs: List) = roomRepository.insertSongs(songs) @@ -243,7 +239,7 @@ class RealRepository( override suspend fun deleteSongsFromPlaylist(playlists: List) = roomRepository.deleteSongsFromPlaylist(playlists) - override suspend fun favoritePlaylist(): List = + override suspend fun favoritePlaylist(): PlaylistEntity = roomRepository.favoritePlaylist(context.getString(R.string.favorites)) override suspend fun isFavoriteSong(songEntity: SongEntity): List = @@ -280,6 +276,9 @@ class RealRepository( override suspend fun playCountSongs(): List = roomRepository.playCountSongs() + override suspend fun blackListPaths(): List = + roomRepository.blackListPaths() + override fun historySong(): LiveData> = roomRepository.historySongs() @@ -328,7 +327,6 @@ class RealRepository( val songs = favoritePlaylistSongs().map { it.toSong() } - println(songs.size) return Home(songs, FAVOURITES, R.string.favorites) } diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/RoomPlaylistRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/RoomRepository.kt similarity index 67% rename from app/src/main/java/code/name/monkey/retromusic/repository/RoomPlaylistRepository.kt rename to app/src/main/java/code/name/monkey/retromusic/repository/RoomRepository.kt index 4bafb945..a523883d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/RoomPlaylistRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/RoomRepository.kt @@ -6,7 +6,10 @@ import code.name.monkey.retromusic.db.* import code.name.monkey.retromusic.model.Song -interface RoomPlaylistRepository { +interface RoomRepository { + fun historySongs(): LiveData> + fun favoritePlaylistLiveData(favorite: String): LiveData> + fun insertBlacklistPath(blackListStoreEntity: BlackListStoreEntity) suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long suspend fun checkPlaylistExists(playlistName: String): List suspend fun playlists(): List @@ -17,7 +20,7 @@ interface RoomPlaylistRepository { suspend fun renamePlaylistEntity(playlistId: Int, name: String) suspend fun deleteSongsInPlaylist(songs: List) suspend fun deleteSongsFromPlaylist(playlists: List) - suspend fun favoritePlaylist(favorite: String): List + suspend fun favoritePlaylist(favorite: String): PlaylistEntity suspend fun isFavoriteSong(songEntity: SongEntity): List suspend fun removeSongFromPlaylist(songEntity: SongEntity) suspend fun addSongToHistory(currentSong: Song) @@ -29,13 +32,18 @@ interface RoomPlaylistRepository { suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) suspend fun checkSongExistInPlayCount(songId: Int): List suspend fun playCountSongs(): List - fun historySongs(): LiveData> - fun favoritePlaylistLiveData(favorite: String): LiveData> + suspend fun insertBlacklistPath(blackListStoreEntities: List) + suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity) + suspend fun clearBlacklist() + suspend fun insertBlacklistPathAsync(blackListStoreEntity: BlackListStoreEntity) + suspend fun blackListPaths(): List } class RealRoomRepository( - private val playlistDao: PlaylistDao -) : RoomPlaylistRepository { + private val playlistDao: PlaylistDao, + private val blackListStoreDao: BlackListStoreDao, + private val playCountDao: PlayCountDao +) : RoomRepository { @WorkerThread override suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long = playlistDao.createPlaylist(playlistEntity) @@ -51,20 +59,18 @@ class RealRoomRepository( override suspend fun playlistWithSongs(): List = playlistDao.playlistsWithSongs() - @WorkerThread - override suspend fun insertSongs(songs: List) { - /* val tempList = ArrayList(songs) + /* val tempList = ArrayList(songs) val existingSongs = songs.map { playlistDao.checkSongExistsWithPlaylistName(it.playlistCreatorName, it.songId) }.first() println("Existing ${existingSongs.size}") tempList.removeAll(existingSongs)*/ + @WorkerThread + override suspend fun insertSongs(songs: List) = playlistDao.insertSongsToPlaylist(songs) - } - override suspend fun getSongs(playlistEntity: PlaylistEntity): List { - return playlistDao.songsFromPlaylist(playlistEntity.playListId) - } + override suspend fun getSongs(playlistEntity: PlaylistEntity): List = + playlistDao.songsFromPlaylist(playlistEntity.playListId) override suspend fun deletePlaylistEntities(playlistEntities: List) = playlistDao.deletePlaylists(playlistEntities) @@ -75,14 +81,21 @@ class RealRoomRepository( override suspend fun deleteSongsInPlaylist(songs: List) = playlistDao.deleteSongsInPlaylist(songs) - override suspend fun deleteSongsFromPlaylist(playlists: List) { + override suspend fun deleteSongsFromPlaylist(playlists: List) = playlists.forEach { playlistDao.deleteSongsInPlaylist(it.playListId) } + + override suspend fun favoritePlaylist(favorite: String): PlaylistEntity { + val playlist: PlaylistEntity? = playlistDao.isPlaylistExists(favorite).firstOrNull() + return if (playlist != null) { + playlist + } else { + createPlaylist(PlaylistEntity(favorite)) + playlistDao.isPlaylistExists(favorite).first() + } } - override suspend fun favoritePlaylist(favorite: String): List = - playlistDao.isPlaylistExists(favorite) override suspend fun isFavoriteSong(songEntity: SongEntity): List = playlistDao.isSongExistsInPlaylist( @@ -111,25 +124,41 @@ class RealRoomRepository( playlistDao.isPlaylistExists(favorite).first().playListId ) - override suspend fun favoritePlaylistSongs(favorite: String): List { - return if (playlistDao.isPlaylistExists(favorite).isNotEmpty()) + override suspend fun favoritePlaylistSongs(favorite: String): List = + if (playlistDao.isPlaylistExists(favorite).isNotEmpty()) playlistDao.favoritesSongs( playlistDao.isPlaylistExists(favorite).first().playListId ) else emptyList() - } override suspend fun insertSongInPlayCount(playCountEntity: PlayCountEntity) = - playlistDao.insertSongInPlayCount(playCountEntity) + playCountDao.insertSongInPlayCount(playCountEntity) override suspend fun updateSongInPlayCount(playCountEntity: PlayCountEntity) = - playlistDao.updateSongInPlayCount(playCountEntity) + playCountDao.updateSongInPlayCount(playCountEntity) override suspend fun deleteSongInPlayCount(playCountEntity: PlayCountEntity) = - playlistDao.deleteSongInPlayCount(playCountEntity) + playCountDao.deleteSongInPlayCount(playCountEntity) override suspend fun checkSongExistInPlayCount(songId: Int): List = - playlistDao.checkSongExistInPlayCount(songId) + playCountDao.checkSongExistInPlayCount(songId) override suspend fun playCountSongs(): List = - playlistDao.playCountSongs() + playCountDao.playCountSongs() + + override fun insertBlacklistPath(blackListStoreEntity: BlackListStoreEntity) = + blackListStoreDao.insertBlacklistPath(blackListStoreEntity) + + override suspend fun insertBlacklistPath(blackListStoreEntities: List) = + blackListStoreDao.insertBlacklistPath(blackListStoreEntities) + + override suspend fun insertBlacklistPathAsync(blackListStoreEntity: BlackListStoreEntity) = + blackListStoreDao.insertBlacklistPath(blackListStoreEntity) + + override suspend fun blackListPaths(): List = + blackListStoreDao.blackListPaths() + + override suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity) = + blackListStoreDao.deleteBlacklistPath(blackListStoreEntity) + + override suspend fun clearBlacklist() = blackListStoreDao.clearBlacklist() } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/ColorUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/ColorUtil.java new file mode 100644 index 00000000..e4133392 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/util/ColorUtil.java @@ -0,0 +1,58 @@ +package code.name.monkey.retromusic.util; + +import android.graphics.Bitmap; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.palette.graphics.Palette; + +import java.util.Collections; +import java.util.Comparator; + +public class ColorUtil { + + @Nullable + public static Palette generatePalette(Bitmap bitmap) { + if (bitmap == null) return null; + return Palette.from(bitmap).generate(); + } + + @ColorInt + public static int getColor(@Nullable Palette palette, int fallback) { + if (palette != null) { + if (palette.getVibrantSwatch() != null) { + return palette.getVibrantSwatch().getRgb(); + } else if (palette.getMutedSwatch() != null) { + return palette.getMutedSwatch().getRgb(); + } else if (palette.getDarkVibrantSwatch() != null) { + return palette.getDarkVibrantSwatch().getRgb(); + } else if (palette.getDarkMutedSwatch() != null) { + return palette.getDarkMutedSwatch().getRgb(); + } else if (palette.getLightVibrantSwatch() != null) { + return palette.getLightVibrantSwatch().getRgb(); + } else if (palette.getLightMutedSwatch() != null) { + return palette.getLightMutedSwatch().getRgb(); + } else if (!palette.getSwatches().isEmpty()) { + return Collections.max(palette.getSwatches(), SwatchComparator.getInstance()).getRgb(); + } + } + return fallback; + } + + private static class SwatchComparator implements Comparator { + private static SwatchComparator sInstance; + + static SwatchComparator getInstance() { + if (sInstance == null) { + sInstance = new SwatchComparator(); + } + return sInstance; + } + + @Override + public int compare(Palette.Swatch lhs, Palette.Swatch rhs) { + return lhs.getPopulation() - rhs.getPopulation(); + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index 1458c415..83b9ea93 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -16,10 +16,82 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:context=".DrawerActivity"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + app:showAsAction="never" /> \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 181b62cc..a25e163f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -org.gradle.jvmargs=-Xmx2048M +org.gradle.jvmargs=-Xmx4608m org.gradle.daemon=true org.gradle.parallel=true jvmArgs='-Xmx2048m'