music: add playlist deletion dialog
Add a dialog that allows the user to confirm playlist deletion, instead of it happening immediately.
This commit is contained in:
parent
d1f9200bf9
commit
97e144058a
13 changed files with 180 additions and 41 deletions
|
@ -41,6 +41,7 @@ import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
|
import org.oxycblt.auxio.music.Playlist
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.navigation.MainNavigationAction
|
import org.oxycblt.auxio.navigation.MainNavigationAction
|
||||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
|
@ -136,6 +137,7 @@ class MainFragment :
|
||||||
collect(navModel.exploreArtistNavigationItem.flow, ::handleArtistNavigationPicker)
|
collect(navModel.exploreArtistNavigationItem.flow, ::handleArtistNavigationPicker)
|
||||||
collect(musicModel.newPlaylistSongs.flow, ::handleNewPlaylist)
|
collect(musicModel.newPlaylistSongs.flow, ::handleNewPlaylist)
|
||||||
collect(musicModel.songsToAdd.flow, ::handleAddToPlaylist)
|
collect(musicModel.songsToAdd.flow, ::handleAddToPlaylist)
|
||||||
|
collect(musicModel.playlistToDelete.flow, ::handleDeletePlaylist)
|
||||||
collectImmediately(playbackModel.song, ::updateSong)
|
collectImmediately(playbackModel.song, ::updateSong)
|
||||||
collect(playbackModel.artistPickerSong.flow, ::handlePlaybackArtistPicker)
|
collect(playbackModel.artistPickerSong.flow, ::handlePlaybackArtistPicker)
|
||||||
collect(playbackModel.genrePickerSong.flow, ::handlePlaybackGenrePicker)
|
collect(playbackModel.genrePickerSong.flow, ::handlePlaybackGenrePicker)
|
||||||
|
@ -322,6 +324,13 @@ class MainFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleDeletePlaylist(playlist: Playlist?) {
|
||||||
|
if (playlist != null) {
|
||||||
|
findNavController()
|
||||||
|
.navigateSafe(MainFragmentDirections.actionDeletePlaylist(playlist.uid))
|
||||||
|
musicModel.playlistToDelete.consume()
|
||||||
|
}
|
||||||
|
}
|
||||||
private fun handlePlaybackArtistPicker(song: Song?) {
|
private fun handlePlaybackArtistPicker(song: Song?) {
|
||||||
if (song != null) {
|
if (song != null) {
|
||||||
navModel.mainNavigateTo(
|
navModel.mainNavigateTo(
|
||||||
|
|
|
@ -99,7 +99,7 @@ class AlbumDetailFragment :
|
||||||
|
|
||||||
// -- VIEWMODEL SETUP ---
|
// -- VIEWMODEL SETUP ---
|
||||||
// DetailViewModel handles most initialization from the navigation argument.
|
// DetailViewModel handles most initialization from the navigation argument.
|
||||||
detailModel.setAlbumUid(args.albumUid)
|
detailModel.setAlbum(args.albumUid)
|
||||||
collectImmediately(detailModel.currentAlbum, ::updateAlbum)
|
collectImmediately(detailModel.currentAlbum, ::updateAlbum)
|
||||||
collectImmediately(detailModel.albumList, ::updateList)
|
collectImmediately(detailModel.albumList, ::updateList)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
|
|
|
@ -98,7 +98,7 @@ class ArtistDetailFragment :
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
// DetailViewModel handles most initialization from the navigation argument.
|
// DetailViewModel handles most initialization from the navigation argument.
|
||||||
detailModel.setArtistUid(args.artistUid)
|
detailModel.setArtist(args.artistUid)
|
||||||
collectImmediately(detailModel.currentArtist, ::updateArtist)
|
collectImmediately(detailModel.currentArtist, ::updateArtist)
|
||||||
collectImmediately(detailModel.artistList, ::updateList)
|
collectImmediately(detailModel.artistList, ::updateList)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
|
|
|
@ -224,47 +224,47 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a new [currentSong] from it's [Music.UID]. If the [Music.UID] differs, [currentSong] and
|
* Set a new [currentSong] from it's [Music.UID]. [currentSong] and [songAudioProperties] will
|
||||||
* [songAudioProperties] will be updated to align with the new [Song].
|
* be updated to align with the new [Song].
|
||||||
*
|
*
|
||||||
* @param uid The UID of the [Song] to load. Must be valid.
|
* @param uid The UID of the [Song] to load. Must be valid.
|
||||||
*/
|
*/
|
||||||
fun setSongUid(uid: Music.UID) {
|
fun setSong(uid: Music.UID) {
|
||||||
logD("Opening Song [uid: $uid]")
|
logD("Opening Song [uid: $uid]")
|
||||||
_currentSong.value = musicRepository.deviceLibrary?.findSong(uid)?.also(::refreshAudioInfo)
|
_currentSong.value = musicRepository.deviceLibrary?.findSong(uid)?.also(::refreshAudioInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a new [currentAlbum] from it's [Music.UID]. If the [Music.UID] differs, [currentAlbum]
|
* Set a new [currentAlbum] from it's [Music.UID]. [currentAlbum] and [albumList] will be
|
||||||
* and [albumList] will be updated to align with the new [Album].
|
* updated to align with the new [Album].
|
||||||
*
|
*
|
||||||
* @param uid The [Music.UID] of the [Album] to update [currentAlbum] to. Must be valid.
|
* @param uid The [Music.UID] of the [Album] to update [currentAlbum] to. Must be valid.
|
||||||
*/
|
*/
|
||||||
fun setAlbumUid(uid: Music.UID) {
|
fun setAlbum(uid: Music.UID) {
|
||||||
logD("Opening Album [uid: $uid]")
|
logD("Opening Album [uid: $uid]")
|
||||||
_currentAlbum.value =
|
_currentAlbum.value =
|
||||||
musicRepository.deviceLibrary?.findAlbum(uid)?.also(::refreshAlbumList)
|
musicRepository.deviceLibrary?.findAlbum(uid)?.also(::refreshAlbumList)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a new [currentArtist] from it's [Music.UID]. If the [Music.UID] differs, [currentArtist]
|
* Set a new [currentArtist] from it's [Music.UID]. [currentArtist] and [artistList] will be
|
||||||
* and [artistList] will be updated to align with the new [Artist].
|
* updated to align with the new [Artist].
|
||||||
*
|
*
|
||||||
* @param uid The [Music.UID] of the [Artist] to update [currentArtist] to. Must be valid.
|
* @param uid The [Music.UID] of the [Artist] to update [currentArtist] to. Must be valid.
|
||||||
*/
|
*/
|
||||||
fun setArtistUid(uid: Music.UID) {
|
fun setArtist(uid: Music.UID) {
|
||||||
logD("Opening Artist [uid: $uid]")
|
logD("Opening Artist [uid: $uid]")
|
||||||
_currentArtist.value =
|
_currentArtist.value =
|
||||||
musicRepository.deviceLibrary?.findArtist(uid)?.also(::refreshArtistList)
|
musicRepository.deviceLibrary?.findArtist(uid)?.also(::refreshArtistList)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a new [currentGenre] from it's [Music.UID]. If the [Music.UID] differs, [currentGenre]
|
* Set a new [currentGenre] from it's [Music.UID]. [currentGenre] and [genreList] will be
|
||||||
* and [genreList] will be updated to align with the new album.
|
* updated to align with the new album.
|
||||||
*
|
*
|
||||||
* @param uid The [Music.UID] of the [Genre] to update [currentGenre] to. Must be valid.
|
* @param uid The [Music.UID] of the [Genre] to update [currentGenre] to. Must be valid.
|
||||||
*/
|
*/
|
||||||
fun setGenreUid(uid: Music.UID) {
|
fun setGenre(uid: Music.UID) {
|
||||||
logD("Opening Genre [uid: $uid]")
|
logD("Opening Genre [uid: $uid]")
|
||||||
_currentGenre.value =
|
_currentGenre.value =
|
||||||
musicRepository.deviceLibrary?.findGenre(uid)?.also(::refreshGenreList)
|
musicRepository.deviceLibrary?.findGenre(uid)?.also(::refreshGenreList)
|
||||||
|
|
|
@ -91,7 +91,7 @@ class GenreDetailFragment :
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
// DetailViewModel handles most initialization from the navigation argument.
|
// DetailViewModel handles most initialization from the navigation argument.
|
||||||
detailModel.setGenreUid(args.genreUid)
|
detailModel.setGenre(args.genreUid)
|
||||||
collectImmediately(detailModel.currentGenre, ::updatePlaylist)
|
collectImmediately(detailModel.currentGenre, ::updatePlaylist)
|
||||||
collectImmediately(detailModel.genreList, ::updateList)
|
collectImmediately(detailModel.genreList, ::updateList)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
|
|
|
@ -67,7 +67,7 @@ class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
|
||||||
super.onBindingCreated(binding, savedInstanceState)
|
super.onBindingCreated(binding, savedInstanceState)
|
||||||
binding.detailProperties.adapter = detailAdapter
|
binding.detailProperties.adapter = detailAdapter
|
||||||
// DetailViewModel handles most initialization from the navigation argument.
|
// DetailViewModel handles most initialization from the navigation argument.
|
||||||
detailModel.setSongUid(args.songUid)
|
detailModel.setSong(args.songUid)
|
||||||
collectImmediately(detailModel.currentSong, detailModel.songAudioProperties, ::updateSong)
|
collectImmediately(detailModel.currentSong, detailModel.songAudioProperties, ::updateSong)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,13 +48,18 @@ constructor(
|
||||||
val statistics: StateFlow<Statistics?>
|
val statistics: StateFlow<Statistics?>
|
||||||
get() = _statistics
|
get() = _statistics
|
||||||
|
|
||||||
private val _newPlaylistSongs = MutableEvent<List<Song>?>()
|
private val _newPlaylistSongs = MutableEvent<List<Song>>()
|
||||||
/** Flag for opening a dialog to create a playlist of the given [Song]s. */
|
/** Flag for opening a dialog to create a playlist of the given [Song]s. */
|
||||||
val newPlaylistSongs: Event<List<Song>?> = _newPlaylistSongs
|
val newPlaylistSongs: Event<List<Song>> = _newPlaylistSongs
|
||||||
|
|
||||||
private val _songsToAdd = MutableEvent<List<Song>?>()
|
private val _songsToAdd = MutableEvent<List<Song>>()
|
||||||
/** Flag for opening a dialog to add the given [Song]s to a playlist. */
|
/** Flag for opening a dialog to add the given [Song]s to a playlist. */
|
||||||
val songsToAdd: Event<List<Song>?> = _songsToAdd
|
val songsToAdd: Event<List<Song>> = _songsToAdd
|
||||||
|
|
||||||
|
private val _playlistToDelete = MutableEvent<Playlist>()
|
||||||
|
/** Flag for opening a dialog to confirm deletion of the given [Playlist]. */
|
||||||
|
val playlistToDelete: Event<Playlist>
|
||||||
|
get() = _playlistToDelete
|
||||||
|
|
||||||
init {
|
init {
|
||||||
musicRepository.addUpdateListener(this)
|
musicRepository.addUpdateListener(this)
|
||||||
|
@ -110,11 +115,15 @@ constructor(
|
||||||
* Delete a [Playlist].
|
* Delete a [Playlist].
|
||||||
*
|
*
|
||||||
* @param playlist The playlist to delete.
|
* @param playlist The playlist to delete.
|
||||||
*
|
* @param rude Whether to immediately delete the playlist or prompt the user first. This should
|
||||||
* TODO: Prompt the user before deleting.
|
* be false at almost all times.
|
||||||
*/
|
*/
|
||||||
fun deletePlaylist(playlist: Playlist) {
|
fun deletePlaylist(playlist: Playlist, rude: Boolean = false) {
|
||||||
musicRepository.deletePlaylist(playlist)
|
if (rude) {
|
||||||
|
musicRepository.deletePlaylist(playlist)
|
||||||
|
} else {
|
||||||
|
_playlistToDelete.put(playlist)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -71,8 +71,8 @@ class AddToPlaylistDialog :
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
pickerModel.setPendingSongs(args.songUids)
|
pickerModel.setSongsToAdd(args.songUids)
|
||||||
collectImmediately(pickerModel.currentPendingSongs, ::updatePendingSongs)
|
collectImmediately(pickerModel.currentSongsToAdd, ::updatePendingSongs)
|
||||||
collectImmediately(pickerModel.playlistChoices, ::updatePlaylistChoices)
|
collectImmediately(pickerModel.playlistChoices, ::updatePlaylistChoices)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,13 +82,13 @@ class AddToPlaylistDialog :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: PlaylistChoice, viewHolder: RecyclerView.ViewHolder) {
|
override fun onClick(item: PlaylistChoice, viewHolder: RecyclerView.ViewHolder) {
|
||||||
musicModel.addToPlaylist(pickerModel.currentPendingSongs.value ?: return, item.playlist)
|
musicModel.addToPlaylist(pickerModel.currentSongsToAdd.value ?: return, item.playlist)
|
||||||
requireContext().showToast(R.string.lng_playlist_added)
|
requireContext().showToast(R.string.lng_playlist_added)
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewPlaylist() {
|
override fun onNewPlaylist() {
|
||||||
musicModel.createPlaylist(songs = pickerModel.currentPendingSongs.value ?: return)
|
musicModel.createPlaylist(songs = pickerModel.currentSongsToAdd.value ?: return)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePendingSongs(songs: List<Song>?) {
|
private fun updatePendingSongs(songs: List<Song>?) {
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
* DeletePlaylistDialog.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.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.DialogDeletePlaylistBinding
|
||||||
|
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.unlikelyToBeNull
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ViewBindingDialogFragment] that asks the user to confirm the deletion of a [Playlist].
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class DeletePlaylistDialog : ViewBindingDialogFragment<DialogDeletePlaylistBinding>() {
|
||||||
|
private val pickerModel: PlaylistPickerViewModel by viewModels()
|
||||||
|
private val musicModel: MusicViewModel by activityViewModels()
|
||||||
|
// 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: DeletePlaylistDialogArgs by navArgs()
|
||||||
|
|
||||||
|
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
||||||
|
builder
|
||||||
|
.setTitle(R.string.lbl_confirm_delete_playlist)
|
||||||
|
.setPositiveButton(R.string.lbl_delete) { _, _ ->
|
||||||
|
// Now we can delete the playlist for-real this time.
|
||||||
|
musicModel.deletePlaylist(
|
||||||
|
unlikelyToBeNull(pickerModel.currentPlaylistToDelete.value), rude = true)
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.lbl_cancel, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||||
|
DialogDeletePlaylistBinding.inflate(inflater)
|
||||||
|
|
||||||
|
override fun onBindingCreated(
|
||||||
|
binding: DialogDeletePlaylistBinding,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
) {
|
||||||
|
super.onBindingCreated(binding, savedInstanceState)
|
||||||
|
|
||||||
|
// --- VIEWMODEL SETUP ---
|
||||||
|
pickerModel.setPlaylistToDelete(args.playlistUid)
|
||||||
|
collectImmediately(pickerModel.currentPlaylistToDelete, ::updatePlaylistToDelete)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePlaylistToDelete(playlist: Playlist?) {
|
||||||
|
if (playlist == null) {
|
||||||
|
// Playlist does not exist anymore, leave
|
||||||
|
findNavController().navigateUp()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
requireBinding().deletionInfo.text =
|
||||||
|
getString(R.string.fmt_deletion_info, playlist.name.resolve(requireContext()))
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,14 +48,18 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
val chosenName: StateFlow<ChosenName>
|
val chosenName: StateFlow<ChosenName>
|
||||||
get() = _chosenName
|
get() = _chosenName
|
||||||
|
|
||||||
private val _currentPendingSongs = MutableStateFlow<List<Song>?>(null)
|
private val _currentSongsToAdd = MutableStateFlow<List<Song>?>(null)
|
||||||
val currentPendingSongs: StateFlow<List<Song>?>
|
val currentSongsToAdd: StateFlow<List<Song>?>
|
||||||
get() = _currentPendingSongs
|
get() = _currentSongsToAdd
|
||||||
|
|
||||||
private val _playlistChoices = MutableStateFlow<List<PlaylistChoice>>(listOf())
|
private val _playlistChoices = MutableStateFlow<List<PlaylistChoice>>(listOf())
|
||||||
val playlistChoices: StateFlow<List<PlaylistChoice>>
|
val playlistChoices: StateFlow<List<PlaylistChoice>>
|
||||||
get() = _playlistChoices
|
get() = _playlistChoices
|
||||||
|
|
||||||
|
private val _currentPlaylistToDelete = MutableStateFlow<Playlist?>(null)
|
||||||
|
val currentPlaylistToDelete: StateFlow<Playlist?>
|
||||||
|
get() = _currentPlaylistToDelete
|
||||||
|
|
||||||
init {
|
init {
|
||||||
musicRepository.addUpdateListener(this)
|
musicRepository.addUpdateListener(this)
|
||||||
}
|
}
|
||||||
|
@ -70,8 +74,8 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
pendingPlaylist.preferredName,
|
pendingPlaylist.preferredName,
|
||||||
pendingPlaylist.songs.mapNotNull { deviceLibrary.findSong(it.uid) })
|
pendingPlaylist.songs.mapNotNull { deviceLibrary.findSong(it.uid) })
|
||||||
}
|
}
|
||||||
_currentPendingSongs.value =
|
_currentSongsToAdd.value =
|
||||||
_currentPendingSongs.value?.let { pendingSongs ->
|
_currentSongsToAdd.value?.let { pendingSongs ->
|
||||||
pendingSongs
|
pendingSongs
|
||||||
.mapNotNull { deviceLibrary.findSong(it.uid) }
|
.mapNotNull { deviceLibrary.findSong(it.uid) }
|
||||||
.ifEmpty { null }
|
.ifEmpty { null }
|
||||||
|
@ -88,7 +92,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshChoicesWith = refreshChoicesWith ?: _currentPendingSongs.value
|
refreshChoicesWith = refreshChoicesWith ?: _currentSongsToAdd.value
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshChoicesWith?.let(::refreshPlaylistChoices)
|
refreshChoicesWith?.let(::refreshPlaylistChoices)
|
||||||
|
@ -99,7 +103,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current [PendingPlaylist]. Will do nothing if already equal.
|
* Set a new [currentPendingPlaylist] from a new batch of pending [Song] [Music.UID]s.
|
||||||
*
|
*
|
||||||
* @param context [Context] required to generate a playlist name.
|
* @param context [Context] required to generate a playlist name.
|
||||||
* @param songUids The [Music.UID]s of songs to be present in the playlist.
|
* @param songUids The [Music.UID]s of songs to be present in the playlist.
|
||||||
|
@ -121,7 +125,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current [ChosenName] based on new user input.
|
* Update the current [chosenName] based on new user input.
|
||||||
*
|
*
|
||||||
* @param name The new user-inputted name, or null if not present.
|
* @param name The new user-inputted name, or null if not present.
|
||||||
*/
|
*/
|
||||||
|
@ -143,16 +147,14 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current [Song]s that to show playlist add choices for. Will do nothing if already
|
* Set a new [currentSongsToAdd] from a new batch of pending [Song] [Music.UID]s.
|
||||||
* equal.
|
|
||||||
*
|
*
|
||||||
* @param songUids The [Music.UID]s of songs to add to a playlist.
|
* @param songUids The [Music.UID]s of songs to add to a playlist.
|
||||||
*/
|
*/
|
||||||
fun setPendingSongs(songUids: Array<Music.UID>) {
|
fun setSongsToAdd(songUids: Array<Music.UID>) {
|
||||||
if (currentPendingSongs.value?.map { it.uid } == songUids) return
|
|
||||||
val deviceLibrary = musicRepository.deviceLibrary ?: return
|
val deviceLibrary = musicRepository.deviceLibrary ?: return
|
||||||
val songs = songUids.mapNotNull(deviceLibrary::findSong)
|
val songs = songUids.mapNotNull(deviceLibrary::findSong)
|
||||||
_currentPendingSongs.value = songs
|
_currentSongsToAdd.value = songs
|
||||||
refreshPlaylistChoices(songs)
|
refreshPlaylistChoices(songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +166,15 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
PlaylistChoice(it, songs.all(songSet::contains))
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
11
app/src/main/res/layout/dialog_delete_playlist.xml
Normal file
11
app/src/main/res/layout/dialog_delete_playlist.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/deletion_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:paddingTop="@dimen/spacing_medium"
|
||||||
|
android:paddingEnd="@dimen/spacing_large"
|
||||||
|
android:paddingStart="@dimen/spacing_large"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:textAppearance="@style/TextAppearance.Auxio.BodyLarge"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:text="Delete Playlist 16? This cannot be undone."/>
|
|
@ -23,6 +23,9 @@
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_add_to_playlist"
|
android:id="@+id/action_add_to_playlist"
|
||||||
app:destination="@id/add_to_playlist_dialog" />
|
app:destination="@id/add_to_playlist_dialog" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_delete_playlist"
|
||||||
|
app:destination="@id/delete_playlist_dialog" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_pick_navigation_artist"
|
android:id="@+id/action_pick_navigation_artist"
|
||||||
app:destination="@id/navigate_to_artist_dialog" />
|
app:destination="@id/navigate_to_artist_dialog" />
|
||||||
|
@ -67,6 +70,16 @@
|
||||||
app:destination="@id/new_playlist_dialog" />
|
app:destination="@id/new_playlist_dialog" />
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog
|
||||||
|
android:id="@+id/delete_playlist_dialog"
|
||||||
|
android:name="org.oxycblt.auxio.music.picker.DeletePlaylistDialog"
|
||||||
|
android:label="delete_playlist_dialog"
|
||||||
|
tools:layout="@layout/dialog_playlist_name">
|
||||||
|
<argument
|
||||||
|
android:name="playlistUid"
|
||||||
|
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<dialog
|
<dialog
|
||||||
android:id="@+id/navigate_to_artist_dialog"
|
android:id="@+id/navigate_to_artist_dialog"
|
||||||
android:name="org.oxycblt.auxio.navigation.picker.NavigateToArtistDialog"
|
android:name="org.oxycblt.auxio.navigation.picker.NavigateToArtistDialog"
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
<string name="lbl_playlists">Playlists</string>
|
<string name="lbl_playlists">Playlists</string>
|
||||||
<string name="lbl_new_playlist">New playlist</string>
|
<string name="lbl_new_playlist">New playlist</string>
|
||||||
<string name="lbl_delete">Delete</string>
|
<string name="lbl_delete">Delete</string>
|
||||||
|
<string name="lbl_confirm_delete_playlist">Delete playlist?</string>
|
||||||
|
|
||||||
<!-- Search for music -->
|
<!-- Search for music -->
|
||||||
<string name="lbl_search">Search</string>
|
<string name="lbl_search">Search</string>
|
||||||
|
@ -395,6 +396,7 @@
|
||||||
<string name="fmt_sample_rate">%d Hz</string>
|
<string name="fmt_sample_rate">%d Hz</string>
|
||||||
|
|
||||||
<string name="fmt_indexing">Loading your music library… (%1$d/%2$d)</string>
|
<string name="fmt_indexing">Loading your music library… (%1$d/%2$d)</string>
|
||||||
|
<string name="fmt_deletion_info">Delete %s? This cannot be undone.</string>
|
||||||
|
|
||||||
<string name="fmt_lib_song_count">Songs loaded: %d</string>
|
<string name="fmt_lib_song_count">Songs loaded: %d</string>
|
||||||
<string name="fmt_lib_album_count">Albums loaded: %d</string>
|
<string name="fmt_lib_album_count">Albums loaded: %d</string>
|
||||||
|
|
Loading…
Reference in a new issue