Add Room for playlist

Added DAO Queries and Able insert songs to playlist
This commit is contained in:
Hemanth S 2020-08-20 12:19:08 +05:30
parent 6ace96708b
commit b5e07a31d8
16 changed files with 140 additions and 66 deletions

View file

@ -1,6 +1,8 @@
package code.name.monkey.retromusic
import code.name.monkey.retromusic.db.PlaylistDatabase
import code.name.monkey.retromusic.db.RealRoomPlaylistRepository
import code.name.monkey.retromusic.db.RoomPlaylistRepository
import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.albums.AlbumDetailsViewModel
import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel
@ -16,9 +18,15 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.bind
import org.koin.dsl.module
private val mainModule = module {
single {
androidContext().contentResolver
}
}
private val dataModule = module {
single {
RealRepository(get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
RealRepository(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
} bind Repository::class
single {
@ -64,12 +72,12 @@ private val dataModule = module {
}
single {
androidContext().contentResolver
PlaylistDatabase.getDatabase(get()).playlistDao()
}
single {
PlaylistDatabase.getDatabase(get())
}
RealRoomPlaylistRepository(get())
} bind RoomPlaylistRepository::class
}
private val viewModules = module {
@ -111,4 +119,4 @@ private val viewModules = module {
}
}
val appModules = listOf(dataModule, viewModules, networkModule)
val appModules = listOf(mainModule, dataModule, viewModules, networkModule)

View file

@ -10,7 +10,7 @@ import android.view.View
import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
import code.name.monkey.retromusic.db.PlaylistDatabase
import code.name.monkey.retromusic.db.RoomPlaylistRepository
import code.name.monkey.retromusic.extensions.findNavController
import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.helper.MusicPlayerRemote.openAndShuffleQueue
@ -58,11 +58,10 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
addMusicServiceEventListener(libraryViewModel)
updateTabs()
val database: PlaylistDatabase = get()
val playlistRepository = get<RoomPlaylistRepository>()
lifecycleScope.launch {
println("Size:${database.playlistDao().playlistsWithSong()}")
println("Size:${playlistRepository.playlistWithSongs()}")
}
}
override fun onSupportNavigateUp(): Boolean =

View file

@ -13,22 +13,21 @@ import androidx.appcompat.widget.PopupMenu
import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.EXTRA_PLAYLIST
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
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.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.model.AbsCustomPlaylist
import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap
import code.name.monkey.retromusic.util.MusicUtil
@ -37,10 +36,10 @@ import java.util.*
class PlaylistAdapter(
private val activity: FragmentActivity,
var dataSet: List<Playlist>,
var dataSet: List<PlaylistWithSongs>,
private var itemLayoutRes: Int,
cabHolder: CabHolder?
) : AbsMultiSelectAdapter<PlaylistAdapter.ViewHolder, Playlist>(
) : AbsMultiSelectAdapter<PlaylistAdapter.ViewHolder, PlaylistWithSongs>(
activity,
cabHolder,
R.menu.menu_playlists_selection
@ -51,13 +50,13 @@ class PlaylistAdapter(
setHasStableIds(true)
}
fun swapDataSet(dataSet: List<Playlist>) {
fun swapDataSet(dataSet: List<PlaylistWithSongs>) {
this.dataSet = dataSet
notifyDataSetChanged()
}
override fun getItemId(position: Int): Long {
return dataSet[position].id.toLong()
return dataSet[position].playlistEntity.playListId.toLong()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -69,18 +68,18 @@ class PlaylistAdapter(
return ViewHolder(view)
}
private fun getPlaylistTitle(playlist: Playlist): String {
return if (TextUtils.isEmpty(playlist.name)) "-" else playlist.name
private fun getPlaylistTitle(playlist: PlaylistEntity): String {
return if (TextUtils.isEmpty(playlist.playlistName)) "-" else playlist.playlistName
}
private fun getPlaylistText(playlist: Playlist): String {
return MusicUtil.getPlaylistInfoString(activity, getSongs(playlist))
private fun getPlaylistText(playlist: PlaylistWithSongs): String {
return MusicUtil.playlistInfoString(activity, getSongs(playlist))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val playlist = dataSet[position]
holder.itemView.isActivated = isChecked(playlist)
holder.title?.text = getPlaylistTitle(playlist)
holder.title?.text = getPlaylistTitle(playlist.playlistEntity)
holder.text?.text = getPlaylistText(playlist)
holder.image?.setImageDrawable(getIconRes(playlist))
val isChecked = isChecked(playlist)
@ -92,37 +91,34 @@ class PlaylistAdapter(
//PlaylistBitmapLoader(this, holder, playlist).execute()
}
private fun getIconRes(playlist: Playlist): Drawable {
return if (MusicUtil.isFavoritePlaylist(activity, playlist))
private fun getIconRes(playlist: PlaylistWithSongs): Drawable {
return/* if (MusicUtil.isFavoritePlaylist(activity, playlist))
TintHelper.createTintedDrawable(
activity,
R.drawable.ic_favorite,
ThemeStore.accentColor(activity)
)
else TintHelper.createTintedDrawable(
else*/ TintHelper.createTintedDrawable(
activity,
R.drawable.ic_playlist_play,
ATHUtil.resolveColor(activity, R.attr.colorControlNormal)
)
}
override fun getItemViewType(position: Int): Int {
return if (dataSet[position] is AbsSmartPlaylist) SMART_PLAYLIST else DEFAULT_PLAYLIST
}
override fun getItemCount(): Int {
return dataSet.size
}
override fun getIdentifier(position: Int): Playlist? {
override fun getIdentifier(position: Int): PlaylistWithSongs? {
return dataSet[position]
}
override fun getName(playlist: Playlist): String {
return playlist.name
override fun getName(playlist: PlaylistWithSongs): String {
return playlist.playlistEntity.playlistName
}
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Playlist>) {
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<PlaylistWithSongs>) {
when (menuItem.itemId) {
else -> SongsMenuHelper.handleMenuClick(
activity,
@ -132,25 +128,27 @@ class PlaylistAdapter(
}
}
private fun getSongList(playlists: List<Playlist>): List<Song> {
private fun getSongList(playlists: List<PlaylistWithSongs>): List<Song> {
val songs = ArrayList<Song>()
for (playlist in playlists) {
/* for (playlist in playlists) {
songs.addAll(playlist.songs)
if (playlist is AbsCustomPlaylist) {
songs.addAll(playlist.songs())
} else {
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
}
}
}*/
return songs
}
private fun getSongs(playlist: Playlist): List<Song> {
val songs = ArrayList<Song>()
if (playlist is AbsSmartPlaylist) {
private fun getSongs(playlist: PlaylistWithSongs): List<SongEntity> {
val songs = ArrayList<SongEntity>()
songs.addAll(playlist.songs)
/*if (playlist is AbsSmartPlaylist) {
songs.addAll(playlist.songs())
} else {
songs.addAll(playlist.getSongs())
}
}*/
return songs
}
@ -167,7 +165,7 @@ class PlaylistAdapter(
val popupMenu = PopupMenu(activity, view)
popupMenu.inflate(R.menu.menu_item_playlist)
popupMenu.setOnMenuItemClickListener { item ->
PlaylistMenuHelper.handleMenuClick(activity, dataSet[layoutPosition], item)
return@setOnMenuItemClickListener true //PlaylistMenuHelper.handleMenuClick(activity, dataSet[layoutPosition], item)
}
popupMenu.show()
}

View file

@ -4,6 +4,10 @@ import androidx.room.*
@Dao
interface PlaylistDao {
@Query("SELECT * FROM PlaylistEntity WHERE playlist_name = :name")
suspend fun checkPlaylistExists(name: String): List<PlaylistEntity>
@Insert
suspend fun createPlaylist(playlistEntity: PlaylistEntity)
@ -16,4 +20,7 @@ interface PlaylistDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertSongs(songEntities: List<SongEntity>)
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id = :playlistName AND song_id = :songId")
suspend fun checkSongExistsWithPlaylistName(playlistName: String, songId: Int): List<SongEntity>
}

View file

@ -7,7 +7,7 @@ import androidx.room.RoomDatabase
@Database(
entities = [PlaylistEntity::class, SongEntity::class],
version = 4,
version = 7,
exportSchema = false
)
abstract class PlaylistDatabase : RoomDatabase() {

View file

@ -9,7 +9,10 @@ import kotlinx.android.parcel.Parcelize
@Entity
@Parcelize
class PlaylistEntity(
@PrimaryKey
@ColumnInfo(name = "playlist_name")
val playlistName: String
) : Parcelable
) : Parcelable {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "playlist_id")
var playListId: Int = 0
}

View file

@ -6,7 +6,7 @@ import androidx.room.Relation
data class PlaylistWithSongs(
@Embedded val playlistEntity: PlaylistEntity,
@Relation(
parentColumn = "playlist_name",
entityColumn = "playlist_creator_name"
parentColumn = "playlist_id",
entityColumn = "playlist_creator_id"
) val songs: List<SongEntity>
)

View file

@ -2,8 +2,39 @@ package code.name.monkey.retromusic.db
import androidx.annotation.WorkerThread
class RoomPlaylistRepository(private val playlistDao: PlaylistDao) {
interface RoomPlaylistRepository {
suspend fun createPlaylist(playlistEntity: PlaylistEntity)
suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity>
suspend fun playlists(): List<PlaylistEntity>
suspend fun playlistWithSongs(): List<PlaylistWithSongs>
suspend fun insertSongs(songs: List<SongEntity>)
}
class RealRoomPlaylistRepository(private val playlistDao: PlaylistDao) : RoomPlaylistRepository {
@WorkerThread
override suspend fun createPlaylist(playlistEntity: PlaylistEntity) =
playlistDao.createPlaylist(playlistEntity)
@WorkerThread
suspend fun getPlaylistWithSongs(): List<PlaylistWithSongs> = playlistDao.playlistsWithSong()
override suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> =
playlistDao.checkPlaylistExists(playlistName)
@WorkerThread
override suspend fun playlists(): List<PlaylistEntity> = playlistDao.playlists()
@WorkerThread
override suspend fun playlistWithSongs(): List<PlaylistWithSongs> =
playlistDao.playlistsWithSong()
@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)*/
playlistDao.insertSongs(songs)
}
}

View file

@ -8,8 +8,8 @@ import androidx.room.PrimaryKey
class SongEntity(
@ColumnInfo(name = "song_id")
val songId: Int,
@ColumnInfo(name = "playlist_creator_name")
val playlistCreatorName: String
@ColumnInfo(name = "playlist_creator_id")
val playlistCreatorId: Int
) {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "song_key")

View file

@ -6,8 +6,8 @@ import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.PlaylistDatabase
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.RoomPlaylistRepository
import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.extensions.colorButtons
import code.name.monkey.retromusic.extensions.extraNotNull
@ -37,15 +37,13 @@ class AddToRetroPlaylist : DialogFragment() {
return materialDialog(R.string.add_playlist_title)
.setItems(playlistNames.toTypedArray()) { _, which ->
val songs = RealSongRepository(requireContext()).songs()
println(songs.size)
if (which == 0) {
CreateRetroPlaylist().show(requireActivity().supportFragmentManager, "Dialog")
} else {
lifecycleScope.launch(Dispatchers.IO) {
println(Thread.currentThread().name)
val database: PlaylistDatabase = get()
val playlistRepository = get<RoomPlaylistRepository>()
val songEntities = songs.withPlaylistIds(playlistEntities[which - 1])
database.playlistDao().insertSongs(songEntities)
playlistRepository.insertSongs(songEntities)
}
}
dismiss()
@ -56,7 +54,7 @@ class AddToRetroPlaylist : DialogFragment() {
private fun List<Song>.withPlaylistIds(playlistEntity: PlaylistEntity): List<SongEntity> {
val songEntities = map {
SongEntity(it.id, playlistEntity.playlistName)
SongEntity(it.id, playlistEntity.playListId)
}
println(songEntities.size)
return songEntities

View file

@ -7,8 +7,8 @@ import android.view.LayoutInflater
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.PlaylistDatabase
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.RoomPlaylistRepository
import code.name.monkey.retromusic.extensions.colorButtons
import code.name.monkey.retromusic.extensions.materialDialog
import com.google.android.material.textfield.TextInputEditText
@ -29,9 +29,13 @@ class CreateRetroPlaylist : DialogFragment() {
) { _, _ ->
val playlistName = playlistView.text.toString()
if (!TextUtils.isEmpty(playlistName)) {
val database: PlaylistDatabase = get()
val playlistRepository: RoomPlaylistRepository = get()
lifecycleScope.launch {
database.playlistDao().createPlaylist(PlaylistEntity(playlistName))
if (playlistRepository.checkPlaylistExists(playlistName).isEmpty()) {
playlistRepository.createPlaylist(PlaylistEntity(playlistName))
} else {
println("Playlist exists")
}
}
} else {
playlistContainer.error = "Playlist is can't be empty"

View file

@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.fragments.ReloadType.*
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.model.*
@ -22,6 +23,7 @@ class LibraryViewModel(
private val songs = MutableLiveData<List<Song>>()
private val artists = MutableLiveData<List<Artist>>()
private val playlists = MutableLiveData<List<Playlist>>()
private val roomPlaylists = MutableLiveData<List<PlaylistWithSongs>>()
private val genres = MutableLiveData<List<Genre>>()
private val home = MutableLiveData<List<Home>>()
@ -31,6 +33,7 @@ class LibraryViewModel(
val songsLiveData: LiveData<List<Song>> = songs
val artistsLiveData: LiveData<List<Artist>> = artists
val playlisitsLiveData: LiveData<List<Playlist>> = playlists
val roomPlaylisitsLiveData: LiveData<List<PlaylistWithSongs>> = roomPlaylists
val genresLiveData: LiveData<List<Genre>> = genres
init {
@ -44,7 +47,8 @@ class LibraryViewModel(
albums.value = loadAlbums.await()
artists.value = loadArtists.await()
playlists.value = loadPlaylists.await()
genres.value = loadGenres.await()
roomPlaylists.value = loadPlaylistsWithSongs.await()
//genres.value = loadGenres.await()
home.value = loadHome.await()
}
@ -68,6 +72,10 @@ class LibraryViewModel(
get() = viewModelScope.async(IO) {
realRepository.allPlaylists()
}
private val loadPlaylistsWithSongs: Deferred<List<PlaylistWithSongs>>
get() = viewModelScope.async(IO) {
realRepository.playlistWithSongs()
}
private val loadGenres: Deferred<List<Genre>>
get() = viewModelScope.async(IO) {
@ -80,7 +88,7 @@ class LibraryViewModel(
Songs -> songs.value = loadSongs.await()
Albums -> albums.value = loadAlbums.await()
Artists -> artists.value = loadArtists.await()
HomeSections -> songs.value = loadSongs.await()
HomeSections -> home.value = loadHome.await()
}
}

View file

@ -10,7 +10,7 @@ import androidx.navigation.ui.NavigationUI
import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.PlaylistDatabase
import code.name.monkey.retromusic.db.RoomPlaylistRepository
import code.name.monkey.retromusic.dialogs.AddToRetroPlaylist
import code.name.monkey.retromusic.extensions.findNavController
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
@ -63,8 +63,8 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
R.id.action_settings ->
//CreateRetroPlaylist().show(childFragmentManager, "Dialog")
lifecycleScope.launch {
val database = get<PlaylistDatabase>()
AddToRetroPlaylist.getInstance(database.playlistDao().playlists())
val playlistRepository = get<RoomPlaylistRepository>()
AddToRetroPlaylist.getInstance(playlistRepository.playlists())
.show(childFragmentManager, "PlaylistDialog")
}

View file

@ -19,7 +19,7 @@ class PlaylistsFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
libraryViewModel.playlisitsLiveData.observe(viewLifecycleOwner, Observer {
libraryViewModel.roomPlaylisitsLiveData.observe(viewLifecycleOwner, Observer {
if (it.isNotEmpty())
adapter?.swapDataSet(it)
else
@ -43,6 +43,7 @@ class PlaylistsFragment :
)
}
companion object {
fun newInstance(): PlaylistsFragment {
return PlaylistsFragment()

View file

@ -16,6 +16,8 @@ package code.name.monkey.retromusic.repository
import android.content.Context
import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.RoomPlaylistRepository
import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.model.smartplaylist.NotPlayedPlaylist
import code.name.monkey.retromusic.network.LastFMService
@ -82,6 +84,10 @@ interface Repository {
suspend fun homeSectionsFlow(): Flow<Result<List<Home>>>
suspend fun playlist(playlistId: Int): Playlist
suspend fun playlistWithSongs(): List<PlaylistWithSongs>
fun songsFlow(): Flow<Result<List<Song>>>
fun albumsFlow(): Flow<Result<List<Album>>>
@ -92,7 +98,6 @@ interface Repository {
fun genresFlow(): Flow<Result<List<Genre>>>
suspend fun playlist(playlistId: Int): Playlist
}
class RealRepository(
@ -105,7 +110,8 @@ class RealRepository(
private val lastAddedRepository: LastAddedRepository,
private val playlistRepository: PlaylistRepository,
private val searchRepository: RealSearchRepository,
private val playedTracksRepository: TopPlayedRepository
private val playedTracksRepository: TopPlayedRepository,
private val roomPlaylistRepository: RoomPlaylistRepository
) : Repository {
override suspend fun allAlbums(): List<Album> = albumRepository.albums()
@ -214,6 +220,9 @@ class RealRepository(
override suspend fun playlist(playlistId: Int) =
playlistRepository.playlist(playlistId)
override suspend fun playlistWithSongs(): List<PlaylistWithSongs> =
roomPlaylistRepository.playlistWithSongs()
override suspend fun suggestionsHome(): Home {
val songs =
NotPlayedPlaylist().songs().shuffled().takeIf {

View file

@ -13,6 +13,7 @@ import android.widget.Toast
import androidx.core.content.FileProvider
import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.helper.MusicPlayerRemote.removeFromQueue
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.model.Playlist
@ -189,6 +190,13 @@ object MusicUtil : KoinComponent {
)
}
fun playlistInfoString(
context: Context,
songs: List<SongEntity>
): String {
return getSongCountString(context, songs.size)
}
fun getReadableDurationString(songDurationMillis: Long): String? {
var minutes = songDurationMillis / 1000 / 60
val seconds = songDurationMillis / 1000 % 60