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" />
+
+