detail: add playlist resorting

Add the ability to resort a playlist when in edit mode.

Resolves #446.
This commit is contained in:
Alexander Capehart 2023-07-25 19:26:06 -06:00
parent b2a3416289
commit 6ef2f74694
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 127 additions and 17 deletions

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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 {

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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()
}
}
}

View file

@ -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) {

View file

@ -28,14 +28,14 @@
app:icon="@drawable/ic_edit_24"
app:layout_constraintEnd_toEndOf="parent" />
<!-- <org.oxycblt.auxio.ui.RippleFixMaterialButton-->
<!-- android:id="@+id/header_sort"-->
<!-- style="@style/Widget.Auxio.Button.Icon.Small"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginEnd="@dimen/spacing_mid_medium"-->
<!-- android:contentDescription="@string/lbl_cancel"-->
<!-- app:icon="@drawable/ic_sort_24"-->
<!-- app:layout_constraintEnd_toEndOf="parent" />-->
<org.oxycblt.auxio.ui.RippleFixMaterialButton
android:id="@+id/header_sort"
style="@style/Widget.Auxio.Button.Icon.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_mid_medium"
android:contentDescription="@string/lbl_edit"
app:icon="@drawable/ic_sort_24"
app:layout_constraintEnd_toEndOf="parent" />
</LinearLayout>

View file

@ -309,6 +309,9 @@
<argument
android:name="playlistUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
<action
android:id="@+id/sort"
app:destination="@+id/playlist_song_sort_dialog" />
<action
android:id="@+id/show_song"
app:destination="@id/song_detail_dialog" />
@ -338,6 +341,12 @@
app:destination="@id/play_from_genre_dialog" />
</fragment>
<dialog
android:id="@+id/playlist_song_sort_dialog"
android:name="org.oxycblt.auxio.detail.sort.PlaylistSongSortDialog"
android:label="PlaylistSongSortDialog"
tools:layout="@layout/dialog_sort" />
<dialog
android:id="@+id/new_playlist_dialog"
android:name="org.oxycblt.auxio.music.decision.NewPlaylistDialog"