music: rename playlist when reimporting
When reimporting an M3U file into a playlist, if the name differs, then initiate a rename dialog so the user has a choice on whether they want to use the new name or not. This does kinda desecrate the "Rename" decision a bit, but it's still to the user the same.
This commit is contained in:
parent
9ad11ec5aa
commit
0675ce8a03
10 changed files with 142 additions and 60 deletions
|
@ -352,7 +352,11 @@ class PlaylistDetailFragment :
|
||||||
}
|
}
|
||||||
is PlaylistDecision.Rename -> {
|
is PlaylistDecision.Rename -> {
|
||||||
logD("Renaming ${decision.playlist}")
|
logD("Renaming ${decision.playlist}")
|
||||||
PlaylistDetailFragmentDirections.renamePlaylist(decision.playlist.uid)
|
PlaylistDetailFragmentDirections.renamePlaylist(
|
||||||
|
decision.playlist.uid,
|
||||||
|
decision.template,
|
||||||
|
decision.applySongs.map { it.uid }.toTypedArray(),
|
||||||
|
decision.reason)
|
||||||
}
|
}
|
||||||
is PlaylistDecision.Export -> {
|
is PlaylistDecision.Export -> {
|
||||||
logD("Exporting ${decision.playlist}")
|
logD("Exporting ${decision.playlist}")
|
||||||
|
|
|
@ -487,7 +487,11 @@ class HomeFragment :
|
||||||
}
|
}
|
||||||
is PlaylistDecision.Rename -> {
|
is PlaylistDecision.Rename -> {
|
||||||
logD("Renaming ${decision.playlist}")
|
logD("Renaming ${decision.playlist}")
|
||||||
HomeFragmentDirections.renamePlaylist(decision.playlist.uid)
|
HomeFragmentDirections.renamePlaylist(
|
||||||
|
decision.playlist.uid,
|
||||||
|
decision.template,
|
||||||
|
decision.applySongs.map { it.uid }.toTypedArray(),
|
||||||
|
decision.reason)
|
||||||
}
|
}
|
||||||
is PlaylistDecision.Export -> {
|
is PlaylistDecision.Export -> {
|
||||||
logD("Exporting ${decision.playlist}")
|
logD("Exporting ${decision.playlist}")
|
||||||
|
|
|
@ -348,6 +348,7 @@ interface Genre : MusicParent {
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
interface Playlist : MusicParent {
|
interface Playlist : MusicParent {
|
||||||
|
override val name: Name.Known
|
||||||
override val songs: List<Song>
|
override val songs: List<Song>
|
||||||
/** The total duration of the songs in this genre, in milliseconds. */
|
/** The total duration of the songs in this genre, in milliseconds. */
|
||||||
val durationMs: Long
|
val durationMs: Long
|
||||||
|
|
|
@ -145,7 +145,7 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a playlist from a file [Uri]. Errors pushed to [importError].
|
* Import a playlist from a file [Uri]. Errors pushed to [playlistMessage].
|
||||||
*
|
*
|
||||||
* @param uri The [Uri] of the file to import. If null, the user will be prompted with a file
|
* @param uri The [Uri] of the file to import. If null, the user will be prompted with a file
|
||||||
* picker.
|
* picker.
|
||||||
|
@ -173,8 +173,17 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target !== null) {
|
if (target !== null) {
|
||||||
musicRepository.rewritePlaylist(target, songs)
|
if (importedPlaylist.name != null && importedPlaylist.name != target.name.raw) {
|
||||||
_playlistMessage.put(PlaylistMessage.ImportSuccess)
|
_playlistDecision.put(
|
||||||
|
PlaylistDecision.Rename(
|
||||||
|
target,
|
||||||
|
importedPlaylist.name,
|
||||||
|
songs,
|
||||||
|
PlaylistDecision.Rename.Reason.IMPORT))
|
||||||
|
} else {
|
||||||
|
musicRepository.rewritePlaylist(target, songs)
|
||||||
|
_playlistMessage.put(PlaylistMessage.ImportSuccess)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_playlistDecision.put(
|
_playlistDecision.put(
|
||||||
PlaylistDecision.New(
|
PlaylistDecision.New(
|
||||||
|
@ -188,7 +197,7 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export a [Playlist] to a file [Uri]. Errors pushed to [exportError].
|
* Export a [Playlist] to a file [Uri]. Errors pushed to [playlistMessage].
|
||||||
*
|
*
|
||||||
* @param playlist The [Playlist] to export.
|
* @param playlist The [Playlist] to export.
|
||||||
* @param uri The [Uri] to export to. If null, the user will be prompted for one.
|
* @param uri The [Uri] to export to. If null, the user will be prompted for one.
|
||||||
|
@ -214,17 +223,24 @@ constructor(
|
||||||
*
|
*
|
||||||
* @param playlist The [Playlist] to rename,
|
* @param playlist The [Playlist] to rename,
|
||||||
* @param name The new name of the [Playlist]. If null, the user will be prompted for a name.
|
* @param name The new name of the [Playlist]. If null, the user will be prompted for a name.
|
||||||
* @param reason The reason why the playlist is being renamed. For all intensive purposes, you
|
* @param applySongs The songs to apply to the playlist after renaming. If empty, no songs will
|
||||||
|
* be applied. This argument is internal and does not need to be specified in normal use.
|
||||||
|
* @param reason The reason why the playlist is being renamed. This argument is internal and
|
||||||
|
* does not need to be specified in normal use.
|
||||||
*/
|
*/
|
||||||
fun renamePlaylist(
|
fun renamePlaylist(
|
||||||
playlist: Playlist,
|
playlist: Playlist,
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
|
applySongs: List<Song> = listOf(),
|
||||||
reason: PlaylistDecision.Rename.Reason = PlaylistDecision.Rename.Reason.ACTION
|
reason: PlaylistDecision.Rename.Reason = PlaylistDecision.Rename.Reason.ACTION
|
||||||
) {
|
) {
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
logD("Renaming $playlist to $name")
|
logD("Renaming $playlist to $name")
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
musicRepository.renamePlaylist(playlist, name)
|
musicRepository.renamePlaylist(playlist, name)
|
||||||
|
if (applySongs.isNotEmpty()) {
|
||||||
|
musicRepository.rewritePlaylist(playlist, applySongs)
|
||||||
|
}
|
||||||
val message =
|
val message =
|
||||||
when (reason) {
|
when (reason) {
|
||||||
PlaylistDecision.Rename.Reason.ACTION -> PlaylistMessage.RenameSuccess
|
PlaylistDecision.Rename.Reason.ACTION -> PlaylistMessage.RenameSuccess
|
||||||
|
@ -234,7 +250,7 @@ constructor(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logD("Launching rename dialog for $playlist")
|
logD("Launching rename dialog for $playlist")
|
||||||
_playlistDecision.put(PlaylistDecision.Rename(playlist, reason))
|
_playlistDecision.put(PlaylistDecision.Rename(playlist, null, applySongs, reason))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +259,8 @@ constructor(
|
||||||
*
|
*
|
||||||
* @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
|
* @param rude Whether to immediately delete the playlist or prompt the user first. This should
|
||||||
* be false at almost all times.
|
* be false at almost all times. This argument is internal and does not need to be specified
|
||||||
|
* in normal use.
|
||||||
*/
|
*/
|
||||||
fun deletePlaylist(playlist: Playlist, rude: Boolean = false) {
|
fun deletePlaylist(playlist: Playlist, rude: Boolean = false) {
|
||||||
if (rude) {
|
if (rude) {
|
||||||
|
@ -375,7 +392,12 @@ sealed interface PlaylistDecision {
|
||||||
*
|
*
|
||||||
* @param playlist The playlist to act on.
|
* @param playlist The playlist to act on.
|
||||||
*/
|
*/
|
||||||
data class Rename(val playlist: Playlist, val reason: Reason) : PlaylistDecision {
|
data class Rename(
|
||||||
|
val playlist: Playlist,
|
||||||
|
val template: String?,
|
||||||
|
val applySongs: List<Song>,
|
||||||
|
val reason: Reason
|
||||||
|
) : PlaylistDecision {
|
||||||
enum class Reason {
|
enum class Reason {
|
||||||
ACTION,
|
ACTION,
|
||||||
IMPORT
|
IMPORT
|
||||||
|
|
|
@ -31,6 +31,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogPlaylistNameBinding
|
import org.oxycblt.auxio.databinding.DialogPlaylistNameBinding
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
|
import org.oxycblt.auxio.music.PlaylistDecision
|
||||||
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
|
||||||
|
@ -52,9 +53,14 @@ class NewPlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNameBi
|
||||||
|
|
||||||
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
||||||
builder
|
builder
|
||||||
.setTitle(R.string.lbl_new_playlist)
|
.setTitle(
|
||||||
|
when (args.reason) {
|
||||||
|
PlaylistDecision.New.Reason.NEW,
|
||||||
|
PlaylistDecision.New.Reason.ADD -> R.string.lbl_new_playlist
|
||||||
|
PlaylistDecision.New.Reason.IMPORT -> R.string.lbl_import_playlist
|
||||||
|
})
|
||||||
.setPositiveButton(R.string.lbl_ok) { _, _ ->
|
.setPositiveButton(R.string.lbl_ok) { _, _ ->
|
||||||
val pendingPlaylist = unlikelyToBeNull(pickerModel.currentPendingPlaylist.value)
|
val pendingPlaylist = unlikelyToBeNull(pickerModel.currentPendingNewPlaylist.value)
|
||||||
val name =
|
val name =
|
||||||
when (val chosenName = pickerModel.chosenName.value) {
|
when (val chosenName = pickerModel.chosenName.value) {
|
||||||
is ChosenName.Valid -> chosenName.value
|
is ChosenName.Valid -> chosenName.value
|
||||||
|
@ -84,27 +90,29 @@ class NewPlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNameBi
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
musicModel.playlistDecision.consume()
|
musicModel.playlistDecision.consume()
|
||||||
pickerModel.setPendingPlaylist(requireContext(), args.songUids, args.reason)
|
pickerModel.setPendingPlaylist(requireContext(), args.songUids, args.template, args.reason)
|
||||||
if (!initializedField) {
|
|
||||||
|
collectImmediately(pickerModel.currentPendingNewPlaylist, ::updatePendingPlaylist)
|
||||||
|
collectImmediately(pickerModel.chosenName, ::updateChosenName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePendingPlaylist(pendingNewPlaylist: PendingNewPlaylist?) {
|
||||||
|
if (pendingNewPlaylist == null) {
|
||||||
|
logD("No playlist to create, leaving")
|
||||||
|
findNavController().navigateUp()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val binding = requireBinding()
|
||||||
|
if (pendingNewPlaylist.template != null) {
|
||||||
|
if (initializedField) return
|
||||||
initializedField = true
|
initializedField = true
|
||||||
// Need to convert args.existingName to an Editable
|
// Need to convert args.existingName to an Editable
|
||||||
if (args.template != null) {
|
if (args.template != null) {
|
||||||
binding.playlistName.text = EDITABLE_FACTORY.newEditable(args.template)
|
binding.playlistName.text = EDITABLE_FACTORY.newEditable(args.template)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
binding.playlistName.hint = pendingNewPlaylist.preferredName
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(pickerModel.currentPendingPlaylist, ::updatePendingPlaylist)
|
|
||||||
collectImmediately(pickerModel.chosenName, ::updateChosenName)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updatePendingPlaylist(pendingPlaylist: PendingPlaylist?) {
|
|
||||||
if (pendingPlaylist == null) {
|
|
||||||
logD("No playlist to create, leaving")
|
|
||||||
findNavController().navigateUp()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
requireBinding().playlistName.hint = pendingPlaylist.preferredName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateChosenName(chosenName: ChosenName) {
|
private fun updateChosenName(chosenName: ChosenName) {
|
||||||
|
|
|
@ -45,15 +45,15 @@ import org.oxycblt.auxio.util.logW
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class PlaylistPickerViewModel @Inject constructor(private val musicRepository: MusicRepository) :
|
class PlaylistPickerViewModel @Inject constructor(private val musicRepository: MusicRepository) :
|
||||||
ViewModel(), MusicRepository.UpdateListener {
|
ViewModel(), MusicRepository.UpdateListener {
|
||||||
private val _currentPendingPlaylist = MutableStateFlow<PendingPlaylist?>(null)
|
private val _currentPendingNewPlaylist = MutableStateFlow<PendingNewPlaylist?>(null)
|
||||||
/** A new [Playlist] having it's name chosen by the user. Null if none yet. */
|
/** A new [Playlist] having it's name chosen by the user. Null if none yet. */
|
||||||
val currentPendingPlaylist: StateFlow<PendingPlaylist?>
|
val currentPendingNewPlaylist: StateFlow<PendingNewPlaylist?>
|
||||||
get() = _currentPendingPlaylist
|
get() = _currentPendingNewPlaylist
|
||||||
|
|
||||||
private val _currentPlaylistToRename = MutableStateFlow<Playlist?>(null)
|
private val _currentPendingRenamePlaylist = MutableStateFlow<PendingRenamePlaylist?>(null)
|
||||||
/** An existing [Playlist] that is being renamed. Null if none yet. */
|
/** An existing [Playlist] that is being renamed. Null if none yet. */
|
||||||
val currentPlaylistToRename: StateFlow<Playlist?>
|
val currentPendingRenamePlaylist: StateFlow<PendingRenamePlaylist?>
|
||||||
get() = _currentPlaylistToRename
|
get() = _currentPendingRenamePlaylist
|
||||||
|
|
||||||
private val _currentPlaylistToExport = MutableStateFlow<Playlist?>(null)
|
private val _currentPlaylistToExport = MutableStateFlow<Playlist?>(null)
|
||||||
/** An existing [Playlist] that is being exported. Null if none yet. */
|
/** An existing [Playlist] that is being exported. Null if none yet. */
|
||||||
|
@ -71,7 +71,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
get() = _currentPlaylistToDelete
|
get() = _currentPlaylistToDelete
|
||||||
|
|
||||||
private val _chosenName = MutableStateFlow<ChosenName>(ChosenName.Empty)
|
private val _chosenName = MutableStateFlow<ChosenName>(ChosenName.Empty)
|
||||||
/** The users chosen name for [currentPendingPlaylist] or [currentPlaylistToRename]. */
|
/** The users chosen name for [currentPendingNewPlaylist] or [currentPendingRenamePlaylist]. */
|
||||||
val chosenName: StateFlow<ChosenName>
|
val chosenName: StateFlow<ChosenName>
|
||||||
get() = _chosenName
|
get() = _chosenName
|
||||||
|
|
||||||
|
@ -93,14 +93,15 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
var refreshChoicesWith: List<Song>? = null
|
var refreshChoicesWith: List<Song>? = null
|
||||||
val deviceLibrary = musicRepository.deviceLibrary
|
val deviceLibrary = musicRepository.deviceLibrary
|
||||||
if (changes.deviceLibrary && deviceLibrary != null) {
|
if (changes.deviceLibrary && deviceLibrary != null) {
|
||||||
_currentPendingPlaylist.value =
|
_currentPendingNewPlaylist.value =
|
||||||
_currentPendingPlaylist.value?.let { pendingPlaylist ->
|
_currentPendingNewPlaylist.value?.let { pendingPlaylist ->
|
||||||
PendingPlaylist(
|
PendingNewPlaylist(
|
||||||
pendingPlaylist.preferredName,
|
pendingPlaylist.preferredName,
|
||||||
pendingPlaylist.songs.mapNotNull { deviceLibrary.findSong(it.uid) },
|
pendingPlaylist.songs.mapNotNull { deviceLibrary.findSong(it.uid) },
|
||||||
|
pendingPlaylist.template,
|
||||||
pendingPlaylist.reason)
|
pendingPlaylist.reason)
|
||||||
}
|
}
|
||||||
logD("Updated pending playlist: ${_currentPendingPlaylist.value?.preferredName}")
|
logD("Updated pending playlist: ${_currentPendingNewPlaylist.value?.preferredName}")
|
||||||
|
|
||||||
_currentSongsToAdd.value =
|
_currentSongsToAdd.value =
|
||||||
_currentSongsToAdd.value?.let { pendingSongs ->
|
_currentSongsToAdd.value?.let { pendingSongs ->
|
||||||
|
@ -141,7 +142,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a new [currentPendingPlaylist] from a new batch of pending [Song] [Music.UID]s.
|
* Set a new [currentPendingNewPlaylist] 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.
|
||||||
|
@ -150,6 +151,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
fun setPendingPlaylist(
|
fun setPendingPlaylist(
|
||||||
context: Context,
|
context: Context,
|
||||||
songUids: Array<Music.UID>,
|
songUids: Array<Music.UID>,
|
||||||
|
template: String?,
|
||||||
reason: PlaylistDecision.New.Reason
|
reason: PlaylistDecision.New.Reason
|
||||||
) {
|
) {
|
||||||
logD("Opening ${songUids.size} songs to create a playlist from")
|
logD("Opening ${songUids.size} songs to create a playlist from")
|
||||||
|
@ -173,9 +175,9 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
possibleName
|
possibleName
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentPendingPlaylist.value =
|
_currentPendingNewPlaylist.value =
|
||||||
if (possibleName != null && songs != null) {
|
if (possibleName != null && songs != null) {
|
||||||
PendingPlaylist(possibleName, songs, reason)
|
PendingNewPlaylist(possibleName, songs, template, reason)
|
||||||
} else {
|
} else {
|
||||||
logW("Given song UIDs to create were invalid")
|
logW("Given song UIDs to create were invalid")
|
||||||
null
|
null
|
||||||
|
@ -183,16 +185,28 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a new [currentPlaylistToRename] from a [Playlist] [Music.UID].
|
* Set a new [currentPendingRenamePlaylist] from a [Playlist] [Music.UID].
|
||||||
*
|
*
|
||||||
* @param playlistUid The [Music.UID]s of the [Playlist] to rename.
|
* @param playlistUid The [Music.UID]s of the [Playlist] to rename.
|
||||||
*/
|
*/
|
||||||
fun setPlaylistToRename(playlistUid: Music.UID) {
|
fun setPlaylistToRename(
|
||||||
|
playlistUid: Music.UID,
|
||||||
|
applySongUids: Array<Music.UID>,
|
||||||
|
template: String?,
|
||||||
|
reason: PlaylistDecision.Rename.Reason
|
||||||
|
) {
|
||||||
logD("Opening playlist $playlistUid to rename")
|
logD("Opening playlist $playlistUid to rename")
|
||||||
_currentPlaylistToRename.value = musicRepository.userLibrary?.findPlaylist(playlistUid)
|
val playlist = musicRepository.userLibrary?.findPlaylist(playlistUid)
|
||||||
if (_currentPlaylistToDelete.value == null) {
|
val applySongs =
|
||||||
logW("Given playlist UID to rename was invalid")
|
musicRepository.deviceLibrary?.let { applySongUids.mapNotNull(it::findSong) }
|
||||||
}
|
|
||||||
|
_currentPendingRenamePlaylist.value =
|
||||||
|
if (playlist != null && applySongs != null) {
|
||||||
|
PendingRenamePlaylist(playlist, applySongs, template, reason)
|
||||||
|
} else {
|
||||||
|
logW("Given playlist UID to rename was invalid")
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -223,7 +237,7 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a new [currentPendingPlaylist] from a new [Playlist] [Music.UID].
|
* Set a new [currentPendingNewPlaylist] from a new [Playlist] [Music.UID].
|
||||||
*
|
*
|
||||||
* @param playlistUid The [Music.UID] of the [Playlist] to delete.
|
* @param playlistUid The [Music.UID] of the [Playlist] to delete.
|
||||||
*/
|
*/
|
||||||
|
@ -301,16 +315,24 @@ class PlaylistPickerViewModel @Inject constructor(private val musicRepository: M
|
||||||
* Represents a playlist that will be created as soon as a name is chosen.
|
* Represents a playlist that will be created as soon as a name is chosen.
|
||||||
*
|
*
|
||||||
* @param preferredName The name to be used by default if no other name is chosen.
|
* @param preferredName The name to be used by default if no other name is chosen.
|
||||||
* @param songs The [Song]s to be contained in the [PendingPlaylist]
|
* @param songs The [Song]s to be contained in the [PendingNewPlaylist]
|
||||||
* @param reason The reason the playlist is being created.
|
* @param reason The reason the playlist is being created.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
data class PendingPlaylist(
|
data class PendingNewPlaylist(
|
||||||
val preferredName: String,
|
val preferredName: String,
|
||||||
val songs: List<Song>,
|
val songs: List<Song>,
|
||||||
|
val template: String?,
|
||||||
val reason: PlaylistDecision.New.Reason
|
val reason: PlaylistDecision.New.Reason
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class PendingRenamePlaylist(
|
||||||
|
val playlist: Playlist,
|
||||||
|
val applySongs: List<Song>,
|
||||||
|
val template: String?,
|
||||||
|
val reason: PlaylistDecision.Rename.Reason
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the (processed) user input from the playlist naming dialogs.
|
* Represents the (processed) user input from the playlist naming dialogs.
|
||||||
*
|
*
|
||||||
|
|
|
@ -30,7 +30,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogPlaylistNameBinding
|
import org.oxycblt.auxio.databinding.DialogPlaylistNameBinding
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
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
|
||||||
|
@ -54,9 +53,14 @@ class RenamePlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNam
|
||||||
builder
|
builder
|
||||||
.setTitle(R.string.lbl_rename_playlist)
|
.setTitle(R.string.lbl_rename_playlist)
|
||||||
.setPositiveButton(R.string.lbl_ok) { _, _ ->
|
.setPositiveButton(R.string.lbl_ok) { _, _ ->
|
||||||
val playlist = unlikelyToBeNull(pickerModel.currentPlaylistToRename.value)
|
val pendingRenamePlaylist =
|
||||||
|
unlikelyToBeNull(pickerModel.currentPendingRenamePlaylist.value)
|
||||||
val chosenName = pickerModel.chosenName.value as ChosenName.Valid
|
val chosenName = pickerModel.chosenName.value as ChosenName.Valid
|
||||||
musicModel.renamePlaylist(playlist, chosenName.value)
|
musicModel.renamePlaylist(
|
||||||
|
pendingRenamePlaylist.playlist,
|
||||||
|
chosenName.value,
|
||||||
|
pendingRenamePlaylist.applySongs,
|
||||||
|
pendingRenamePlaylist.reason)
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.lbl_cancel, null)
|
.setNegativeButton(R.string.lbl_cancel, null)
|
||||||
|
@ -73,20 +77,23 @@ class RenamePlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNam
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
musicModel.playlistDecision.consume()
|
musicModel.playlistDecision.consume()
|
||||||
pickerModel.setPlaylistToRename(args.playlistUid)
|
pickerModel.setPlaylistToRename(
|
||||||
collectImmediately(pickerModel.currentPlaylistToRename, ::updatePlaylistToRename)
|
args.playlistUid, args.applySongUids, args.template, args.reason)
|
||||||
|
collectImmediately(pickerModel.currentPendingRenamePlaylist, ::updatePlaylistToRename)
|
||||||
collectImmediately(pickerModel.chosenName, ::updateChosenName)
|
collectImmediately(pickerModel.chosenName, ::updateChosenName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePlaylistToRename(playlist: Playlist?) {
|
private fun updatePlaylistToRename(pendingRenamePlaylist: PendingRenamePlaylist?) {
|
||||||
if (playlist == null) {
|
if (pendingRenamePlaylist == null) {
|
||||||
// Nothing to rename anymore.
|
// Nothing to rename anymore.
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initializedField) {
|
if (!initializedField) {
|
||||||
val default = playlist.name.resolve(requireContext())
|
val default =
|
||||||
|
pendingRenamePlaylist.template
|
||||||
|
?: pendingRenamePlaylist.playlist.name.resolve(requireContext())
|
||||||
logD("Name input is not initialized, setting to $default")
|
logD("Name input is not initialized, setting to $default")
|
||||||
requireBinding().playlistName.setText(default)
|
requireBinding().playlistName.setText(default)
|
||||||
initializedField = true
|
initializedField = true
|
||||||
|
|
|
@ -321,7 +321,11 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
||||||
}
|
}
|
||||||
is PlaylistDecision.Rename -> {
|
is PlaylistDecision.Rename -> {
|
||||||
logD("Renaming ${decision.playlist}")
|
logD("Renaming ${decision.playlist}")
|
||||||
SearchFragmentDirections.renamePlaylist(decision.playlist.uid)
|
SearchFragmentDirections.renamePlaylist(
|
||||||
|
decision.playlist.uid,
|
||||||
|
decision.template,
|
||||||
|
decision.applySongs.map { it.uid }.toTypedArray(),
|
||||||
|
decision.reason)
|
||||||
}
|
}
|
||||||
is PlaylistDecision.Delete -> {
|
is PlaylistDecision.Delete -> {
|
||||||
logD("Deleting ${decision.playlist}")
|
logD("Deleting ${decision.playlist}")
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/playback_controls_container"
|
app:layout_constraintBottom_toTopOf="@+id/playback_controls_container"
|
||||||
app:layout_constraintEnd_toEndOf="@+id/playback_cover_pager"
|
app:layout_constraintEnd_toEndOf="@+id/playback_info_container"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
|
|
@ -430,6 +430,16 @@
|
||||||
<argument
|
<argument
|
||||||
android:name="playlistUid"
|
android:name="playlistUid"
|
||||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||||
|
<argument
|
||||||
|
android:name="template"
|
||||||
|
app:argType="string"
|
||||||
|
app:nullable="true" />
|
||||||
|
<argument
|
||||||
|
android:name="applySongUids"
|
||||||
|
app:argType="org.oxycblt.auxio.music.Music$UID[]" />
|
||||||
|
<argument
|
||||||
|
android:name="reason"
|
||||||
|
app:argType="org.oxycblt.auxio.music.PlaylistDecision$Rename$Reason" />
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<dialog
|
<dialog
|
||||||
|
|
Loading…
Reference in a new issue