From 6ef2f746945e64cc804450c4ec574c272e4b5700 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 25 Jul 2023 19:26:06 -0600 Subject: [PATCH] detail: add playlist resorting Add the ability to resort a playlist when in edit mode. Resolves #446. --- CHANGELOG.md | 3 +- .../oxycblt/auxio/detail/DetailViewModel.kt | 12 +++- .../auxio/detail/PlaylistDetailFragment.kt | 13 ++-- .../detail/list/PlaylistDetailListAdapter.kt | 17 ++++- .../detail/sort/PlaylistSongSortDialog.kt | 69 +++++++++++++++++++ .../org/oxycblt/auxio/list/sort/SortDialog.kt | 3 +- app/src/main/res/layout/item_edit_header.xml | 18 ++--- app/src/main/res/navigation/inner.xml | 9 +++ 8 files changed, 127 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/detail/sort/PlaylistSongSortDialog.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 57d03abe2..e9e66ee13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ## dev #### What's New -- Menus have been refreshed with a cleaner look +- Item and sort menus have been refreshed with a cleaner look +- Added ability to sort playlists - Added option to play song by itself in library/item details #### What's Improved diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index 09d0cb01d..e7191b13e 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -480,6 +480,17 @@ constructor( return true } + /** + * Apply a [Sort] to the edited playlist. Does nothing if not in an editing session. + * + * @param sort The [Sort] to apply. + */ + fun applyPlaylistSongSort(sort: Sort) { + val playlist = _currentPlaylist.value ?: return + _editedPlaylist.value = sort.songs(_editedPlaylist.value ?: return) + refreshPlaylistList(playlist, UpdateInstructions.Replace(2)) + } + /** * (Visually) move a song in the current playlist. Does nothing if not in an editing session. * @@ -488,7 +499,6 @@ constructor( * @return true if the song was moved, false otherwise. */ fun movePlaylistSongs(from: Int, to: Int): Boolean { - // TODO: Song re-sorting val playlist = _currentPlaylist.value ?: return false val editedPlaylist = (_editedPlaylist.value ?: return false).toMutableList() val realFrom = from - 2 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 9c0088c11..b9c8e4815 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/PlaylistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/PlaylistDetailFragment.kt @@ -182,9 +182,12 @@ class PlaylistDetailFragment : initialNavDestinationChange = true return } - // Drop any pending playlist edits when navigating away. This could actually happen - // if the user is quick enough. - detailModel.dropPlaylistEdit() + if (destination.id != R.id.playlist_detail_fragment && + destination.id != R.id.playlist_song_sort_dialog) { + // Drop any pending playlist edits when navigating away. This could actually happen + // if the user is quick enough. + detailModel.dropPlaylistEdit() + } } override fun onMenuItemClick(item: MenuItem): Boolean { @@ -251,7 +254,9 @@ class PlaylistDetailFragment : detailModel.startPlaylistEdit() } - override fun onOpenSortMenu() {} + override fun onOpenSortMenu() { + findNavController().navigateSafe(PlaylistDetailFragmentDirections.sort()) + } private fun updatePlaylist(playlist: Playlist?) { if (playlist == null) { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/list/PlaylistDetailListAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/list/PlaylistDetailListAdapter.kt index 5b1de107b..ca8a0657b 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/list/PlaylistDetailListAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/list/PlaylistDetailListAdapter.kt @@ -170,10 +170,25 @@ private class EditHeaderViewHolder private constructor(private val binding: Item TooltipCompat.setTooltipText(this, contentDescription) setOnClickListener { listener.onStartEdit() } } + binding.headerSort.apply { + TooltipCompat.setTooltipText(this, contentDescription) + setOnClickListener { listener.onOpenSortMenu() } + } } override fun updateEditing(editing: Boolean) { - binding.headerEdit.isEnabled = !editing + binding.headerEdit.apply { + isVisible = !editing + isClickable = !editing + isFocusable = !editing + jumpDrawablesToCurrentState() + } + binding.headerSort.apply { + isVisible = editing + isClickable = editing + isFocusable = editing + jumpDrawablesToCurrentState() + } } companion object { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/sort/PlaylistSongSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/sort/PlaylistSongSortDialog.kt new file mode 100644 index 000000000..58b989679 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/detail/sort/PlaylistSongSortDialog.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Auxio Project + * PlaylistSongSortDialog.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.detail.sort + +import android.os.Bundle +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.findNavController +import dagger.hilt.android.AndroidEntryPoint +import org.oxycblt.auxio.databinding.DialogSortBinding +import org.oxycblt.auxio.detail.DetailViewModel +import org.oxycblt.auxio.list.Sort +import org.oxycblt.auxio.list.sort.SortDialog +import org.oxycblt.auxio.music.Playlist +import org.oxycblt.auxio.util.collectImmediately +import org.oxycblt.auxio.util.logD + +/** + * A [SortDialog] that controls the [Sort] of [DetailViewModel.genreSongSort]. + * + * @author Alexander Capehart (OxygenCobalt) + */ +@AndroidEntryPoint +class PlaylistSongSortDialog : SortDialog() { + private val detailModel: DetailViewModel by activityViewModels() + + override fun onBindingCreated(binding: DialogSortBinding, savedInstanceState: Bundle?) { + super.onBindingCreated(binding, savedInstanceState) + + // --- VIEWMODEL SETUP --- + collectImmediately(detailModel.currentPlaylist, ::updatePlaylist) + } + + override fun getInitialSort() = null + + override fun applyChosenSort(sort: Sort) { + detailModel.applyPlaylistSongSort(sort) + } + + override fun getModeChoices() = + listOf( + Sort.Mode.ByName, + Sort.Mode.ByArtist, + Sort.Mode.ByAlbum, + Sort.Mode.ByDate, + Sort.Mode.ByDuration) + + private fun updatePlaylist(genre: Playlist?) { + if (genre == null) { + logD("No genre to sort, navigating away") + findNavController().navigateUp() + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/list/sort/SortDialog.kt b/app/src/main/java/org/oxycblt/auxio/list/sort/SortDialog.kt index 98ead4aa4..7976041fd 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/sort/SortDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/sort/SortDialog.kt @@ -62,9 +62,10 @@ abstract class SortDialog : } // --- STATE SETUP --- + modeAdapter.update(getModeChoices(), UpdateInstructions.Diff) + val initial = getInitialSort() if (initial != null) { - modeAdapter.update(getModeChoices(), UpdateInstructions.Diff) modeAdapter.setSelected(initial.mode) val directionId = when (initial.direction) { diff --git a/app/src/main/res/layout/item_edit_header.xml b/app/src/main/res/layout/item_edit_header.xml index 80659deca..e3bbb9009 100644 --- a/app/src/main/res/layout/item_edit_header.xml +++ b/app/src/main/res/layout/item_edit_header.xml @@ -28,14 +28,14 @@ app:icon="@drawable/ic_edit_24" app:layout_constraintEnd_toEndOf="parent" /> - - - - - - - - - + \ No newline at end of file diff --git a/app/src/main/res/navigation/inner.xml b/app/src/main/res/navigation/inner.xml index d87710469..52ed213e3 100644 --- a/app/src/main/res/navigation/inner.xml +++ b/app/src/main/res/navigation/inner.xml @@ -309,6 +309,9 @@ + @@ -338,6 +341,12 @@ app:destination="@id/play_from_genre_dialog" /> + +