detail: add playlist resorting
Add the ability to resort a playlist when in edit mode. Resolves #446.
This commit is contained in:
parent
b2a3416289
commit
6ef2f74694
8 changed files with 127 additions and 17 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue