music: add more playlist messages

Add more types of playlist messages corresponding to other actions, so
they can be indicated in the UI only when the process is complete.

This is somewhat incomplete. It does not include indicating errors for
other playlist operations (Which I want to do), and neither does it
handle situations in which some playlist operations and up reducing
to others (i.e import -> create). I need to do that later.
This commit is contained in:
Alexander Capehart 2023-12-23 22:00:38 -07:00
parent 21970349cc
commit c5a3f72b99
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
13 changed files with 128 additions and 54 deletions

View file

@ -44,6 +44,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.PlaylistMessage
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.info.Disc import org.oxycblt.auxio.music.info.Disc
import org.oxycblt.auxio.playback.PlaybackDecision import org.oxycblt.auxio.playback.PlaybackDecision
@ -55,6 +56,7 @@ import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.overrideOnOverflowMenuClick import org.oxycblt.auxio.util.overrideOnOverflowMenuClick
import org.oxycblt.auxio.util.setFullWidthLookup import org.oxycblt.auxio.util.setFullWidthLookup
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
/** /**
@ -126,6 +128,7 @@ class AlbumDetailFragment :
collect(listModel.menu.flow, ::handleMenu) collect(listModel.menu.flow, ::handleMenu)
collectImmediately(listModel.selected, ::updateSelection) collectImmediately(listModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision) collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
collect(musicModel.playlistMessage.flow, ::handlePlaylistMessage)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision) collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
@ -281,6 +284,12 @@ class AlbumDetailFragment :
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
private fun handlePlaylistMessage(message: PlaylistMessage?) {
if (message == null) return
requireContext().showToast(message.stringRes)
musicModel.playlistMessage.consume()
}
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
albumListAdapter.setPlaying( albumListAdapter.setPlaying(
song.takeIf { parent == detailModel.currentAlbum.value }, isPlaying) song.takeIf { parent == detailModel.currentAlbum.value }, isPlaying)

View file

@ -45,6 +45,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.PlaylistMessage
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackDecision import org.oxycblt.auxio.playback.PlaybackDecision
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
@ -54,6 +55,7 @@ import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.overrideOnOverflowMenuClick import org.oxycblt.auxio.util.overrideOnOverflowMenuClick
import org.oxycblt.auxio.util.setFullWidthLookup import org.oxycblt.auxio.util.setFullWidthLookup
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
/** /**
@ -128,6 +130,7 @@ class ArtistDetailFragment :
collect(listModel.menu.flow, ::handleMenu) collect(listModel.menu.flow, ::handleMenu)
collectImmediately(listModel.selected, ::updateSelection) collectImmediately(listModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision) collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
collect(musicModel.playlistMessage.flow, ::handlePlaylistMessage)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision) collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
@ -284,6 +287,12 @@ class ArtistDetailFragment :
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
private fun handlePlaylistMessage(message: PlaylistMessage?) {
if (message == null) return
requireContext().showToast(message.stringRes)
musicModel.playlistMessage.consume()
}
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
val currentArtist = unlikelyToBeNull(detailModel.currentArtist.value) val currentArtist = unlikelyToBeNull(detailModel.currentArtist.value)
val playingItem = val playingItem =

View file

@ -45,6 +45,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.PlaylistMessage
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackDecision import org.oxycblt.auxio.playback.PlaybackDecision
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
@ -54,6 +55,7 @@ import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.overrideOnOverflowMenuClick import org.oxycblt.auxio.util.overrideOnOverflowMenuClick
import org.oxycblt.auxio.util.setFullWidthLookup import org.oxycblt.auxio.util.setFullWidthLookup
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
/** /**
@ -125,7 +127,8 @@ class GenreDetailFragment :
collect(detailModel.toShow.flow, ::handleShow) collect(detailModel.toShow.flow, ::handleShow)
collect(listModel.menu.flow, ::handleMenu) collect(listModel.menu.flow, ::handleMenu)
collectImmediately(listModel.selected, ::updateSelection) collectImmediately(listModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handleDecision) collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
collect(musicModel.playlistMessage.flow, ::handlePlaylistMessage)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision) collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
@ -259,7 +262,7 @@ class GenreDetailFragment :
} }
} }
private fun handleDecision(decision: PlaylistDecision?) { private fun handlePlaylistDecision(decision: PlaylistDecision?) {
if (decision == null) return if (decision == null) return
val directions = val directions =
when (decision) { when (decision) {
@ -277,6 +280,12 @@ class GenreDetailFragment :
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
private fun handlePlaylistMessage(message: PlaylistMessage?) {
if (message == null) return
requireContext().showToast(message.stringRes)
musicModel.playlistMessage.consume()
}
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
val currentGenre = unlikelyToBeNull(detailModel.currentGenre.value) val currentGenre = unlikelyToBeNull(detailModel.currentGenre.value)
val playingItem = val playingItem =

View file

@ -49,6 +49,7 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.PlaylistMessage
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.external.M3U import org.oxycblt.auxio.music.external.M3U
import org.oxycblt.auxio.playback.PlaybackDecision import org.oxycblt.auxio.playback.PlaybackDecision
@ -61,6 +62,7 @@ import org.oxycblt.auxio.util.logW
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.overrideOnOverflowMenuClick import org.oxycblt.auxio.util.overrideOnOverflowMenuClick
import org.oxycblt.auxio.util.setFullWidthLookup import org.oxycblt.auxio.util.setFullWidthLookup
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
/** /**
@ -159,7 +161,8 @@ class PlaylistDetailFragment :
collect(detailModel.toShow.flow, ::handleShow) collect(detailModel.toShow.flow, ::handleShow)
collect(listModel.menu.flow, ::handleMenu) collect(listModel.menu.flow, ::handleMenu)
collectImmediately(listModel.selected, ::updateSelection) collectImmediately(listModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handleDecision) collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
collect(musicModel.playlistMessage.flow, ::handlePlaylistMessage)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision) collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
@ -333,7 +336,7 @@ class PlaylistDetailFragment :
updateMultiToolbar() updateMultiToolbar()
} }
private fun handleDecision(decision: PlaylistDecision?) { private fun handlePlaylistDecision(decision: PlaylistDecision?) {
if (decision == null) return if (decision == null) return
val directions = val directions =
when (decision) { when (decision) {
@ -369,6 +372,12 @@ class PlaylistDetailFragment :
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
private fun handlePlaylistMessage(message: PlaylistMessage?) {
if (message == null) return
requireContext().showToast(message.stringRes)
musicModel.playlistMessage.consume()
}
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
// Prefer songs that are playing from this playlist. // Prefer songs that are playing from this playlist.
playlistListAdapter.setPlaying( playlistListAdapter.setPlaying(

View file

@ -70,7 +70,7 @@ import org.oxycblt.auxio.music.NoMusicException
import org.oxycblt.auxio.music.PERMISSION_READ_AUDIO import org.oxycblt.auxio.music.PERMISSION_READ_AUDIO
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.PlaylistError import org.oxycblt.auxio.music.PlaylistMessage
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.external.M3U import org.oxycblt.auxio.music.external.M3U
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
@ -211,7 +211,7 @@ class HomeFragment :
collectImmediately(listModel.selected, ::updateSelection) collectImmediately(listModel.selected, ::updateSelection)
collectImmediately(musicModel.indexingState, ::updateIndexerState) collectImmediately(musicModel.indexingState, ::updateIndexerState)
collect(musicModel.playlistDecision.flow, ::handleDecision) collect(musicModel.playlistDecision.flow, ::handleDecision)
collectImmediately(musicModel.playlistError.flow, ::handlePlaylistError) collectImmediately(musicModel.playlistMessage.flow, ::handlePlaylistMessage)
collect(detailModel.toShow.flow, ::handleShow) collect(detailModel.toShow.flow, ::handleShow)
} }
@ -503,19 +503,10 @@ class HomeFragment :
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
private fun handlePlaylistError(error: PlaylistError?) { private fun handlePlaylistMessage(message: PlaylistMessage?) {
when (error) { if (message == null) return
is PlaylistError.ImportFailed -> { requireContext().showToast(message.stringRes)
requireContext().showToast(R.string.err_import_failed) musicModel.playlistMessage.consume()
musicModel.importError.consume()
}
is PlaylistError.ExportFailed -> {
requireContext().showToast(R.string.err_export_failed)
musicModel.importError.consume()
}
null -> {}
}
musicModel.playlistError.consume()
} }
private fun updateFab(songs: List<Song>, isFastScrolling: Boolean) { private fun updateFab(songs: List<Song>, isFastScrolling: Boolean) {

View file

@ -27,6 +27,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.oxycblt.auxio.R
import org.oxycblt.auxio.list.ListSettings import org.oxycblt.auxio.list.ListSettings
import org.oxycblt.auxio.music.external.ExportConfig import org.oxycblt.auxio.music.external.ExportConfig
import org.oxycblt.auxio.music.external.ExternalPlaylistManager import org.oxycblt.auxio.music.external.ExternalPlaylistManager
@ -65,19 +66,9 @@ constructor(
val playlistDecision: Event<PlaylistDecision> val playlistDecision: Event<PlaylistDecision>
get() = _playlistDecision get() = _playlistDecision
private val _playlistError = MutableEvent<PlaylistError>() private val _playlistMessage = MutableEvent<PlaylistMessage>()
val playlistError: Event<PlaylistError> val playlistMessage: Event<PlaylistMessage>
get() = _playlistError get() = _playlistMessage
private val _importError = MutableEvent<Unit>()
/** Flag for when playlist importing failed. Consume this and show an error if active. */
val importError: Event<Unit>
get() = _importError
private val _exportError = MutableEvent<Unit>()
/** Flag for when playlist exporting failed. Consume this and show an error if active. */
val exportError: Event<Unit>
get() = _exportError
init { init {
musicRepository.addUpdateListener(this) musicRepository.addUpdateListener(this)
@ -127,7 +118,10 @@ constructor(
fun createPlaylist(name: String? = null, songs: List<Song> = listOf()) { fun createPlaylist(name: String? = null, songs: List<Song> = listOf()) {
if (name != null) { if (name != null) {
logD("Creating $name with ${songs.size} songs]") logD("Creating $name with ${songs.size} songs]")
viewModelScope.launch(Dispatchers.IO) { musicRepository.createPlaylist(name, songs) } viewModelScope.launch(Dispatchers.IO) {
musicRepository.createPlaylist(name, songs)
_playlistMessage.put(PlaylistMessage.NewPlaylistSuccess)
}
} else { } else {
logD("Launching creation dialog for ${songs.size} songs") logD("Launching creation dialog for ${songs.size} songs")
_playlistDecision.put(PlaylistDecision.New(songs)) _playlistDecision.put(PlaylistDecision.New(songs))
@ -148,7 +142,7 @@ constructor(
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val importedPlaylist = externalPlaylistManager.import(uri) val importedPlaylist = externalPlaylistManager.import(uri)
if (importedPlaylist == null) { if (importedPlaylist == null) {
_playlistError.put(PlaylistError.ImportFailed) _playlistMessage.put(PlaylistMessage.ImportFailed)
return@launch return@launch
} }
@ -156,14 +150,16 @@ constructor(
val songs = importedPlaylist.paths.mapNotNull(deviceLibrary::findSongByPath) val songs = importedPlaylist.paths.mapNotNull(deviceLibrary::findSongByPath)
if (songs.isEmpty()) { if (songs.isEmpty()) {
_playlistError.put(PlaylistError.ImportFailed) _playlistMessage.put(PlaylistMessage.ImportFailed)
return@launch return@launch
} }
// TODO Require the user to name it something else if the name is a duplicate of // TODO Require the user to name it something else if the name is a duplicate of
// a prior playlist // a prior playlist
if (target !== null) { if (target !== null) {
musicRepository.rewritePlaylist(target, songs) musicRepository.rewritePlaylist(target, songs)
_playlistMessage.put(PlaylistMessage.ImportSuccess)
} else { } else {
// TODO: Have to properly propagate the "Playlist Created" message
createPlaylist(importedPlaylist.name, songs) createPlaylist(importedPlaylist.name, songs)
} }
} }
@ -183,8 +179,10 @@ constructor(
if (uri != null && config != null) { if (uri != null && config != null) {
logD("Exporting playlist to $uri") logD("Exporting playlist to $uri")
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
if (!externalPlaylistManager.export(playlist, uri, config)) { if (externalPlaylistManager.export(playlist, uri, config)) {
_playlistError.put(PlaylistError.ExportFailed) _playlistMessage.put(PlaylistMessage.ExportSuccess)
} else {
_playlistMessage.put(PlaylistMessage.ExportFailed)
} }
} }
} else { } else {
@ -202,7 +200,10 @@ constructor(
fun renamePlaylist(playlist: Playlist, name: String? = null) { fun renamePlaylist(playlist: Playlist, name: String? = null) {
if (name != null) { if (name != null) {
logD("Renaming $playlist to $name") logD("Renaming $playlist to $name")
viewModelScope.launch(Dispatchers.IO) { musicRepository.renamePlaylist(playlist, name) } viewModelScope.launch(Dispatchers.IO) {
musicRepository.renamePlaylist(playlist, name)
_playlistMessage.put(PlaylistMessage.RenameSuccess)
}
} else { } else {
logD("Launching rename dialog for $playlist") logD("Launching rename dialog for $playlist")
_playlistDecision.put(PlaylistDecision.Rename(playlist)) _playlistDecision.put(PlaylistDecision.Rename(playlist))
@ -219,7 +220,10 @@ constructor(
fun deletePlaylist(playlist: Playlist, rude: Boolean = false) { fun deletePlaylist(playlist: Playlist, rude: Boolean = false) {
if (rude) { if (rude) {
logD("Deleting $playlist") logD("Deleting $playlist")
viewModelScope.launch(Dispatchers.IO) { musicRepository.deletePlaylist(playlist) } viewModelScope.launch(Dispatchers.IO) {
musicRepository.deletePlaylist(playlist)
_playlistMessage.put(PlaylistMessage.DeleteSuccess)
}
} else { } else {
logD("Launching deletion dialog for $playlist") logD("Launching deletion dialog for $playlist")
_playlistDecision.put(PlaylistDecision.Delete(playlist)) _playlistDecision.put(PlaylistDecision.Delete(playlist))
@ -279,7 +283,10 @@ constructor(
fun addToPlaylist(songs: List<Song>, playlist: Playlist? = null) { fun addToPlaylist(songs: List<Song>, playlist: Playlist? = null) {
if (playlist != null) { if (playlist != null) {
logD("Adding ${songs.size} songs to $playlist") logD("Adding ${songs.size} songs to $playlist")
viewModelScope.launch(Dispatchers.IO) { musicRepository.addToPlaylist(songs, playlist) } viewModelScope.launch(Dispatchers.IO) {
musicRepository.addToPlaylist(songs, playlist)
_playlistMessage.put(PlaylistMessage.AddSuccess)
}
} else { } else {
logD("Launching addition dialog for songs=${songs.size}") logD("Launching addition dialog for songs=${songs.size}")
_playlistDecision.put(PlaylistDecision.Add(songs)) _playlistDecision.put(PlaylistDecision.Add(songs))
@ -354,8 +361,46 @@ sealed interface PlaylistDecision {
data class Add(val songs: List<Song>) : PlaylistDecision data class Add(val songs: List<Song>) : PlaylistDecision
} }
sealed interface PlaylistError { sealed interface PlaylistMessage {
data object ImportFailed : PlaylistError val stringRes: Int
data object ExportFailed : PlaylistError data object NewPlaylistSuccess : PlaylistMessage {
override val stringRes: Int
get() = R.string.lng_playlist_created
}
data object ImportSuccess : PlaylistMessage {
override val stringRes: Int
get() = R.string.lng_playlist_imported
}
data object ImportFailed : PlaylistMessage {
override val stringRes: Int
get() = R.string.err_import_failed
}
data object RenameSuccess : PlaylistMessage {
override val stringRes: Int
get() = R.string.lng_playlist_renamed
}
data object DeleteSuccess : PlaylistMessage {
override val stringRes: Int
get() = R.string.lng_playlist_deleted
}
data object AddSuccess : PlaylistMessage {
override val stringRes: Int
get() = R.string.lng_playlist_added
}
data object ExportSuccess : PlaylistMessage {
override val stringRes: Int
get() = R.string.lng_playlist_exported
}
data object ExportFailed : PlaylistMessage {
override val stringRes: Int
get() = R.string.err_export_failed
}
} }

View file

@ -37,7 +37,6 @@ import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.showToast
/** /**
* A dialog that allows the user to pick a specific playlist to add song(s) to. * A dialog that allows the user to pick a specific playlist to add song(s) to.
@ -86,7 +85,6 @@ class AddToPlaylistDialog :
override fun onClick(item: PlaylistChoice, viewHolder: RecyclerView.ViewHolder) { override fun onClick(item: PlaylistChoice, viewHolder: RecyclerView.ViewHolder) {
musicModel.addToPlaylist(pickerModel.currentSongsToAdd.value ?: return, item.playlist) musicModel.addToPlaylist(pickerModel.currentSongsToAdd.value ?: return, item.playlist)
requireContext().showToast(R.string.lng_playlist_added)
findNavController().navigateUp() findNavController().navigateUp()
} }

View file

@ -33,7 +33,6 @@ import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
/** /**
@ -56,7 +55,6 @@ class DeletePlaylistDialog : ViewBindingMaterialDialogFragment<DialogDeletePlayl
// Now we can delete the playlist for-real this time. // Now we can delete the playlist for-real this time.
musicModel.deletePlaylist( musicModel.deletePlaylist(
unlikelyToBeNull(pickerModel.currentPlaylistToDelete.value), rude = true) unlikelyToBeNull(pickerModel.currentPlaylistToDelete.value), rude = true)
requireContext().showToast(R.string.lng_playlist_deleted)
} }
.setNegativeButton(R.string.lbl_cancel, null) .setNegativeButton(R.string.lbl_cancel, null)
} }

View file

@ -98,7 +98,6 @@ class ExportPlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistExp
binding.exportWindowsPaths.setOnClickListener { _ -> binding.exportWindowsPaths.setOnClickListener { _ ->
val current = pickerModel.currentExportConfig.value val current = pickerModel.currentExportConfig.value
logD("change")
pickerModel.setExportConfig(current.copy(windowsPaths = !current.windowsPaths)) pickerModel.setExportConfig(current.copy(windowsPaths = !current.windowsPaths))
} }

View file

@ -33,7 +33,6 @@ import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
/** /**
@ -62,7 +61,6 @@ class NewPlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNameBi
} }
// TODO: Navigate to playlist if there are songs in it // TODO: Navigate to playlist if there are songs in it
musicModel.createPlaylist(name, pendingPlaylist.songs) musicModel.createPlaylist(name, pendingPlaylist.songs)
requireContext().showToast(R.string.lng_playlist_created)
findNavController().apply { findNavController().apply {
navigateUp() navigateUp()
// Do an additional navigation away from the playlist addition dialog, if // Do an additional navigation away from the playlist addition dialog, if

View file

@ -34,7 +34,6 @@ import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
/** /**
@ -58,7 +57,6 @@ class RenamePlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNam
val playlist = unlikelyToBeNull(pickerModel.currentPlaylistToRename.value) val playlist = unlikelyToBeNull(pickerModel.currentPlaylistToRename.value)
val chosenName = pickerModel.chosenName.value as ChosenName.Valid val chosenName = pickerModel.chosenName.value as ChosenName.Valid
musicModel.renamePlaylist(playlist, chosenName.value) musicModel.renamePlaylist(playlist, chosenName.value)
requireContext().showToast(R.string.lng_playlist_renamed)
findNavController().navigateUp() findNavController().navigateUp()
} }
.setNegativeButton(R.string.lbl_cancel, null) .setNegativeButton(R.string.lbl_cancel, null)

View file

@ -52,6 +52,7 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.PlaylistMessage
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.external.M3U import org.oxycblt.auxio.music.external.M3U
import org.oxycblt.auxio.playback.PlaybackDecision import org.oxycblt.auxio.playback.PlaybackDecision
@ -64,6 +65,7 @@ import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logW import org.oxycblt.auxio.util.logW
import org.oxycblt.auxio.util.navigateSafe import org.oxycblt.auxio.util.navigateSafe
import org.oxycblt.auxio.util.setFullWidthLookup import org.oxycblt.auxio.util.setFullWidthLookup
import org.oxycblt.auxio.util.showToast
/** /**
* The [ListFragment] providing search functionality for the music library. * The [ListFragment] providing search functionality for the music library.
@ -160,7 +162,8 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
collectImmediately(searchModel.searchResults, ::updateSearchResults) collectImmediately(searchModel.searchResults, ::updateSearchResults)
collectImmediately(listModel.selected, ::updateSelection) collectImmediately(listModel.selected, ::updateSelection)
collect(listModel.menu.flow, ::handleMenu) collect(listModel.menu.flow, ::handleMenu)
collect(musicModel.playlistDecision.flow, ::handleDecision) collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
collect(musicModel.playlistMessage.flow, ::handlePlaylistMessage)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision) collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
@ -302,7 +305,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
} }
} }
private fun handleDecision(decision: PlaylistDecision?) { private fun handlePlaylistDecision(decision: PlaylistDecision?) {
if (decision == null) return if (decision == null) return
val directions = val directions =
when (decision) { when (decision) {
@ -340,6 +343,12 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
private fun handlePlaylistMessage(message: PlaylistMessage?) {
if (message == null) return
requireContext().showToast(message.stringRes)
musicModel.playlistMessage.consume()
}
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) { private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
searchAdapter.setPlaying(parent ?: song, isPlaying) searchAdapter.setPlaying(parent ?: song, isPlaying)
} }

View file

@ -196,7 +196,9 @@
<string name="lng_observing">Monitoring your music library for changes…</string> <string name="lng_observing">Monitoring your music library for changes…</string>
<string name="lng_queue_added">Added to queue</string> <string name="lng_queue_added">Added to queue</string>
<string name="lng_playlist_created">Playlist created</string> <string name="lng_playlist_created">Playlist created</string>
<string name="lng_playlist_imported">Playlist imported</string>
<string name="lng_playlist_renamed">Playlist renamed</string> <string name="lng_playlist_renamed">Playlist renamed</string>
<string name="lng_playlist_exported">Playlist exported</string>
<string name="lng_playlist_deleted">Playlist deleted</string> <string name="lng_playlist_deleted">Playlist deleted</string>
<string name="lng_playlist_added">Added to playlist</string> <string name="lng_playlist_added">Added to playlist</string>
<string name="lng_author">Developed by Alexander Capehart</string> <string name="lng_author">Developed by Alexander Capehart</string>