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
|
## dev
|
||||||
|
|
||||||
#### What's New
|
#### 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
|
- Added option to play song by itself in library/item details
|
||||||
|
|
||||||
#### What's Improved
|
#### What's Improved
|
||||||
|
|
|
@ -480,6 +480,17 @@ constructor(
|
||||||
return true
|
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.
|
* (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.
|
* @return true if the song was moved, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun movePlaylistSongs(from: Int, to: Int): Boolean {
|
fun movePlaylistSongs(from: Int, to: Int): Boolean {
|
||||||
// TODO: Song re-sorting
|
|
||||||
val playlist = _currentPlaylist.value ?: return false
|
val playlist = _currentPlaylist.value ?: return false
|
||||||
val editedPlaylist = (_editedPlaylist.value ?: return false).toMutableList()
|
val editedPlaylist = (_editedPlaylist.value ?: return false).toMutableList()
|
||||||
val realFrom = from - 2
|
val realFrom = from - 2
|
||||||
|
|
|
@ -182,9 +182,12 @@ class PlaylistDetailFragment :
|
||||||
initialNavDestinationChange = true
|
initialNavDestinationChange = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Drop any pending playlist edits when navigating away. This could actually happen
|
if (destination.id != R.id.playlist_detail_fragment &&
|
||||||
// if the user is quick enough.
|
destination.id != R.id.playlist_song_sort_dialog) {
|
||||||
detailModel.dropPlaylistEdit()
|
// 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 {
|
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||||
|
@ -251,7 +254,9 @@ class PlaylistDetailFragment :
|
||||||
detailModel.startPlaylistEdit()
|
detailModel.startPlaylistEdit()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenSortMenu() {}
|
override fun onOpenSortMenu() {
|
||||||
|
findNavController().navigateSafe(PlaylistDetailFragmentDirections.sort())
|
||||||
|
}
|
||||||
|
|
||||||
private fun updatePlaylist(playlist: Playlist?) {
|
private fun updatePlaylist(playlist: Playlist?) {
|
||||||
if (playlist == null) {
|
if (playlist == null) {
|
||||||
|
|
|
@ -170,10 +170,25 @@ private class EditHeaderViewHolder private constructor(private val binding: Item
|
||||||
TooltipCompat.setTooltipText(this, contentDescription)
|
TooltipCompat.setTooltipText(this, contentDescription)
|
||||||
setOnClickListener { listener.onStartEdit() }
|
setOnClickListener { listener.onStartEdit() }
|
||||||
}
|
}
|
||||||
|
binding.headerSort.apply {
|
||||||
|
TooltipCompat.setTooltipText(this, contentDescription)
|
||||||
|
setOnClickListener { listener.onOpenSortMenu() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateEditing(editing: Boolean) {
|
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 {
|
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 ---
|
// --- STATE SETUP ---
|
||||||
|
modeAdapter.update(getModeChoices(), UpdateInstructions.Diff)
|
||||||
|
|
||||||
val initial = getInitialSort()
|
val initial = getInitialSort()
|
||||||
if (initial != null) {
|
if (initial != null) {
|
||||||
modeAdapter.update(getModeChoices(), UpdateInstructions.Diff)
|
|
||||||
modeAdapter.setSelected(initial.mode)
|
modeAdapter.setSelected(initial.mode)
|
||||||
val directionId =
|
val directionId =
|
||||||
when (initial.direction) {
|
when (initial.direction) {
|
||||||
|
|
|
@ -28,14 +28,14 @@
|
||||||
app:icon="@drawable/ic_edit_24"
|
app:icon="@drawable/ic_edit_24"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
<!-- <org.oxycblt.auxio.ui.RippleFixMaterialButton-->
|
<org.oxycblt.auxio.ui.RippleFixMaterialButton
|
||||||
<!-- android:id="@+id/header_sort"-->
|
android:id="@+id/header_sort"
|
||||||
<!-- style="@style/Widget.Auxio.Button.Icon.Small"-->
|
style="@style/Widget.Auxio.Button.Icon.Small"
|
||||||
<!-- android:layout_width="wrap_content"-->
|
android:layout_width="wrap_content"
|
||||||
<!-- android:layout_height="wrap_content"-->
|
android:layout_height="wrap_content"
|
||||||
<!-- android:layout_marginEnd="@dimen/spacing_mid_medium"-->
|
android:layout_marginEnd="@dimen/spacing_mid_medium"
|
||||||
<!-- android:contentDescription="@string/lbl_cancel"-->
|
android:contentDescription="@string/lbl_edit"
|
||||||
<!-- app:icon="@drawable/ic_sort_24"-->
|
app:icon="@drawable/ic_sort_24"
|
||||||
<!-- app:layout_constraintEnd_toEndOf="parent" />-->
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -309,6 +309,9 @@
|
||||||
<argument
|
<argument
|
||||||
android:name="playlistUid"
|
android:name="playlistUid"
|
||||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/sort"
|
||||||
|
app:destination="@+id/playlist_song_sort_dialog" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/show_song"
|
android:id="@+id/show_song"
|
||||||
app:destination="@id/song_detail_dialog" />
|
app:destination="@id/song_detail_dialog" />
|
||||||
|
@ -338,6 +341,12 @@
|
||||||
app:destination="@id/play_from_genre_dialog" />
|
app:destination="@id/play_from_genre_dialog" />
|
||||||
</fragment>
|
</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
|
<dialog
|
||||||
android:id="@+id/new_playlist_dialog"
|
android:id="@+id/new_playlist_dialog"
|
||||||
android:name="org.oxycblt.auxio.music.decision.NewPlaylistDialog"
|
android:name="org.oxycblt.auxio.music.decision.NewPlaylistDialog"
|
||||||
|
|
Loading…
Reference in a new issue