Merge branch 'room-playlist' of https://github.com/h4h13/RetroMusicPlayer into room-playlist
This commit is contained in:
commit
54b037c0c1
32 changed files with 1032 additions and 158 deletions
|
@ -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
|
||||
|
|
|
@ -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<PlaylistDao>().insertBlacklistPath(BlackListStoreEntity(it))
|
||||
get<BlackListStoreDao>().insertBlacklistPath(BlackListStoreEntity(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,9 +49,17 @@ private val roomModule = module {
|
|||
get<RetroDatabase>().playlistDao()
|
||||
}
|
||||
|
||||
factory {
|
||||
get<RetroDatabase>().blackListStore()
|
||||
}
|
||||
|
||||
factory {
|
||||
get<RetroDatabase>().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)
|
|
@ -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<MusicServiceEventListener>()
|
||||
|
||||
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() {
|
||||
|
|
|
@ -73,7 +73,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
setContentView(createContentView())
|
||||
chooseFragmentForTheme()
|
||||
setupSlidingUpPanel()
|
||||
addMusicServiceEventListener(libraryViewModel)
|
||||
|
||||
setupBottomSheet()
|
||||
|
||||
|
|
|
@ -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,12 +181,10 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
saveFab = findViewById(R.id.saveTags)
|
||||
getIntentExtras()
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
songPaths = getSongPaths()
|
||||
if (songPaths!!.isEmpty()) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
setUpViews()
|
||||
}
|
||||
|
||||
|
@ -258,7 +255,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
protected abstract suspend fun getSongPaths(): List<String>
|
||||
protected abstract fun getSongPaths(): List<String>
|
||||
|
||||
protected fun searchWebFor(vararg keys: String) {
|
||||
val stringBuilder = StringBuilder()
|
||||
|
|
|
@ -167,7 +167,7 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getSongPaths(): List<String> {
|
||||
override fun getSongPaths(): List<String> {
|
||||
val songs = repository.albumById(id).songs
|
||||
val paths = ArrayList<String>(songs!!.size)
|
||||
for (song in songs) {
|
||||
|
|
|
@ -88,7 +88,7 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
|||
writeValuesToFiles(fieldKeyValueMap, null)
|
||||
}
|
||||
|
||||
override suspend fun getSongPaths(): List<String> {
|
||||
override fun getSongPaths(): List<String> {
|
||||
val paths = ArrayList<String>(1)
|
||||
paths.add(songRepository.song(id).data)
|
||||
return paths
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<BlackListStoreEntity>)
|
||||
|
||||
@Delete
|
||||
suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity)
|
||||
|
||||
@Query("DELETE FROM BlackListStoreEntity")
|
||||
suspend fun clearBlacklist()
|
||||
|
||||
@Query("SELECT * FROM BlackListStoreEntity")
|
||||
fun blackListPaths(): List<BlackListStoreEntity>
|
||||
}
|
|
@ -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<PlayCountEntity>
|
||||
|
||||
@Query("SELECT * FROM PlayCountEntity ORDER BY play_count DESC")
|
||||
fun playCountSongs(): List<PlayCountEntity>
|
||||
}
|
|
@ -63,30 +63,6 @@ interface PlaylistDao {
|
|||
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId")
|
||||
fun favoritesSongs(playlistId: Int): List<SongEntity>
|
||||
|
||||
@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<PlayCountEntity>
|
||||
|
||||
@Query("SELECT * FROM PlayCountEntity ORDER BY play_count DESC")
|
||||
fun playCountSongs(): List<PlayCountEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertBlacklistPath(blackListStoreEntity: BlackListStoreEntity)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertBlacklistPath(blackListStoreEntities: List<BlackListStoreEntity>)
|
||||
|
||||
@Delete
|
||||
suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity)
|
||||
|
||||
@Query("DELETE FROM BlackListStoreEntity")
|
||||
suspend fun clearBlacklist()
|
||||
}
|
|
@ -10,4 +10,6 @@ import androidx.room.RoomDatabase
|
|||
)
|
||||
abstract class RetroDatabase : RoomDatabase() {
|
||||
abstract fun playlistDao(): PlaylistDao
|
||||
abstract fun blackListStore(): BlackListStoreDao
|
||||
abstract fun playCountDao(): PlayCountDao
|
||||
}
|
|
@ -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])
|
||||
|
|
|
@ -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<RealRepository>()
|
||||
private val libraryViewModel by sharedViewModel<LibraryViewModel>()
|
||||
|
||||
companion object {
|
||||
fun create(song: Song): CreateRetroPlaylist {
|
||||
val list = mutableListOf<Song>()
|
||||
list.add(song)
|
||||
return create(list)
|
||||
}
|
||||
|
||||
fun create(songs: List<Song>): 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<List<Song>>(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"
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<LastFmAlbum>()
|
||||
private val _moreAlbums = MutableLiveData<List<Album>>()
|
||||
|
||||
fun getAlbum(): LiveData<Album> = _album
|
||||
fun getAlbum(): LiveData<Album> = liveData(IO) {
|
||||
val album = realRepository.albumByIdAsync(albumId)
|
||||
emit(album)
|
||||
}
|
||||
|
||||
fun getArtist(): LiveData<Artist> = _artist
|
||||
fun getAlbumInfo(): LiveData<LastFmAlbum> = _lastFmAlbum
|
||||
fun getMoreAlbums(): LiveData<List<Album>> = _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<Album?>
|
||||
get() = viewModelScope.async(Dispatchers.IO) {
|
||||
realRepository.albumById(albumId)
|
||||
get() = viewModelScope.async(IO) {
|
||||
realRepository.albumByIdAsync(albumId)
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
|
|
|
@ -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<AlbumAdapter, GridLayoutManager>(),
|
||||
AlbumClickListener {
|
||||
|
@ -103,6 +107,181 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
val gridSizeItem: MenuItem = menu.findItem(R.id.action_grid_size)
|
||||
if (RetroUtil.isLandscape()) {
|
||||
gridSizeItem.setTitle(R.string.action_grid_size_land)
|
||||
}
|
||||
setUpGridSizeMenu(gridSizeItem.subMenu)
|
||||
val layoutItem = menu.findItem(R.id.action_layout_type)
|
||||
setupLayoutMenu(layoutItem.subMenu)
|
||||
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
private fun setUpSortOrderMenu(
|
||||
sortOrderMenu: SubMenu
|
||||
) {
|
||||
val currentSortOrder: String? = getSortOrder()
|
||||
sortOrderMenu.clear()
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_album_sort_order_asc,
|
||||
0,
|
||||
R.string.sort_order_a_z
|
||||
).isChecked =
|
||||
currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_A_Z)
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_album_sort_order_desc,
|
||||
1,
|
||||
R.string.sort_order_z_a
|
||||
).isChecked =
|
||||
currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_Z_A)
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_album_sort_order_artist,
|
||||
2,
|
||||
R.string.sort_order_artist
|
||||
).isChecked =
|
||||
currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_ARTIST)
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_album_sort_order_year,
|
||||
3,
|
||||
R.string.sort_order_year
|
||||
).isChecked =
|
||||
currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_YEAR)
|
||||
|
||||
sortOrderMenu.setGroupCheckable(0, true, true)
|
||||
}
|
||||
|
||||
private fun setupLayoutMenu(
|
||||
subMenu: SubMenu
|
||||
) {
|
||||
when (itemLayoutRes()) {
|
||||
R.layout.item_card -> 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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<ArtistAdapter, GridLayoutManager>(),
|
||||
ArtistClickListener {
|
||||
|
@ -97,6 +100,161 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
|
|||
val controller = findActivityNavController(R.id.fragment_container)
|
||||
controller.navigate(R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to artistId))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
val gridSizeItem: MenuItem = menu.findItem(R.id.action_grid_size)
|
||||
if (RetroUtil.isLandscape()) {
|
||||
gridSizeItem.setTitle(R.string.action_grid_size_land)
|
||||
}
|
||||
setUpGridSizeMenu(gridSizeItem.subMenu)
|
||||
val layoutItem = menu.findItem(R.id.action_layout_type)
|
||||
setupLayoutMenu(layoutItem.subMenu)
|
||||
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
private fun setUpSortOrderMenu(
|
||||
sortOrderMenu: SubMenu
|
||||
) {
|
||||
val currentSortOrder: String? = getSortOrder()
|
||||
sortOrderMenu.clear()
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_artist_sort_order_asc,
|
||||
0,
|
||||
R.string.sort_order_a_z
|
||||
).isChecked = currentSortOrder.equals(ArtistSortOrder.ARTIST_A_Z)
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_artist_sort_order_desc,
|
||||
1,
|
||||
R.string.sort_order_z_a
|
||||
).isChecked = currentSortOrder.equals(ArtistSortOrder.ARTIST_Z_A)
|
||||
sortOrderMenu.setGroupCheckable(0, true, true)
|
||||
}
|
||||
|
||||
private fun setupLayoutMenu(
|
||||
subMenu: SubMenu
|
||||
) {
|
||||
when (itemLayoutRes()) {
|
||||
R.layout.item_card -> 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 {
|
||||
|
|
|
@ -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 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)
|
||||
}
|
||||
}
|
||||
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,
|
||||
|
|
|
@ -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<Song> = realRepository.playlistSongs(playlist)
|
||||
val songs: List<Song> = playlist.songs.toSongs()
|
||||
withContext(Main) { _playListSongs.postValue(songs) }
|
||||
}
|
||||
|
||||
|
|
|
@ -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<SongAdapter, GridLayoutManager>() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
libraryViewModel.songsLiveData.observe(viewLifecycleOwner, Observer {
|
||||
|
@ -34,7 +38,7 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLay
|
|||
return SongAdapter(
|
||||
requireActivity(),
|
||||
dataSet,
|
||||
R.layout.item_list,
|
||||
itemLayoutRes(),
|
||||
null
|
||||
)
|
||||
}
|
||||
|
@ -80,6 +84,213 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLay
|
|||
libraryViewModel.forceReload(ReloadType.Songs)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
val gridSizeItem: MenuItem = menu.findItem(R.id.action_grid_size)
|
||||
if (RetroUtil.isLandscape()) {
|
||||
gridSizeItem.setTitle(R.string.action_grid_size_land)
|
||||
}
|
||||
setUpGridSizeMenu(gridSizeItem.subMenu)
|
||||
val layoutItem = menu.findItem(R.id.action_layout_type)
|
||||
setupLayoutMenu(layoutItem.subMenu)
|
||||
setUpSortOrderMenu(menu.findItem(R.id.action_sort_order).subMenu)
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
private fun setUpSortOrderMenu(
|
||||
sortOrderMenu: SubMenu
|
||||
) {
|
||||
val currentSortOrder: String? = getSortOrder()
|
||||
sortOrderMenu.clear()
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_asc,
|
||||
0,
|
||||
R.string.sort_order_a_z
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_A_Z
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_desc,
|
||||
1,
|
||||
R.string.sort_order_z_a
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_Z_A
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_artist,
|
||||
2,
|
||||
R.string.sort_order_artist
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_ARTIST
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_album,
|
||||
3,
|
||||
R.string.sort_order_album
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_ALBUM
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_year,
|
||||
4,
|
||||
R.string.sort_order_year
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_YEAR
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_date,
|
||||
5,
|
||||
R.string.sort_order_date
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_DATE
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_date_modified,
|
||||
6,
|
||||
R.string.sort_order_date_modified
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_DATE_MODIFIED
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_composer,
|
||||
7,
|
||||
R.string.sort_order_composer
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.COMPOSER
|
||||
|
||||
sortOrderMenu.setGroupCheckable(0, true, true)
|
||||
}
|
||||
|
||||
private fun setupLayoutMenu(
|
||||
subMenu: SubMenu
|
||||
) {
|
||||
when (itemLayoutRes()) {
|
||||
R.layout.item_card -> 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
|
||||
|
|
|
@ -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<List<Song>>().apply { value = mutableListOf() }
|
||||
|
||||
fun getSongList(): LiveData<List<Song>> {
|
||||
return songsData
|
||||
}
|
||||
|
||||
fun update() {
|
||||
viewModelScope.launch(IO) {
|
||||
val songs = songRepository.songs()
|
||||
songsData.postValue(songs)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
super.onResourceReady(resource, glideAnimation)
|
||||
resource?.let {
|
||||
onColorReady(
|
||||
ColorUtil.getColor(
|
||||
it.palette,
|
||||
ATHUtil.resolveColor(view.context, R.attr.colorPrimary)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,9 +35,11 @@ interface Repository {
|
|||
fun artistsFlow(): Flow<Result<List<Artist>>>
|
||||
fun playlistsFlow(): Flow<Result<List<Playlist>>>
|
||||
fun genresFlow(): Flow<Result<List<Genre>>>
|
||||
|
||||
fun historySong(): LiveData<List<HistoryEntity>>
|
||||
fun favorites(): LiveData<List<SongEntity>>
|
||||
suspend fun allAlbums(): List<Album>
|
||||
suspend fun albumById(albumId: Int): Album
|
||||
suspend fun albumByIdAsync(albumId: Int): Album
|
||||
fun albumById(albumId: Int): Album
|
||||
suspend fun allSongs(): List<Song>
|
||||
suspend fun allArtists(): List<Artist>
|
||||
suspend fun albumArtists(): List<Artist>
|
||||
|
@ -75,7 +77,7 @@ interface Repository {
|
|||
suspend fun deleteSongsInPlaylist(songs: List<SongEntity>)
|
||||
suspend fun removeSongFromPlaylist(songEntity: SongEntity)
|
||||
suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>)
|
||||
suspend fun favoritePlaylist(): List<PlaylistEntity>
|
||||
suspend fun favoritePlaylist(): PlaylistEntity
|
||||
suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity>
|
||||
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<PlayCountEntity>
|
||||
suspend fun playCountSongs(): List<PlayCountEntity>
|
||||
fun historySong(): LiveData<List<HistoryEntity>>
|
||||
fun favorites(): LiveData<List<SongEntity>>
|
||||
suspend fun blackListPaths(): List<BlackListStoreEntity>
|
||||
}
|
||||
|
||||
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<Album> = 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<Artist> = artistRepository.artists()
|
||||
|
||||
override suspend fun albumArtists(): List<Artist> = artistRepository.albumArtists()
|
||||
|
@ -130,28 +131,24 @@ class RealRepository(
|
|||
|
||||
override suspend fun allSongs(): List<Song> = songRepository.songs()
|
||||
|
||||
|
||||
override suspend fun search(query: String?): MutableList<Any> =
|
||||
searchRepository.searchAll(context, query)
|
||||
|
||||
override suspend fun getPlaylistSongs(playlist: Playlist): List<Song> {
|
||||
return if (playlist is AbsCustomPlaylist) {
|
||||
override suspend fun getPlaylistSongs(playlist: Playlist): List<Song> =
|
||||
if (playlist is AbsCustomPlaylist) {
|
||||
playlist.songs()
|
||||
} else {
|
||||
PlaylistSongsLoader.getPlaylistSongList(context, playlist.id)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getGenre(genreId: Int): List<Song> = 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<PlaylistWithSongs> =
|
||||
roomRepository.playlistWithSongs()
|
||||
|
||||
override suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List<Song> {
|
||||
return playlistWithSongs.songs.map {
|
||||
override suspend fun playlistSongs(playlistWithSongs: PlaylistWithSongs): List<Song> =
|
||||
playlistWithSongs.songs.map {
|
||||
it.toSong()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insertSongs(songs: List<SongEntity>) =
|
||||
roomRepository.insertSongs(songs)
|
||||
|
@ -243,7 +239,7 @@ class RealRepository(
|
|||
override suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) =
|
||||
roomRepository.deleteSongsFromPlaylist(playlists)
|
||||
|
||||
override suspend fun favoritePlaylist(): List<PlaylistEntity> =
|
||||
override suspend fun favoritePlaylist(): PlaylistEntity =
|
||||
roomRepository.favoritePlaylist(context.getString(R.string.favorites))
|
||||
|
||||
override suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity> =
|
||||
|
@ -280,6 +276,9 @@ class RealRepository(
|
|||
override suspend fun playCountSongs(): List<PlayCountEntity> =
|
||||
roomRepository.playCountSongs()
|
||||
|
||||
override suspend fun blackListPaths(): List<BlackListStoreEntity> =
|
||||
roomRepository.blackListPaths()
|
||||
|
||||
override fun historySong(): LiveData<List<HistoryEntity>> =
|
||||
roomRepository.historySongs()
|
||||
|
||||
|
@ -328,7 +327,6 @@ class RealRepository(
|
|||
val songs = favoritePlaylistSongs().map {
|
||||
it.toSong()
|
||||
}
|
||||
println(songs.size)
|
||||
return Home(songs, FAVOURITES, R.string.favorites)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,10 @@ import code.name.monkey.retromusic.db.*
|
|||
import code.name.monkey.retromusic.model.Song
|
||||
|
||||
|
||||
interface RoomPlaylistRepository {
|
||||
interface RoomRepository {
|
||||
fun historySongs(): LiveData<List<HistoryEntity>>
|
||||
fun favoritePlaylistLiveData(favorite: String): LiveData<List<SongEntity>>
|
||||
fun insertBlacklistPath(blackListStoreEntity: BlackListStoreEntity)
|
||||
suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long
|
||||
suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity>
|
||||
suspend fun playlists(): List<PlaylistEntity>
|
||||
|
@ -17,7 +20,7 @@ interface RoomPlaylistRepository {
|
|||
suspend fun renamePlaylistEntity(playlistId: Int, name: String)
|
||||
suspend fun deleteSongsInPlaylist(songs: List<SongEntity>)
|
||||
suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>)
|
||||
suspend fun favoritePlaylist(favorite: String): List<PlaylistEntity>
|
||||
suspend fun favoritePlaylist(favorite: String): PlaylistEntity
|
||||
suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity>
|
||||
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<PlayCountEntity>
|
||||
suspend fun playCountSongs(): List<PlayCountEntity>
|
||||
fun historySongs(): LiveData<List<HistoryEntity>>
|
||||
fun favoritePlaylistLiveData(favorite: String): LiveData<List<SongEntity>>
|
||||
suspend fun insertBlacklistPath(blackListStoreEntities: List<BlackListStoreEntity>)
|
||||
suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity)
|
||||
suspend fun clearBlacklist()
|
||||
suspend fun insertBlacklistPathAsync(blackListStoreEntity: BlackListStoreEntity)
|
||||
suspend fun blackListPaths(): List<BlackListStoreEntity>
|
||||
}
|
||||
|
||||
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<PlaylistWithSongs> =
|
||||
playlistDao.playlistsWithSongs()
|
||||
|
||||
@WorkerThread
|
||||
override suspend fun insertSongs(songs: List<SongEntity>) {
|
||||
/* val tempList = ArrayList<SongEntity>(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<SongEntity>) =
|
||||
playlistDao.insertSongsToPlaylist(songs)
|
||||
}
|
||||
|
||||
override suspend fun getSongs(playlistEntity: PlaylistEntity): List<SongEntity> {
|
||||
return playlistDao.songsFromPlaylist(playlistEntity.playListId)
|
||||
}
|
||||
override suspend fun getSongs(playlistEntity: PlaylistEntity): List<SongEntity> =
|
||||
playlistDao.songsFromPlaylist(playlistEntity.playListId)
|
||||
|
||||
override suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) =
|
||||
playlistDao.deletePlaylists(playlistEntities)
|
||||
|
@ -75,14 +81,21 @@ class RealRoomRepository(
|
|||
override suspend fun deleteSongsInPlaylist(songs: List<SongEntity>) =
|
||||
playlistDao.deleteSongsInPlaylist(songs)
|
||||
|
||||
override suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) {
|
||||
override suspend fun deleteSongsFromPlaylist(playlists: List<PlaylistEntity>) =
|
||||
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<PlaylistEntity> =
|
||||
playlistDao.isPlaylistExists(favorite)
|
||||
|
||||
override suspend fun isFavoriteSong(songEntity: SongEntity): List<SongEntity> =
|
||||
playlistDao.isSongExistsInPlaylist(
|
||||
|
@ -111,25 +124,41 @@ class RealRoomRepository(
|
|||
playlistDao.isPlaylistExists(favorite).first().playListId
|
||||
)
|
||||
|
||||
override suspend fun favoritePlaylistSongs(favorite: String): List<SongEntity> {
|
||||
return if (playlistDao.isPlaylistExists(favorite).isNotEmpty())
|
||||
override suspend fun favoritePlaylistSongs(favorite: String): List<SongEntity> =
|
||||
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<PlayCountEntity> =
|
||||
playlistDao.checkSongExistInPlayCount(songId)
|
||||
playCountDao.checkSongExistInPlayCount(songId)
|
||||
|
||||
override suspend fun playCountSongs(): List<PlayCountEntity> =
|
||||
playlistDao.playCountSongs()
|
||||
playCountDao.playCountSongs()
|
||||
|
||||
override fun insertBlacklistPath(blackListStoreEntity: BlackListStoreEntity) =
|
||||
blackListStoreDao.insertBlacklistPath(blackListStoreEntity)
|
||||
|
||||
override suspend fun insertBlacklistPath(blackListStoreEntities: List<BlackListStoreEntity>) =
|
||||
blackListStoreDao.insertBlacklistPath(blackListStoreEntities)
|
||||
|
||||
override suspend fun insertBlacklistPathAsync(blackListStoreEntity: BlackListStoreEntity) =
|
||||
blackListStoreDao.insertBlacklistPath(blackListStoreEntity)
|
||||
|
||||
override suspend fun blackListPaths(): List<BlackListStoreEntity> =
|
||||
blackListStoreDao.blackListPaths()
|
||||
|
||||
override suspend fun deleteBlacklistPath(blackListStoreEntity: BlackListStoreEntity) =
|
||||
blackListStoreDao.deleteBlacklistPath(blackListStoreEntity)
|
||||
|
||||
override suspend fun clearBlacklist() = blackListStoreDao.clearBlacklist()
|
||||
}
|
|
@ -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<Palette.Swatch> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -16,10 +16,82 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".DrawerActivity">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_grid_size"
|
||||
android:icon="@drawable/ic_grid_size"
|
||||
android:title="@string/action_grid_size"
|
||||
app:showAsAction="never">
|
||||
<menu>
|
||||
<group
|
||||
android:id="@+id/group_grid_size"
|
||||
android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/action_grid_size_1"
|
||||
android:title="@string/grid_size_1" />
|
||||
<item
|
||||
android:id="@+id/action_grid_size_2"
|
||||
android:title="@string/grid_size_2" />
|
||||
<item
|
||||
android:id="@+id/action_grid_size_3"
|
||||
android:title="@string/grid_size_3" />
|
||||
<item
|
||||
android:id="@+id/action_grid_size_4"
|
||||
android:title="@string/grid_size_4" />
|
||||
<item
|
||||
android:id="@+id/action_grid_size_5"
|
||||
android:title="@string/grid_size_5" />
|
||||
<item
|
||||
android:id="@+id/action_grid_size_6"
|
||||
android:title="@string/grid_size_6" />
|
||||
<item
|
||||
android:id="@+id/action_grid_size_7"
|
||||
android:title="@string/grid_size_7" />
|
||||
<item
|
||||
android:id="@+id/action_grid_size_8"
|
||||
android:title="@string/grid_size_8" />
|
||||
</group>
|
||||
</menu>
|
||||
</item>
|
||||
<item
|
||||
android:id="@+id/action_layout_type"
|
||||
android:icon="@drawable/ic_dashboard"
|
||||
android:title="@string/grid_style_label"
|
||||
app:showAsAction="never">
|
||||
<menu>
|
||||
<group
|
||||
android:id="@+id/group_layout_type"
|
||||
android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/action_layout_normal"
|
||||
android:title="@string/normal" />
|
||||
<item
|
||||
android:id="@+id/action_layout_card"
|
||||
android:title="@string/card" />
|
||||
<item
|
||||
android:id="@+id/action_layout_colored_card"
|
||||
android:title="@string/card_color_style" />
|
||||
<item
|
||||
android:id="@+id/action_layout_circular"
|
||||
android:title="@string/circular" />
|
||||
<item
|
||||
android:id="@+id/action_layout_image"
|
||||
android:title="@string/image" />
|
||||
<item
|
||||
android:id="@+id/action_layout_gradient_image"
|
||||
android:title="@string/image_gradient" />
|
||||
</group>
|
||||
</menu>
|
||||
</item>
|
||||
<item
|
||||
android:id="@+id/action_sort_order"
|
||||
android:icon="@drawable/ic_sort"
|
||||
android:title="@string/action_sort_order"
|
||||
app:showAsAction="never">
|
||||
<menu></menu>
|
||||
</item>
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:icon="@drawable/ic_settings"
|
||||
android:title="@string/action_settings"
|
||||
app:showAsAction="ifRoom" />
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -1,4 +1,4 @@
|
|||
org.gradle.jvmargs=-Xmx2048M
|
||||
org.gradle.jvmargs=-Xmx4608m
|
||||
org.gradle.daemon=true
|
||||
org.gradle.parallel=true
|
||||
jvmArgs='-Xmx2048m'
|
||||
|
|
Loading…
Reference in a new issue