From 3a5e1a51116179b9f3cc94369ee9af0071f140be Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Thu, 18 May 2023 17:09:26 -0600 Subject: [PATCH] music: add playlist renaming Add the flow for renaming a playlist. --- .../java/org/oxycblt/auxio/MainFragment.kt | 22 +++-- .../auxio/detail/PlaylistDetailFragment.kt | 4 + .../main/java/org/oxycblt/auxio/list/Data.kt | 6 ++ .../org/oxycblt/auxio/list/ListFragment.kt | 3 + .../auxio/list/recycler/AuxioRecyclerView.kt | 2 - .../auxio/list/recycler/ViewHolders.kt | 9 +- .../java/org/oxycblt/auxio/music/Music.kt | 10 ++ .../oxycblt/auxio/music/MusicRepository.kt | 17 ++++ .../org/oxycblt/auxio/music/MusicViewModel.kt | 25 ++++- .../auxio/music/picker/AddToPlaylistDialog.kt | 2 +- .../music/picker/DeletePlaylistDialog.kt | 2 + .../auxio/music/picker/NewPlaylistDialog.kt | 2 +- .../music/picker/PlaylistPickerViewModel.kt | 53 ++++++---- .../music/picker/RenamePlaylistDialog.kt | 98 +++++++++++++++++++ .../oxycblt/auxio/music/user/PlaylistImpl.kt | 21 +++- .../oxycblt/auxio/music/user/UserLibrary.kt | 15 +++ .../main/res/menu/menu_playlist_actions.xml | 3 + .../main/res/menu/menu_playlist_detail.xml | 3 + app/src/main/res/navigation/nav_main.xml | 33 +++++-- app/src/main/res/values/strings.xml | 4 + 20 files changed, 285 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/music/picker/RenamePlaylistDialog.kt diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index 106ba2885..90ada8b9c 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -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?) { - 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?) { + 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( diff --git a/app/src/main/java/org/oxycblt/auxio/detail/PlaylistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/PlaylistDetailFragment.kt index d1b7da0c1..9b7d78b5c 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/PlaylistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/PlaylistDetailFragment.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/list/Data.kt b/app/src/main/java/org/oxycblt/auxio/list/Data.kt index 27f059602..e41dd4149 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/Data.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/Data.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt b/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt index f2e050e40..49655d01b 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt @@ -255,6 +255,9 @@ abstract class ListFragment : playbackModel.addToQueue(playlist) requireContext().showToast(R.string.lng_queue_added) } + R.id.action_rename -> { + musicModel.renamePlaylist(playlist) + } R.id.action_delete -> { musicModel.deletePlaylist(playlist) } diff --git a/app/src/main/java/org/oxycblt/auxio/list/recycler/AuxioRecyclerView.kt b/app/src/main/java/org/oxycblt/auxio/list/recycler/AuxioRecyclerView.kt index eb7c820a4..1c5923b3c 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/recycler/AuxioRecyclerView.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/recycler/AuxioRecyclerView.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/list/recycler/ViewHolders.kt b/app/src/main/java/org/oxycblt/auxio/list/recycler/ViewHolders.kt index 1b575978f..0c9962996 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/recycler/ViewHolders.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/recycler/ViewHolders.kt @@ -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 = diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index 37cbf83bc..bcf2fb53e 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -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. diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt index 99e93ffb0..91ad069fb 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt @@ -118,6 +118,14 @@ interface MusicRepository { */ fun createPlaylist(name: String, songs: List) + /** + * 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) diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt index 75527b6d8..873ed851e 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt @@ -52,15 +52,20 @@ constructor( /** Flag for opening a dialog to create a playlist of the given [Song]s. */ val newPlaylistSongs: Event> = _newPlaylistSongs - private val _songsToAdd = MutableEvent>() - /** Flag for opening a dialog to add the given [Song]s to a playlist. */ - val songsToAdd: Event> = _songsToAdd + private val _playlistToRename = MutableEvent() + /** Flag for opening a dialog to rename the given [Playlist]. */ + val playlistToRename: Event + get() = _playlistToRename private val _playlistToDelete = MutableEvent() /** Flag for opening a dialog to confirm deletion of the given [Playlist]. */ val playlistToDelete: Event get() = _playlistToDelete + private val _songsToAdd = MutableEvent>() + /** Flag for opening a dialog to add the given [Song]s to a playlist. */ + val songsToAdd: Event> = _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]. * diff --git a/app/src/main/java/org/oxycblt/auxio/music/picker/AddToPlaylistDialog.kt b/app/src/main/java/org/oxycblt/auxio/music/picker/AddToPlaylistDialog.kt index 66660bb6d..1cdb8b4db 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/picker/AddToPlaylistDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/picker/AddToPlaylistDialog.kt @@ -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) { diff --git a/app/src/main/java/org/oxycblt/auxio/music/picker/DeletePlaylistDialog.kt b/app/src/main/java/org/oxycblt/auxio/music/picker/DeletePlaylistDialog.kt index cada8ed00..afc90c825 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/picker/DeletePlaylistDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/picker/DeletePlaylistDialog.kt @@ -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(null) + /** A new [Playlist] having it's name chosen by the user. Null if none yet. */ val currentPendingPlaylist: StateFlow get() = _currentPendingPlaylist + private val _currentPlaylistToRename = MutableStateFlow(null) + /** An existing [Playlist] that is being renamed. Null if none yet. */ + val currentPlaylistToRename: StateFlow + get() = _currentPlaylistToRename + + private val _currentPlaylistToDelete = MutableStateFlow(null) + /** The current [Playlist] that needs it's deletion confirmed. Null if none yet. */ + val currentPlaylistToDelete: StateFlow + get() = _currentPlaylistToDelete + private val _chosenName = MutableStateFlow(ChosenName.Empty) + /** The users chosen name for [currentPendingPlaylist] or [currentPlaylistToRename]. */ val chosenName: StateFlow get() = _chosenName private val _currentSongsToAdd = MutableStateFlow?>(null) + /** A batch of [Song]s to add to a playlist chosen by the user. Null if none yet. */ val currentSongsToAdd: StateFlow?> get() = _currentSongsToAdd - private val _playlistChoices = MutableStateFlow>(listOf()) - val playlistChoices: StateFlow> - get() = _playlistChoices - - private val _currentPlaylistToDelete = MutableStateFlow(null) - val currentPlaylistToDelete: StateFlow - get() = _currentPlaylistToDelete + private val _playlistAddChoices = MutableStateFlow>(listOf()) + /** The [Playlist]s that [currentSongsToAdd] could be added to. */ + val playlistAddChoices: StateFlow> + 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) { 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) - } } /** diff --git a/app/src/main/java/org/oxycblt/auxio/music/picker/RenamePlaylistDialog.kt b/app/src/main/java/org/oxycblt/auxio/music/picker/RenamePlaylistDialog.kt new file mode 100644 index 000000000..d3fb58323 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/music/picker/RenamePlaylistDialog.kt @@ -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 . + */ + +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() { + 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 + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/music/user/PlaylistImpl.kt b/app/src/main/java/org/oxycblt/auxio/music/user/PlaylistImpl.kt index afd89de1d..00127a846 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/user/PlaylistImpl.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/user/PlaylistImpl.kt @@ -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.() -> 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, musicSettings: MusicSettings) = PlaylistImpl( - Music.UID.auxio(MusicMode.PLAYLISTS) { update(name) }, + Music.UID.auxio(MusicMode.PLAYLISTS), Name.Known.from(name, null, musicSettings), songs) diff --git a/app/src/main/java/org/oxycblt/auxio/music/user/UserLibrary.kt b/app/src/main/java/org/oxycblt/auxio/music/user/UserLibrary.kt index 7ea2c05b5..563f99316 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/user/UserLibrary.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/user/UserLibrary.kt @@ -80,6 +80,14 @@ interface MutableUserLibrary : UserLibrary { */ fun createPlaylist(name: String, songs: List) + /** + * 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) diff --git a/app/src/main/res/menu/menu_playlist_actions.xml b/app/src/main/res/menu/menu_playlist_actions.xml index 4f852e49f..395ec387b 100644 --- a/app/src/main/res/menu/menu_playlist_actions.xml +++ b/app/src/main/res/menu/menu_playlist_actions.xml @@ -12,6 +12,9 @@ + diff --git a/app/src/main/res/menu/menu_playlist_detail.xml b/app/src/main/res/menu/menu_playlist_detail.xml index c3e30e8b9..05a11b388 100644 --- a/app/src/main/res/menu/menu_playlist_detail.xml +++ b/app/src/main/res/menu/menu_playlist_detail.xml @@ -6,6 +6,9 @@ + diff --git a/app/src/main/res/navigation/nav_main.xml b/app/src/main/res/navigation/nav_main.xml index a11e936ac..54a1a4f37 100644 --- a/app/src/main/res/navigation/nav_main.xml +++ b/app/src/main/res/navigation/nav_main.xml @@ -21,11 +21,14 @@ android:id="@+id/action_new_playlist" app:destination="@id/new_playlist_dialog" /> + android:id="@+id/action_rename_playlist" + app:destination="@id/rename_playlist_dialog" /> + @@ -58,16 +61,13 @@ - + android:name="playlistUid" + app:argType="org.oxycblt.auxio.music.Music$UID" /> + + + + + Playlist Playlists New playlist + Rename + Rename playlist Delete Delete playlist? @@ -165,6 +167,8 @@ Monitoring your music library for changes… Added to queue Playlist created + Playlist renamed + Playlist deleted Added to playlist Developed by Alexander Capehart