Merge branch 'room-playlist' of https://github.com/h4h13/RetroMusicPlayer into room-playlist

This commit is contained in:
Hemanth S 2020-09-02 20:55:03 +05:30
commit 54b037c0c1
32 changed files with 1032 additions and 158 deletions

View file

@ -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

View file

@ -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)

View file

@ -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() {

View file

@ -73,7 +73,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
setContentView(createContentView())
chooseFragmentForTheme()
setupSlidingUpPanel()
addMusicServiceEventListener(libraryViewModel)
setupBottomSheet()

View file

@ -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()

View file

@ -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) {

View file

@ -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

View file

@ -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)

View file

@ -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>
}

View file

@ -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>
}

View file

@ -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()
}

View file

@ -10,4 +10,6 @@ import androidx.room.RoomDatabase
)
abstract class RetroDatabase : RoomDatabase() {
abstract fun playlistDao(): PlaylistDao
abstract fun blackListStore(): BlackListStoreDao
abstract fun playCountDao(): PlayCountDao
}

View file

@ -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])

View file

@ -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"

View file

@ -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()
}
}

View file

@ -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) }
}
}

View file

@ -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() {

View file

@ -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) {

View file

@ -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() {

View file

@ -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 {

View file

@ -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,

View file

@ -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 {

View file

@ -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,

View file

@ -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) }
}

View file

@ -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

View file

@ -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)
}
}
}

View file

@ -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)
)
)
}
}
}

View file

@ -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)
}

View file

@ -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()
}

View file

@ -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();
}
}
}

View file

@ -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>

View file

@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx2048M
org.gradle.jvmargs=-Xmx4608m
org.gradle.daemon=true
org.gradle.parallel=true
jvmArgs='-Xmx2048m'