Code refactor

This commit is contained in:
Hemanth S 2020-08-31 18:00:07 +05:30
parent c379342f6a
commit 6881e9a4c1
32 changed files with 411 additions and 173 deletions

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

@ -34,7 +34,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
}
private val repository by inject<Repository>()
private var blockRequestPermissions = false
override fun createContentView(): View {
@ -133,7 +132,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
if (id >= 0) {
lifecycleScope.launch(Dispatchers.Main) {
val position = intent.getIntExtra("position", 0)
openQueue(repository.albumById(id).songs!!, position, true)
openQueue(repository.albumByIdAsync(id).songs!!, position, true)
handled = true
}
}

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,11 +181,9 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
saveFab = findViewById(R.id.saveTags)
getIntentExtras()
lifecycleScope.launchWhenCreated {
songPaths = getSongPaths()
if (songPaths!!.isEmpty()) {
finish()
}
songPaths = getSongPaths()
if (songPaths!!.isEmpty()) {
finish()
}
setUpViews()
}
@ -258,7 +255,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
}
}
protected abstract suspend fun getSongPaths(): List<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

@ -5,9 +5,11 @@ import androidx.room.RoomDatabase
@Database(
entities = [PlaylistEntity::class, SongEntity::class, HistoryEntity::class, PlayCountEntity::class, BlackListStoreEntity::class],
version = 18,
version = 19,
exportSchema = false
)
abstract class RetroDatabase : RoomDatabase() {
abstract fun playlistDao(): PlaylistDao
abstract fun blackListStore(): BlackListStoreDao
abstract fun playCountDao(): PlayCountDao
}

View file

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

@ -35,7 +35,6 @@ import code.name.monkey.retromusic.R
import com.google.android.material.button.MaterialButton
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
fun Int.ripAlpha(): Int {
@ -145,10 +144,6 @@ fun TextInputLayout.accentColor() {
isHintAnimationEnabled = true
}
fun TextInputEditText.accentColor() {
}
fun AppCompatImageView.accentColor(): Int {
return ThemeStore.accentColor(context)
}

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

@ -5,9 +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.toPlayCount
import code.name.monkey.retromusic.fragments.ReloadType.*
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.repository.RealRepository
@ -51,7 +49,7 @@ class LibraryViewModel(
artists.value = loadArtists.await()
playlists.value = loadPlaylists.await()
roomPlaylists.value = loadPlaylistsWithSongs.await()
genres.value = loadGenres.await()
//genres.value = loadGenres.await()
}
private val loadHome: Deferred<List<Home>>
@ -86,7 +84,6 @@ class LibraryViewModel(
fun forceReload(reloadType: ReloadType) = viewModelScope.launch {
println(reloadType)
when (reloadType) {
Songs -> songs.value = loadSongs.await()
Albums -> albums.value = loadAlbums.await()
@ -121,22 +118,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

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

@ -35,7 +35,6 @@ import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.*
import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
@ -75,7 +74,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
R.id.action_add_to_playlist -> {
lifecycleScope.launch(IO) {
val playlists = get<RealRepository>().roomPlaylists()
withContext(Dispatchers.Main) {
withContext(Main) {
AddToRetroPlaylist.create(playlists, song)
.show(childFragmentManager, "ADD_PLAYLIST")
}
@ -87,7 +86,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
}
@ -163,15 +162,17 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
protected open fun toggleFavorite(song: Song) {
lifecycleScope.launch(IO) {
val playlist: PlaylistEntity = repository.favoritePlaylist().first()
val songEntity = song.toSongEntity(playlist.playListId)
val isFavorite = repository.isFavoriteSong(songEntity).isNotEmpty()
if (isFavorite) {
repository.removeSongFromPlaylist(songEntity)
} else {
repository.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
libraryViewModel.forceReload(Playlists)
val playlist: PlaylistEntity? = repository.favoritePlaylist()
if (playlist != null) {
val songEntity = song.toSongEntity(playlist.playListId)
val isFavorite = repository.isFavoriteSong(songEntity).isNotEmpty()
if (isFavorite) {
repository.removeSongFromPlaylist(songEntity)
} else {
repository.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
}
}
libraryViewModel.forceReload(Playlists)
requireContext().sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED))
}
}
@ -198,13 +199,18 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
fun updateIsFavorite() {
lifecycleScope.launch(IO) {
val playlist: PlaylistEntity = repository.favoritePlaylist().first()
val playlist: PlaylistEntity = repository.favoritePlaylist()
val song = MusicPlayerRemote.currentSong.toSongEntity(playlist.playListId)
val isFavorite = repository.isFavoriteSong(song).isNotEmpty()
withContext(Dispatchers.Main) {
val icon = if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border
withContext(Main) {
val icon = if (isFavorite) R.drawable.ic_favorite
else R.drawable.ic_favorite_border
val drawable =
RetroUtil.getTintedVectorDrawable(requireContext(), icon, toolbarIconColor())
RetroUtil.getTintedVectorDrawable(
requireContext(),
icon,
toolbarIconColor()
)
if (playerToolbar() != null) {
playerToolbar()?.menu?.findItem(R.id.action_toggle_favorite)
?.setIcon(drawable)?.title =

View file

@ -16,7 +16,6 @@ package code.name.monkey.retromusic.fragments.home
import android.app.ActivityOptions
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.View
import androidx.core.os.bundleOf
import androidx.lifecycle.Observer
@ -50,13 +49,6 @@ class HomeFragment :
private val repository by inject<Repository>()
private val libraryViewModel: LibraryViewModel by sharedViewModel()
private val displayMetrics: DisplayMetrics
get() {
val display = mainActivity.windowManager.defaultDisplay
val metrics = DisplayMetrics()
display.getMetrics(metrics)
return metrics
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

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

@ -15,7 +15,6 @@ import code.name.monkey.retromusic.util.PreferenceUtil
class SongsFragment :
AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLayoutManager>(),
MainActivityFragmentCallbacks {
override fun handleBackPress(): Boolean {
return false
}

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