⬇️ Import legacy playlists

This commit is contained in:
Hemanth S 2020-09-06 01:43:45 +05:30
parent 51d2c17ad7
commit 45b93ed3aa
13 changed files with 175 additions and 65 deletions

View file

@ -3,6 +3,7 @@ package code.name.monkey.retromusic.activities
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import android.view.View import android.view.View
@ -95,8 +96,8 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
private fun handlePlaybackIntent(intent: Intent) { private fun handlePlaybackIntent(intent: Intent) {
lifecycleScope.launch(IO) { lifecycleScope.launch(IO) {
val uri = intent.data val uri: Uri? = intent.data
val mimeType = intent.type val mimeType: String? = intent.type
var handled = false var handled = false
if (intent.action != null && if (intent.action != null &&
intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
@ -113,9 +114,9 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
MusicPlayerRemote.playFromUri(uri) MusicPlayerRemote.playFromUri(uri)
handled = true handled = true
} else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) { } else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "playlistId", "playlist").toInt() val id: Int = parseIdFromIntent(intent, "playlistId", "playlist").toInt()
if (id >= 0) { if (id >= 0) {
val position = intent.getIntExtra("position", 0) val position: Int = intent.getIntExtra("position", 0)
val songs: List<Song> = val songs: List<Song> =
PlaylistSongsLoader.getPlaylistSongList(this@MainActivity, id) PlaylistSongsLoader.getPlaylistSongList(this@MainActivity, id)
MusicPlayerRemote.openQueue(songs, position, true) MusicPlayerRemote.openQueue(songs, position, true)
@ -124,7 +125,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) { } else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "albumId", "album").toInt() val id = parseIdFromIntent(intent, "albumId", "album").toInt()
if (id >= 0) { if (id >= 0) {
val position = intent.getIntExtra("position", 0) val position: Int = intent.getIntExtra("position", 0)
MusicPlayerRemote.openQueue( MusicPlayerRemote.openQueue(
libraryViewModel.albumById(id).songs!!, libraryViewModel.albumById(id).songs!!,
position, position,
@ -133,9 +134,9 @@ class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeLis
handled = true handled = true
} }
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) { } else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "artistId", "artist").toInt() val id: Int = parseIdFromIntent(intent, "artistId", "artist").toInt()
if (id >= 0) { if (id >= 0) {
val position = intent.getIntExtra("position", 0) val position: Int = intent.getIntExtra("position", 0)
MusicPlayerRemote.openQueue( MusicPlayerRemote.openQueue(
libraryViewModel.artistById(id).songs, libraryViewModel.artistById(id).songs,
position, position,

View file

@ -22,6 +22,7 @@ import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.PlaylistWithSongs import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.db.toSongs
import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
@ -33,7 +34,6 @@ import code.name.monkey.retromusic.repository.PlaylistSongsLoader
import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroColorUtil
import java.util.*
class PlaylistAdapter( class PlaylistAdapter(
private val activity: FragmentActivity, private val activity: FragmentActivity,
@ -73,7 +73,7 @@ class PlaylistAdapter(
} }
private fun getPlaylistText(playlist: PlaylistWithSongs): String { private fun getPlaylistText(playlist: PlaylistWithSongs): String {
return MusicUtil.playlistInfoString(activity, getSongs(playlist)) return MusicUtil.getPlaylistInfoString(activity, playlist.songs.toSongs())
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
@ -81,7 +81,7 @@ class PlaylistAdapter(
holder.itemView.isActivated = isChecked(playlist) holder.itemView.isActivated = isChecked(playlist)
holder.title?.text = getPlaylistTitle(playlist.playlistEntity) holder.title?.text = getPlaylistTitle(playlist.playlistEntity)
holder.text?.text = getPlaylistText(playlist) holder.text?.text = getPlaylistText(playlist)
holder.image?.setImageDrawable(getIconRes(playlist)) holder.image?.setImageDrawable(getIconRes())
val isChecked = isChecked(playlist) val isChecked = isChecked(playlist)
if (isChecked) { if (isChecked) {
holder.menu?.hide() holder.menu?.hide()
@ -91,20 +91,11 @@ class PlaylistAdapter(
//PlaylistBitmapLoader(this, holder, playlist).execute() //PlaylistBitmapLoader(this, holder, playlist).execute()
} }
private fun getIconRes(playlist: PlaylistWithSongs): Drawable { private fun getIconRes(): Drawable = TintHelper.createTintedDrawable(
return/* if (MusicUtil.isFavoritePlaylist(activity, playlist))
TintHelper.createTintedDrawable(
activity,
R.drawable.ic_favorite,
ThemeStore.accentColor(activity)
)
else*/ TintHelper.createTintedDrawable(
activity, activity,
R.drawable.ic_playlist_play, R.drawable.ic_playlist_play,
ATHUtil.resolveColor(activity, R.attr.colorControlNormal) ATHUtil.resolveColor(activity, R.attr.colorControlNormal)
) )
}
override fun getItemCount(): Int { override fun getItemCount(): Int {
return dataSet.size return dataSet.size
@ -129,27 +120,16 @@ class PlaylistAdapter(
} }
private fun getSongList(playlists: List<PlaylistWithSongs>): List<Song> { private fun getSongList(playlists: List<PlaylistWithSongs>): List<Song> {
val songs = ArrayList<Song>() val songs = mutableListOf<Song>()
/* for (playlist in playlists) { playlists.forEach {
songs.addAll(playlist.songs) songs.addAll(it.songs.toSongs())
if (playlist is AbsCustomPlaylist) {
songs.addAll(playlist.songs())
} else {
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
} }
}*/
return songs return songs
} }
private fun getSongs(playlist: PlaylistWithSongs): List<SongEntity> { private fun getSongs(playlist: PlaylistWithSongs): List<SongEntity> =
val songs = ArrayList<SongEntity>() mutableListOf<SongEntity>().apply {
songs.addAll(playlist.songs) addAll(playlist.songs)
/*if (playlist is AbsSmartPlaylist) {
songs.addAll(playlist.songs())
} else {
songs.addAll(playlist.getSongs())
}*/
return songs
} }
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {

View file

@ -17,17 +17,14 @@ import code.name.monkey.retromusic.extensions.materialDialog
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType.Playlists import code.name.monkey.retromusic.fragments.ReloadType.Playlists
import code.name.monkey.retromusic.model.Song 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.TextInputEditText
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import kotlinx.android.synthetic.main.dialog_playlist.view.* import kotlinx.android.synthetic.main.dialog_playlist.view.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class CreatePlaylistDialog : DialogFragment() { class CreatePlaylistDialog : DialogFragment() {
private val repository by inject<RealRepository>()
private val libraryViewModel by sharedViewModel<LibraryViewModel>() private val libraryViewModel by sharedViewModel<LibraryViewModel>()
companion object { companion object {
@ -57,9 +54,10 @@ class CreatePlaylistDialog : DialogFragment() {
val playlistName = playlistView.text.toString() val playlistName = playlistView.text.toString()
if (!TextUtils.isEmpty(playlistName)) { if (!TextUtils.isEmpty(playlistName)) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
if (repository.checkPlaylistExists(playlistName).isEmpty()) { if (libraryViewModel.checkPlaylistExists(playlistName).isEmpty()) {
val playlistId = repository.createPlaylist(PlaylistEntity(playlistName)) val playlistId: Long =
repository.insertSongs(songs.map { it.toSongEntity(playlistId.toInt()) }) libraryViewModel.createPlaylist(PlaylistEntity(playlistName))
libraryViewModel.insertSongs(songs.map { it.toSongEntity(playlistId.toInt()) })
libraryViewModel.forceReload(Playlists) libraryViewModel.forceReload(Playlists)
} else { } else {
Toast.makeText(requireContext(), "Playlist exists", Toast.LENGTH_SHORT) Toast.makeText(requireContext(), "Playlist exists", Toast.LENGTH_SHORT)

View file

@ -1,7 +0,0 @@
package code.name.monkey.retromusic.dialogs
import androidx.fragment.app.DialogFragment
class ImportPlaylist : DialogFragment() {
}

View file

@ -0,0 +1,111 @@
package code.name.monkey.retromusic.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.fragments.ReloadType.Playlists
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.util.MusicUtil
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
class ImportPlaylistFragment :
AbsRecyclerViewFragment<LegacyPlaylistAdapter, LinearLayoutManager>(), PlaylistClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
libraryViewModel.getLegacyPlaylist().observe(viewLifecycleOwner, Observer {
if (it.isNotEmpty())
adapter?.swapData(it)
else
adapter?.swapData(listOf())
})
}
override fun createLayoutManager(): LinearLayoutManager {
return LinearLayoutManager(requireContext())
}
override fun createAdapter(): LegacyPlaylistAdapter {
return LegacyPlaylistAdapter(
requireActivity(),
ArrayList(),
R.layout.item_list_no_image,
this
)
}
override fun onPlaylistClick(playlist: Playlist) {
Toast.makeText(requireContext(), "Importing ${playlist.name}", Toast.LENGTH_LONG).show()
lifecycleScope.launch(IO) {
if (playlist.name.isNotEmpty()) {
if (libraryViewModel.checkPlaylistExists(playlist.name).isEmpty()) {
val playlistId: Long =
libraryViewModel.createPlaylist(PlaylistEntity(playlist.name))
libraryViewModel.insertSongs(playlist.getSongs().map {
it.toSongEntity(playlistId.toInt())
})
libraryViewModel.forceReload(Playlists)
} else {
Toast.makeText(requireContext(), "Playlist exists", Toast.LENGTH_SHORT)
.show()
}
}
}
}
}
class LegacyPlaylistAdapter(
private val activity: FragmentActivity,
private var list: List<Playlist>,
private val layoutRes: Int,
private val playlistClickListener: PlaylistClickListener
) :
RecyclerView.Adapter<LegacyPlaylistAdapter.ViewHolder>() {
fun swapData(list: List<Playlist>) {
this.list = list
notifyDataSetChanged()
}
class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(layoutRes, parent, false)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val playlist: Playlist = list[position]
holder.title?.text = playlist.name
holder.text?.text = MusicUtil.getPlaylistInfoString(activity, playlist.getSongs())
holder.itemView.setOnClickListener {
playlistClickListener.onPlaylistClick(playlist)
}
}
override fun getItemCount(): Int {
return list.size
}
}
interface PlaylistClickListener {
fun onPlaylistClick(playlist: Playlist)
}

View file

@ -24,6 +24,7 @@ class LibraryViewModel(
private val songs = MutableLiveData<List<Song>>() private val songs = MutableLiveData<List<Song>>()
private val artists = MutableLiveData<List<Artist>>() private val artists = MutableLiveData<List<Artist>>()
private val playlists = MutableLiveData<List<PlaylistWithSongs>>() private val playlists = MutableLiveData<List<PlaylistWithSongs>>()
private val legacyPlaylists = MutableLiveData<List<Playlist>>()
private val genres = MutableLiveData<List<Genre>>() private val genres = MutableLiveData<List<Genre>>()
private val home = MutableLiveData<List<Home>>() private val home = MutableLiveData<List<Home>>()
@ -58,6 +59,11 @@ class LibraryViewModel(
return playlists return playlists
} }
fun getLegacyPlaylist(): LiveData<List<Playlist>> {
fetchLegacyPlaylist()
return legacyPlaylists
}
fun getGenre(): LiveData<List<Genre>> { fun getGenre(): LiveData<List<Genre>> {
fetchGenres() fetchGenres()
return genres return genres
@ -92,6 +98,12 @@ class LibraryViewModel(
} }
} }
private fun fetchLegacyPlaylist() {
viewModelScope.launch(IO) {
legacyPlaylists.postValue(repository.fetchLegacyPlaylist())
}
}
private fun fetchGenres() { private fun fetchGenres() {
viewModelScope.launch(IO) { viewModelScope.launch(IO) {
genres.postValue(repository.fetchGenres()) genres.postValue(repository.fetchGenres())
@ -186,6 +198,12 @@ class LibraryViewModel(
suspend fun removeSongFromPlaylist(songEntity: SongEntity) = suspend fun removeSongFromPlaylist(songEntity: SongEntity) =
repository.removeSongFromPlaylist(songEntity) repository.removeSongFromPlaylist(songEntity)
suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> =
repository.checkPlaylistExists(playlistName)
suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long =
repository.createPlaylist(playlistEntity)
} }
enum class ReloadType { enum class ReloadType {

View file

@ -61,7 +61,8 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
null, null,
navOptions navOptions
) )
R.id.action_add_to_playlist -> CreatePlaylistDialog().show( R.id.action_import_playlist -> findNavController(R.id.fragment_container).navigate(R.id.action_import_playlist)
R.id.action_add_to_playlist -> CreatePlaylistDialog.create(emptyList()).show(
childFragmentManager, childFragmentManager,
"ShowCreatePlaylistDialog" "ShowCreatePlaylistDialog"
) )

View file

@ -6,14 +6,14 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import kotlinx.android.synthetic.main.fragment_library.* import kotlinx.android.synthetic.main.fragment_library.*
class PlaylistsFragment : AbsRecyclerViewFragment<PlaylistAdapter, GridLayoutManager>() { class PlaylistsFragment : AbsRecyclerViewFragment<PlaylistAdapter, LinearLayoutManager>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -28,8 +28,8 @@ class PlaylistsFragment : AbsRecyclerViewFragment<PlaylistAdapter, GridLayoutMan
override val emptyMessage: Int override val emptyMessage: Int
get() = R.string.no_playlists get() = R.string.no_playlists
override fun createLayoutManager(): GridLayoutManager { override fun createLayoutManager(): LinearLayoutManager {
return GridLayoutManager(requireContext(), 1) return LinearLayoutManager(requireContext())
} }
override fun createAdapter(): PlaylistAdapter { override fun createAdapter(): PlaylistAdapter {
@ -51,7 +51,7 @@ class PlaylistsFragment : AbsRecyclerViewFragment<PlaylistAdapter, GridLayoutMan
menu.removeItem(R.id.action_layout_type) menu.removeItem(R.id.action_layout_type)
menu.removeItem(R.id.action_sort_order) menu.removeItem(R.id.action_sort_order)
menu.add(0, R.id.action_add_to_playlist, 0, R.string.new_playlist_title) menu.add(0, R.id.action_add_to_playlist, 0, R.string.new_playlist_title)
.setIcon(R.drawable.ic_playlist_add) menu.add(0, R.id.action_import_playlist, 0, R.string.import_playlist)
menu.findItem(R.id.action_settings).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.findItem(R.id.action_settings).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }

View file

@ -43,7 +43,7 @@ interface Repository {
suspend fun allSongs(): List<Song> suspend fun allSongs(): List<Song>
suspend fun fetchArtists(): List<Artist> suspend fun fetchArtists(): List<Artist>
suspend fun albumArtists(): List<Artist> suspend fun albumArtists(): List<Artist>
suspend fun allPlaylists(): List<Playlist> suspend fun fetchLegacyPlaylist(): List<Playlist>
suspend fun fetchGenres(): List<Genre> suspend fun fetchGenres(): List<Genre>
suspend fun search(query: String?): MutableList<Any> suspend fun search(query: String?): MutableList<Any>
suspend fun getPlaylistSongs(playlist: Playlist): List<Song> suspend fun getPlaylistSongs(playlist: Playlist): List<Song>
@ -125,7 +125,7 @@ class RealRepository(
override suspend fun topAlbums(): List<Album> = topPlayedRepository.topAlbums() override suspend fun topAlbums(): List<Album> = topPlayedRepository.topAlbums()
override suspend fun allPlaylists(): List<Playlist> = playlistRepository.playlists() override suspend fun fetchLegacyPlaylist(): List<Playlist> = playlistRepository.playlists()
override suspend fun fetchGenres(): List<Genre> = genreRepository.genres() override suspend fun fetchGenres(): List<Genre> = genreRepository.genres()

View file

@ -89,6 +89,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:textAppearance="@style/TextViewBody2"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
tools:text="@tools:sample/full_names" /> tools:text="@tools:sample/full_names" />
</LinearLayout> </LinearLayout>

View file

@ -40,6 +40,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="true"
android:textAppearance="@style/TextViewBody2" android:textAppearance="@style/TextViewBody2"
android:textColor="?android:textColorSecondary"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" app:layout_constraintTop_toBottomOf="@id/title"

View file

@ -46,4 +46,9 @@
android:label="" android:label=""
tools:layout="@layout/fragment_banner_home" /> tools:layout="@layout/fragment_banner_home" />
<fragment
android:id="@+id/action_import_playlist"
android:name="code.name.monkey.retromusic.dialogs.ImportPlaylistFragment"
android:label="ImportPlaylist"
tools:layout="@layout/fragment_main_activity_recycler_view" />
</navigation> </navigation>

View file

@ -884,4 +884,5 @@
<string name="hello_blank_fragment">Hello blank fragment</string> <string name="hello_blank_fragment">Hello blank fragment</string>
<string name="done">Done</string> <string name="done">Done</string>
<string name="ok">Ok</string> <string name="ok">Ok</string>
<string name="import_playlist">Import playlist</string>
</resources> </resources>