music: add playlist renaming
Add the flow for renaming a playlist.
This commit is contained in:
parent
08d36df905
commit
3a5e1a5111
20 changed files with 285 additions and 49 deletions
|
@ -136,8 +136,9 @@ class MainFragment :
|
|||
collect(navModel.exploreNavigationItem.flow, ::handleExploreNavigation)
|
||||
collect(navModel.exploreArtistNavigationItem.flow, ::handleArtistNavigationPicker)
|
||||
collect(musicModel.newPlaylistSongs.flow, ::handleNewPlaylist)
|
||||
collect(musicModel.songsToAdd.flow, ::handleAddToPlaylist)
|
||||
collect(musicModel.playlistToRename.flow, ::handleRenamePlaylist)
|
||||
collect(musicModel.playlistToDelete.flow, ::handleDeletePlaylist)
|
||||
collect(musicModel.songsToAdd.flow, ::handleAddToPlaylist)
|
||||
collectImmediately(playbackModel.song, ::updateSong)
|
||||
collect(playbackModel.artistPickerSong.flow, ::handlePlaybackArtistPicker)
|
||||
collect(playbackModel.genrePickerSong.flow, ::handlePlaybackGenrePicker)
|
||||
|
@ -315,12 +316,11 @@ class MainFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleAddToPlaylist(songs: List<Song>?) {
|
||||
if (songs != null) {
|
||||
private fun handleRenamePlaylist(playlist: Playlist?) {
|
||||
if (playlist != null) {
|
||||
findNavController()
|
||||
.navigateSafe(
|
||||
MainFragmentDirections.actionAddToPlaylist(songs.map { it.uid }.toTypedArray()))
|
||||
musicModel.songsToAdd.consume()
|
||||
.navigateSafe(MainFragmentDirections.actionRenamePlaylist(playlist.uid))
|
||||
musicModel.playlistToRename.consume()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,6 +331,16 @@ class MainFragment :
|
|||
musicModel.playlistToDelete.consume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAddToPlaylist(songs: List<Song>?) {
|
||||
if (songs != null) {
|
||||
findNavController()
|
||||
.navigateSafe(
|
||||
MainFragmentDirections.actionAddToPlaylist(songs.map { it.uid }.toTypedArray()))
|
||||
musicModel.songsToAdd.consume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePlaybackArtistPicker(song: Song?) {
|
||||
if (song != null) {
|
||||
navModel.mainNavigateTo(
|
||||
|
|
|
@ -139,6 +139,10 @@ class PlaylistDetailFragment :
|
|||
requireContext().showToast(R.string.lng_queue_added)
|
||||
true
|
||||
}
|
||||
R.id.action_rename -> {
|
||||
musicModel.renamePlaylist(currentPlaylist)
|
||||
true
|
||||
}
|
||||
R.id.action_delete -> {
|
||||
musicModel.deletePlaylist(currentPlaylist)
|
||||
true
|
||||
|
|
|
@ -41,4 +41,10 @@ interface Header : Item {
|
|||
*/
|
||||
data class BasicHeader(@StringRes override val titleRes: Int) : Header
|
||||
|
||||
/**
|
||||
* A divider decoration used to delimit groups of data.
|
||||
*
|
||||
* @param anchor The [Header] this divider should be next to in a list. Used as a way to preserve
|
||||
* divider continuity during list updates.
|
||||
*/
|
||||
data class Divider(val anchor: Header?) : Item
|
||||
|
|
|
@ -255,6 +255,9 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
playbackModel.addToQueue(playlist)
|
||||
requireContext().showToast(R.string.lng_queue_added)
|
||||
}
|
||||
R.id.action_rename -> {
|
||||
musicModel.renamePlaylist(playlist)
|
||||
}
|
||||
R.id.action_delete -> {
|
||||
musicModel.deletePlaylist(playlist)
|
||||
}
|
||||
|
|
|
@ -31,8 +31,6 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
|
|||
* - Automatic edge-to-edge support
|
||||
* - Automatic [setHasFixedSize] setup
|
||||
*
|
||||
* FIXME: Broken span configuration
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
open class AuxioRecyclerView
|
||||
|
|
|
@ -20,9 +20,9 @@ package org.oxycblt.auxio.list.recycler
|
|||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.divider.MaterialDivider
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.ItemDividerBinding
|
||||
import org.oxycblt.auxio.databinding.ItemHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemParentBinding
|
||||
import org.oxycblt.auxio.databinding.ItemSongBinding
|
||||
|
@ -356,8 +356,8 @@ class BasicHeaderViewHolder private constructor(private val binding: ItemHeaderB
|
|||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class DividerViewHolder private constructor(private val binding: ItemDividerBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
class DividerViewHolder private constructor(divider: MaterialDivider) :
|
||||
RecyclerView.ViewHolder(divider) {
|
||||
|
||||
companion object {
|
||||
/** Unique ID for this ViewHolder type. */
|
||||
|
@ -369,8 +369,7 @@ class DividerViewHolder private constructor(private val binding: ItemDividerBind
|
|||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
fun from(parent: View) =
|
||||
DividerViewHolder(ItemDividerBinding.inflate(parent.context.inflater))
|
||||
fun from(parent: View) = DividerViewHolder(MaterialDivider(parent.context))
|
||||
|
||||
/** A comparator that can be used with DiffUtil. */
|
||||
val DIFF_CALLBACK =
|
||||
|
|
|
@ -118,6 +118,16 @@ sealed interface Music : Item {
|
|||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates an Auxio-style [UID] of random composition. Used if there is no
|
||||
* non-subjective, unlikely-to-change metadata of the music.
|
||||
*
|
||||
* @param mode The analogous [MusicMode] of the item that created this [UID].
|
||||
*/
|
||||
fun auxio(mode: MusicMode): UID {
|
||||
return UID(Format.AUXIO, mode, UUID.randomUUID())
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Auxio-style [UID] with a [UUID] composed of a hash of the non-subjective,
|
||||
* unlikely-to-change metadata of the music.
|
||||
|
|
|
@ -118,6 +118,14 @@ interface MusicRepository {
|
|||
*/
|
||||
fun createPlaylist(name: String, songs: List<Song>)
|
||||
|
||||
/**
|
||||
* Rename a [Playlist].
|
||||
*
|
||||
* @param playlist The [Playlist] to rename.
|
||||
* @param name The name of the new [Playlist].
|
||||
*/
|
||||
fun renamePlaylist(playlist: Playlist, name: String)
|
||||
|
||||
/**
|
||||
* Delete a [Playlist].
|
||||
*
|
||||
|
@ -269,6 +277,15 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun renamePlaylist(playlist: Playlist, name: String) {
|
||||
val userLibrary = userLibrary ?: return
|
||||
userLibrary.renamePlaylist(playlist, name)
|
||||
for (listener in updateListeners) {
|
||||
listener.onMusicChanges(
|
||||
MusicRepository.Changes(deviceLibrary = false, userLibrary = true))
|
||||
}
|
||||
}
|
||||
|
||||
override fun deletePlaylist(playlist: Playlist) {
|
||||
val userLibrary = userLibrary ?: return
|
||||
userLibrary.deletePlaylist(playlist)
|
||||
|
|
|
@ -52,15 +52,20 @@ constructor(
|
|||
/** Flag for opening a dialog to create a playlist of the given [Song]s. */
|
||||
val newPlaylistSongs: Event<List<Song>> = _newPlaylistSongs
|
||||
|
||||
private val _songsToAdd = MutableEvent<List<Song>>()
|
||||
/** Flag for opening a dialog to add the given [Song]s to a playlist. */
|
||||
val songsToAdd: Event<List<Song>> = _songsToAdd
|
||||
private val _playlistToRename = MutableEvent<Playlist?>()
|
||||
/** Flag for opening a dialog to rename the given [Playlist]. */
|
||||
val playlistToRename: Event<Playlist?>
|
||||
get() = _playlistToRename
|
||||
|
||||
private val _playlistToDelete = MutableEvent<Playlist>()
|
||||
/** Flag for opening a dialog to confirm deletion of the given [Playlist]. */
|
||||
val playlistToDelete: Event<Playlist>
|
||||
get() = _playlistToDelete
|
||||
|
||||
private val _songsToAdd = MutableEvent<List<Song>>()
|
||||
/** Flag for opening a dialog to add the given [Song]s to a playlist. */
|
||||
val songsToAdd: Event<List<Song>> = _songsToAdd
|
||||
|
||||
init {
|
||||
musicRepository.addUpdateListener(this)
|
||||
musicRepository.addIndexingListener(this)
|
||||
|
@ -111,6 +116,20 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the given playlist.
|
||||
*
|
||||
* @param playlist The [Playlist] to rename,
|
||||
* @param name The new name of the [Playlist]. If null, the user will be prompted for a name.
|
||||
*/
|
||||
fun renamePlaylist(playlist: Playlist, name: String? = null) {
|
||||
if (name != null) {
|
||||
musicRepository.renamePlaylist(playlist, name)
|
||||
} else {
|
||||
_playlistToRename.put(playlist)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a [Playlist].
|
||||
*
|
||||
|
|
|
@ -73,7 +73,7 @@ class AddToPlaylistDialog :
|
|||
// --- VIEWMODEL SETUP ---
|
||||
pickerModel.setSongsToAdd(args.songUids)
|
||||
collectImmediately(pickerModel.currentSongsToAdd, ::updatePendingSongs)
|
||||
collectImmediately(pickerModel.playlistChoices, ::updatePlaylistChoices)
|
||||
collectImmediately(pickerModel.playlistAddChoices, ::updatePlaylistChoices)
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: DialogMusicChoicesBinding) {
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.oxycblt.auxio.music.MusicViewModel
|
|||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
/**
|
||||
|
@ -54,6 +55,7 @@ class DeletePlaylistDialog : ViewBindingDialogFragment<DialogDeletePlaylistBindi
|
|||
// Now we can delete the playlist for-real this time.
|
||||
musicModel.deletePlaylist(
|
||||
unlikelyToBeNull(pickerModel.currentPlaylistToDelete.value), rude = true)
|
||||
requireContext().showToast(R.string.lng_playlist_deleted)
|
||||
}
|
||||
.setNegativeButton(R.string.lbl_cancel, null)
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.oxycblt.auxio.util.showToast
|
|||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
/**
|
||||
* A dialog allowing the name of a new/existing playlist to be edited.
|
||||
* A dialog allowing the name of a new playlist to be chosen before committing it to the database.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
|
|
@ -41,24 +41,34 @@ import org.oxycblt.auxio.music.Song
|
|||
class PlaylistPickerViewModel @Inject constructor(private val musicRepository: MusicRepository) :
|
||||
ViewModel(), MusicRepository.UpdateListener {
|
||||
private val _currentPendingPlaylist = MutableStateFlow<PendingPlaylist?>(null)
|
||||
/** A new [Playlist] having it's name chosen by the user. Null if none yet. */
|
||||
val currentPendingPlaylist: StateFlow<PendingPlaylist?>
|
||||
get() = _currentPendingPlaylist
|
||||
|
||||
private val _currentPlaylistToRename = MutableStateFlow<Playlist?>(null)
|
||||
/** An existing [Playlist] that is being renamed. Null if none yet. */
|
||||
val currentPlaylistToRename: StateFlow<Playlist?>
|
||||
get() = _currentPlaylistToRename
|
||||
|
||||
private val _currentPlaylistToDelete = MutableStateFlow<Playlist?>(null)
|
||||
/** The current [Playlist] that needs it's deletion confirmed. Null if none yet. */
|
||||
val currentPlaylistToDelete: StateFlow<Playlist?>
|
||||
get() = _currentPlaylistToDelete
|
||||
|
||||
private val _chosenName = MutableStateFlow<ChosenName>(ChosenName.Empty)
|
||||
/** The users chosen name for [currentPendingPlaylist] or [currentPlaylistToRename]. */
|
||||
val chosenName: StateFlow<ChosenName>
|
||||
get() = _chosenName
|
||||
|
||||
private val _currentSongsToAdd = MutableStateFlow<List<Song>?>(null)
|
||||
/** A batch of [Song]s to add to a playlist chosen by the user. Null if none yet. */
|
||||
val currentSongsToAdd: StateFlow<List<Song>?>
|
||||
get() = _currentSongsToAdd
|
||||
|
||||
private val _playlistChoices = MutableStateFlow<List<PlaylistChoice>>(listOf())
|
||||
val playlistChoices: StateFlow<List<PlaylistChoice>>
|
||||
get() = _playlistChoices
|
||||
|
||||
private val _currentPlaylistToDelete = MutableStateFlow<Playlist?>(null)
|
||||
val currentPlaylistToDelete: StateFlow<Playlist?>
|
||||
get() = _currentPlaylistToDelete
|
||||
private val _playlistAddChoices = MutableStateFlow<List<PlaylistChoice>>(listOf())
|
||||
/** The [Playlist]s that [currentSongsToAdd] could be added to. */
|
||||
val playlistAddChoices: StateFlow<List<PlaylistChoice>>
|
||||
get() = _playlistAddChoices
|
||||
|
||||
init {
|
||||
musicRepository.addUpdateListener(this)
|
||||
|
@ -124,6 +134,24 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new [currentPlaylistToRename] from a [Playlist] [Music.UID].
|
||||
*
|
||||
* @param playlistUid The [Music.UID]s of the [Playlist] to rename.
|
||||
*/
|
||||
fun setPlaylistToRename(playlistUid: Music.UID) {
|
||||
_currentPlaylistToRename.value = musicRepository.userLibrary?.findPlaylist(playlistUid)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new [currentPendingPlaylist] from a new [Playlist] [Music.UID].
|
||||
*
|
||||
* @param playlistUid The [Music.UID] of the [Playlist] to delete.
|
||||
*/
|
||||
fun setPlaylistToDelete(playlistUid: Music.UID) {
|
||||
_currentPlaylistToDelete.value = musicRepository.userLibrary?.findPlaylist(playlistUid)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current [chosenName] based on new user input.
|
||||
*
|
||||
|
@ -160,21 +188,12 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
|||
|
||||
private fun refreshPlaylistChoices(songs: List<Song>) {
|
||||
val userLibrary = musicRepository.userLibrary ?: return
|
||||
_playlistChoices.value =
|
||||
_playlistAddChoices.value =
|
||||
Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).playlists(userLibrary.playlists).map {
|
||||
val songSet = it.songs.toSet()
|
||||
PlaylistChoice(it, songs.all(songSet::contains))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new [currentPendingPlaylist] from a new [Playlist] [Music.UID].
|
||||
*
|
||||
* @param playlistUid The [Music.UID] of the [Playlist] to delete.
|
||||
*/
|
||||
fun setPlaylistToDelete(playlistUid: Music.UID) {
|
||||
_currentPlaylistToDelete.value = musicRepository.userLibrary?.findPlaylist(playlistUid)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Auxio Project
|
||||
* RenamePlaylistDialog.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.picker
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.DialogPlaylistNameBinding
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
/**
|
||||
* A dialog allowing the name of a new playlist to be chosen before committing it to the database.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class RenamePlaylistDialog : ViewBindingDialogFragment<DialogPlaylistNameBinding>() {
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
private val pickerModel: PlaylistPickerViewModel by viewModels()
|
||||
// Information about what playlist to name for is initially within the navigation arguments
|
||||
// as UIDs, as that is the only safe way to parcel playlist information.
|
||||
private val args: RenamePlaylistDialogArgs by navArgs()
|
||||
private var initializedField = false
|
||||
|
||||
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
||||
builder
|
||||
.setTitle(R.string.lbl_rename)
|
||||
.setPositiveButton(R.string.lbl_ok) { _, _ ->
|
||||
val playlist = unlikelyToBeNull(pickerModel.currentPlaylistToRename.value)
|
||||
val chosenName = pickerModel.chosenName.value as ChosenName.Valid
|
||||
musicModel.renamePlaylist(playlist, chosenName.value)
|
||||
requireContext().showToast(R.string.lng_playlist_renamed)
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
.setNegativeButton(R.string.lbl_cancel, null)
|
||||
}
|
||||
|
||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||
DialogPlaylistNameBinding.inflate(inflater)
|
||||
|
||||
override fun onBindingCreated(binding: DialogPlaylistNameBinding, savedInstanceState: Bundle?) {
|
||||
super.onBindingCreated(binding, savedInstanceState)
|
||||
|
||||
// --- UI SETUP ---
|
||||
binding.playlistName.addTextChangedListener { pickerModel.updateChosenName(it?.toString()) }
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
pickerModel.setPlaylistToRename(args.playlistUid)
|
||||
collectImmediately(pickerModel.currentPlaylistToRename, ::updatePlaylistToRename)
|
||||
collectImmediately(pickerModel.chosenName, ::updateChosenName)
|
||||
}
|
||||
|
||||
private fun updatePlaylistToRename(playlist: Playlist?) {
|
||||
if (playlist == null) {
|
||||
// Nothing to rename anymore.
|
||||
findNavController().navigateUp()
|
||||
return
|
||||
}
|
||||
|
||||
if (!initializedField) {
|
||||
requireBinding().playlistName.setText(playlist.name.resolve(requireContext()))
|
||||
initializedField = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateChosenName(chosenName: ChosenName) {
|
||||
(dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE)?.isEnabled =
|
||||
chosenName is ChosenName.Valid
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ package org.oxycblt.auxio.music.user
|
|||
import org.oxycblt.auxio.music.*
|
||||
import org.oxycblt.auxio.music.device.DeviceLibrary
|
||||
import org.oxycblt.auxio.music.info.Name
|
||||
import org.oxycblt.auxio.util.update
|
||||
|
||||
class PlaylistImpl
|
||||
private constructor(
|
||||
|
@ -33,6 +32,15 @@ private constructor(
|
|||
override val albums =
|
||||
songs.groupBy { it.album }.entries.sortedByDescending { it.value.size }.map { it.key }
|
||||
|
||||
/**
|
||||
* Clone the data in this instance to a new [PlaylistImpl] with the given [name].
|
||||
*
|
||||
* @param name The new name to use.
|
||||
* @param musicSettings [MusicSettings] required for name configuration.
|
||||
*/
|
||||
fun edit(name: String, musicSettings: MusicSettings) =
|
||||
PlaylistImpl(uid, Name.Known.from(name, null, musicSettings), songs)
|
||||
|
||||
/**
|
||||
* Clone the data in this instance to a new [PlaylistImpl] with the given [Song]s.
|
||||
*
|
||||
|
@ -48,9 +56,14 @@ private constructor(
|
|||
inline fun edit(edits: MutableList<Song>.() -> Unit) = edit(songs.toMutableList().apply(edits))
|
||||
|
||||
override fun equals(other: Any?) =
|
||||
other is PlaylistImpl && uid == other.uid && songs == other.songs
|
||||
other is PlaylistImpl && uid == other.uid && name == other.name && songs == other.songs
|
||||
|
||||
override fun hashCode() = 31 * uid.hashCode() + songs.hashCode()
|
||||
override fun hashCode(): Int {
|
||||
var hashCode = uid.hashCode()
|
||||
hashCode = 31 * hashCode + name.hashCode()
|
||||
hashCode = 31 * hashCode + songs.hashCode()
|
||||
return hashCode
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
|
@ -62,7 +75,7 @@ private constructor(
|
|||
*/
|
||||
fun from(name: String, songs: List<Song>, musicSettings: MusicSettings) =
|
||||
PlaylistImpl(
|
||||
Music.UID.auxio(MusicMode.PLAYLISTS) { update(name) },
|
||||
Music.UID.auxio(MusicMode.PLAYLISTS),
|
||||
Name.Known.from(name, null, musicSettings),
|
||||
songs)
|
||||
|
||||
|
|
|
@ -80,6 +80,14 @@ interface MutableUserLibrary : UserLibrary {
|
|||
*/
|
||||
fun createPlaylist(name: String, songs: List<Song>)
|
||||
|
||||
/**
|
||||
* Rename a [Playlist].
|
||||
*
|
||||
* @param playlist The [Playlist] to rename.
|
||||
* @param name The name of the new [Playlist].
|
||||
*/
|
||||
fun renamePlaylist(playlist: Playlist, name: String)
|
||||
|
||||
/**
|
||||
* Delete a [Playlist].
|
||||
*
|
||||
|
@ -122,6 +130,13 @@ private class UserLibraryImpl(
|
|||
playlistMap[playlistImpl.uid] = playlistImpl
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun renamePlaylist(playlist: Playlist, name: String) {
|
||||
val playlistImpl =
|
||||
requireNotNull(playlistMap[playlist.uid]) { "Cannot rename invalid playlist" }
|
||||
playlistMap[playlist.uid] = playlistImpl.edit(name, musicSettings)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun deletePlaylist(playlist: Playlist) {
|
||||
playlistMap.remove(playlist.uid)
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
<item
|
||||
android:id="@+id/action_queue_add"
|
||||
android:title="@string/lbl_queue_add" />
|
||||
<item
|
||||
android:id="@+id/action_rename"
|
||||
android:title="@string/lbl_rename" />
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:title="@string/lbl_delete" />
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
<item
|
||||
android:id="@+id/action_queue_add"
|
||||
android:title="@string/lbl_queue_add" />
|
||||
<item
|
||||
android:id="@+id/action_rename"
|
||||
android:title="@string/lbl_rename" />
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:title="@string/lbl_delete" />
|
||||
|
|
|
@ -21,11 +21,14 @@
|
|||
android:id="@+id/action_new_playlist"
|
||||
app:destination="@id/new_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_add_to_playlist"
|
||||
app:destination="@id/add_to_playlist_dialog" />
|
||||
android:id="@+id/action_rename_playlist"
|
||||
app:destination="@id/rename_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_delete_playlist"
|
||||
app:destination="@id/delete_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_add_to_playlist"
|
||||
app:destination="@id/add_to_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_pick_navigation_artist"
|
||||
app:destination="@id/navigate_to_artist_dialog" />
|
||||
|
@ -58,16 +61,13 @@
|
|||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/add_to_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.AddToPlaylistDialog"
|
||||
android:label="new_playlist_dialog"
|
||||
android:id="@+id/rename_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.RenamePlaylistDialog"
|
||||
android:label="rename_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="songUids"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID[]" />
|
||||
<action
|
||||
android:id="@+id/action_new_playlist"
|
||||
app:destination="@id/new_playlist_dialog" />
|
||||
android:name="playlistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
|
@ -80,6 +80,19 @@
|
|||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/add_to_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.AddToPlaylistDialog"
|
||||
android:label="new_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="songUids"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID[]" />
|
||||
<action
|
||||
android:id="@+id/action_new_playlist"
|
||||
app:destination="@id/new_playlist_dialog" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/navigate_to_artist_dialog"
|
||||
android:name="org.oxycblt.auxio.navigation.picker.NavigateToArtistDialog"
|
||||
|
|
|
@ -79,6 +79,8 @@
|
|||
<string name="lbl_playlist">Playlist</string>
|
||||
<string name="lbl_playlists">Playlists</string>
|
||||
<string name="lbl_new_playlist">New playlist</string>
|
||||
<string name="lbl_rename">Rename</string>
|
||||
<string name="lbl_rename_playlist">Rename playlist</string>
|
||||
<string name="lbl_delete">Delete</string>
|
||||
<string name="lbl_confirm_delete_playlist">Delete playlist?</string>
|
||||
|
||||
|
@ -165,6 +167,8 @@
|
|||
<string name="lng_observing">Monitoring your music library for changes…</string>
|
||||
<string name="lng_queue_added">Added to queue</string>
|
||||
<string name="lng_playlist_created">Playlist created</string>
|
||||
<string name="lng_playlist_renamed">Playlist renamed</string>
|
||||
<string name="lng_playlist_deleted">Playlist deleted</string>
|
||||
<string name="lng_playlist_added">Added to playlist</string>
|
||||
<string name="lng_author">Developed by Alexander Capehart</string>
|
||||
<!-- As in music library -->
|
||||
|
|
Loading…
Reference in a new issue