Update sorting

Make some changes to the sorting system.
This commit is contained in:
OxygenCobalt 2021-01-04 15:38:10 -07:00
parent 119078fc77
commit fcc6a7e8d7
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 72 additions and 65 deletions

View file

@ -137,19 +137,27 @@ class AlbumDetailFragment : DetailFragment() {
} }
/** /**
* Calculate the position and and scroll to a currently playing item. * Scroll to the currently playing item.
*/ */
private fun scrollToPlayingItem() { private fun scrollToPlayingItem() {
// Calculate where the item for the currently played song is, and scroll to there // Calculate where the item for the currently played song is, -1 if it isnt here
val pos = detailModel.albumSortMode.value!!.getSortedSongList( val pos = detailModel.albumSortMode.value!!.getSortedSongList(
detailModel.currentAlbum.value!!.songs detailModel.currentAlbum.value!!.songs
).indexOf(playbackModel.song.value) ).indexOf(playbackModel.song.value)
if (pos != -1) { if (pos != -1) {
binding.detailRecycler.post { binding.detailRecycler.post {
// Make sure to increment the position to make up for the detail header
binding.detailRecycler.layoutManager?.startSmoothScroll( binding.detailRecycler.layoutManager?.startSmoothScroll(
CenterSmoothScroller(requireContext(), pos.inc()) CenterSmoothScroller(requireContext(), pos.inc())
) )
// If the recyclerview can scroll, its certain that it will have to scroll to
// correctly center the playing item, so make sure that the Toolbar is lifted in
// that case.
if (binding.detailRecycler.computeVerticalScrollRange() > binding.detailRecycler.height) {
binding.detailAppbar.isLifted = true
}
} }
detailModel.doneWithNavToItem() detailModel.doneWithNavToItem()

View file

@ -27,6 +27,7 @@ import kotlin.random.Random
* *
* All access should be done with [PlaybackStateManager.getInstance]. * All access should be done with [PlaybackStateManager.getInstance].
* @author OxygenCobalt * @author OxygenCobalt
* // TODO: Sort queues
*/ */
class PlaybackStateManager private constructor() { class PlaybackStateManager private constructor() {
// Playback // Playback
@ -675,7 +676,7 @@ class PlaybackStateManager private constructor() {
} }
/** /**
* Back the current state into a [PlaybackState] to be saved. * Pack the current state into a [PlaybackState] to be saved.
* @return A [PlaybackState] reflecting the current state. * @return A [PlaybackState] reflecting the current state.
*/ */
private fun packToPlaybackState(): PlaybackState { private fun packToPlaybackState(): PlaybackState {
@ -696,28 +697,6 @@ class PlaybackStateManager private constructor() {
) )
} }
/**
* Pack the queue into a list of [QueueItem]s to be saved.
* @return A list of packed queue items.
*/
private fun packQueue(): List<QueueItem> {
val unified = mutableListOf<QueueItem>()
var queueItemId = 0L
mUserQueue.forEach {
unified.add(QueueItem(queueItemId, it.name, it.album.name, true))
queueItemId++
}
mQueue.forEach {
unified.add(QueueItem(queueItemId, it.name, it.album.name, false))
queueItemId++
}
return unified
}
/** /**
* Unpack the state from a [PlaybackState] * Unpack the state from a [PlaybackState]
* @param playbackState The state to unpack. * @param playbackState The state to unpack.
@ -742,6 +721,28 @@ class PlaybackStateManager private constructor() {
} }
} }
/**
* Pack the queue into a list of [QueueItem]s to be saved.
* @return A list of packed queue items.
*/
private fun packQueue(): List<QueueItem> {
val unified = mutableListOf<QueueItem>()
var queueItemId = 0L
mUserQueue.forEach {
unified.add(QueueItem(queueItemId, it.name, it.album.name, true))
queueItemId++
}
mQueue.forEach {
unified.add(QueueItem(queueItemId, it.name, it.album.name, false))
queueItemId++
}
return unified
}
/** /**
* Unpack a list of queue items into a queue & user queue. * Unpack a list of queue items into a queue & user queue.
* @param queueItems The list of [QueueItem]s to unpack. * @param queueItems The list of [QueueItem]s to unpack.
@ -810,20 +811,14 @@ class PlaybackStateManager private constructor() {
* Create an ordered queue based on an [Album]. * Create an ordered queue based on an [Album].
*/ */
private fun orderSongsInAlbum(album: Album): MutableList<Song> { private fun orderSongsInAlbum(album: Album): MutableList<Song> {
return album.songs.sortedBy { it.track }.toMutableList() return SortMode.NUMERIC_DOWN.getSortedSongList(album.songs).toMutableList()
} }
/** /**
* Create an ordered queue based on an [Artist]. * Create an ordered queue based on an [Artist].
*/ */
private fun orderSongsInArtist(artist: Artist): MutableList<Song> { private fun orderSongsInArtist(artist: Artist): MutableList<Song> {
val final = mutableListOf<Song>() return SortMode.NUMERIC_DOWN.getSortedArtistSongList(artist.songs).toMutableList()
artist.albums.sortedByDescending { it.year }.forEach { album ->
final.addAll(album.songs.sortedBy { it.track })
}
return final
} }
/** /**

View file

@ -11,6 +11,7 @@ import org.oxycblt.auxio.music.Song
/** /**
* An enum for the current sorting mode. Contains helper functions to sort lists based * An enum for the current sorting mode. Contains helper functions to sort lists based
* off the given sorting mode. * off the given sorting mode.
* TODO: Improve sorting by separating UP/DOWN from what should be sorted (Names, Tracks, etc)
* @property iconRes The icon for this [SortMode] * @property iconRes The icon for this [SortMode]
* @author OxygenCobalt * @author OxygenCobalt
*/ */
@ -85,6 +86,28 @@ enum class SortMode(@DrawableRes val iconRes: Int) {
} }
} }
/**
* Get a sorted list of songs with regards to an artist.
* @param songs An unsorted list of songs
* @return The sorted list of songs
*/
fun getSortedArtistSongList(songs: List<Song>): List<Song> {
return when (this) {
ALPHA_UP -> songs.sortedWith(
compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }
)
ALPHA_DOWN -> songs.sortedWith(
compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
)
NUMERIC_UP -> songs.sortedWith(compareBy { it.album.year })
NUMERIC_DOWN -> songs.sortedWith(compareByDescending { it.album.year })
else -> songs
}
}
/** /**
* Get a sorted list of BaseModels. Supports alpha + numeric sorting. * Get a sorted list of BaseModels. Supports alpha + numeric sorting.
* @param baseModels An unsorted list of BaseModels. * @param baseModels An unsorted list of BaseModels.

View file

@ -21,7 +21,8 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
* @param activity [AppCompatActivity] required as both a context and ViewModelStore owner. * @param activity [AppCompatActivity] required as both a context and ViewModelStore owner.
* @param anchor [View] This should be centered around * @param anchor [View] This should be centered around
* @param data [BaseModel] this menu corresponds to * @param data [BaseModel] this menu corresponds to
* @param flag Any extra flags to accompany the data. See [Companion] for more details. * @param flag Any extra flags to accompany the data.
* See [FLAG_NONE], [FLAG_IN_ALBUM], [FLAG_IN_ARTIST] and [FLAG_IN_GENRE] for more details.
*/ */
class ActionMenu( class ActionMenu(
activity: AppCompatActivity, activity: AppCompatActivity,
@ -84,11 +85,13 @@ class ActionMenu(
} }
} }
/**
* Determine what to do when a MenuItem is clicked.
*/
private fun onMenuClick(@IdRes id: Int) { private fun onMenuClick(@IdRes id: Int) {
when (id) { when (id) {
R.id.action_play -> { R.id.action_play -> {
when (data) { when (data) {
is Song -> playbackModel.playSong(data, getPlaybackModeFromFlag())
is Album -> playbackModel.playAlbum(data, false) is Album -> playbackModel.playAlbum(data, false)
is Artist -> playbackModel.playArtist(data, false) is Artist -> playbackModel.playArtist(data, false)
is Genre -> playbackModel.playGenre(data, false) is Genre -> playbackModel.playGenre(data, false)
@ -114,21 +117,17 @@ class ActionMenu(
} }
R.id.action_queue_add -> { R.id.action_queue_add -> {
val success = when (data) { when (data) {
is Song -> { is Song -> {
playbackModel.addToUserQueue(data) playbackModel.addToUserQueue(data)
true context.getString(R.string.label_queue_added).createToast(context)
} }
is Album -> { is Album -> {
playbackModel.addToUserQueue(data) playbackModel.addToUserQueue(data)
true context.getString(R.string.label_queue_added).createToast(context)
} }
else -> false else -> {}
}
if (success) {
context.getString(R.string.label_queue_added).createToast(context)
} }
} }
@ -156,17 +155,6 @@ class ActionMenu(
} }
} }
private fun getPlaybackModeFromFlag(): PlaybackMode {
return when (flag) {
FLAG_NONE -> PlaybackMode.ALL_SONGS
FLAG_IN_ALBUM -> PlaybackMode.IN_ALBUM
FLAG_IN_ARTIST -> PlaybackMode.IN_ARTIST
FLAG_IN_GENRE -> PlaybackMode.IN_GENRE
else -> PlaybackMode.ALL_SONGS
}
}
companion object { companion object {
/** No Flags **/ /** No Flags **/
const val FLAG_NONE = -1 const val FLAG_NONE = -1

View file

@ -13,7 +13,7 @@ import android.view.WindowManager
/** /**
* Check if we are in the "Irregular" landscape mode [e.g landscape, but nav bar is on the sides] * Check if we are in the "Irregular" landscape mode [e.g landscape, but nav bar is on the sides]
* Used to disable most of edge-to-edge if that's the case, as I cant get it to work on this mode yet. * Used to disable most of edge-to-edge if that's the case, as I cant get it to work on this mode.
* @return True if we are in the irregular landscape mode, false if not. * @return True if we are in the irregular landscape mode, false if not.
*/ */
fun Activity.isIrregularLandscape(): Boolean { fun Activity.isIrregularLandscape(): Boolean {

View file

@ -16,7 +16,7 @@ import kotlin.reflect.KProperty
* A delegate that creates a binding that can be used as a member variable without nullability or * A delegate that creates a binding that can be used as a member variable without nullability or
* memory leaks. * memory leaks.
* @param bindingFactory The ViewBinding inflation method that should be used * @param bindingFactory The ViewBinding inflation method that should be used
* @param onDestroy Any code that should be run when the binding is destroyed. * @param onDestroy Any code that should be run when the binding is destroyed
*/ */
fun <T : ViewBinding> Fragment.memberBinding( fun <T : ViewBinding> Fragment.memberBinding(
bindingFactory: (LayoutInflater) -> T, bindingFactory: (LayoutInflater) -> T,

View file

@ -13,19 +13,11 @@ import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.annotation.MenuRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
/** /**
* Apply a text color to a [MenuItem] * Apply a text color to a [MenuItem]
@ -116,6 +108,6 @@ fun Fragment.requireCompatActivity(): AppCompatActivity {
if (activity is AppCompatActivity) { if (activity is AppCompatActivity) {
return activity return activity
} else { } else {
error("Required activity to be AppCompatActivity, however it wasn't.") error("Required AppCompatActivity, got ${activity::class.simpleName} instead.")
} }
} }

View file

@ -9,6 +9,7 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/detail_appbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:liftOnScroll="true"> app:liftOnScroll="true">