From ec7f2d979aece245024be6dab89708817310a563 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 25 Jul 2023 16:04:02 -0600 Subject: [PATCH 1/3] list: add sort menu scaffold Add the base dialog that will replace the prior sort menus in-app. --- .../auxio/detail/AlbumDetailFragment.kt | 1 + .../main/java/org/oxycblt/auxio/list/Sort.kt | 29 +++++ .../auxio/list/menu/MenuItemAdapter.kt | 4 +- .../auxio/list/recycler/DialogRecyclerView.kt | 9 -- .../org/oxycblt/auxio/list/sort/SortDialog.kt | 89 +++++++++++++ .../auxio/list/sort/SortModeAdapter.kt | 121 ++++++++++++++++++ app/src/main/res/layout/dialog_menu.xml | 6 +- app/src/main/res/layout/dialog_music_dirs.xml | 2 +- app/src/main/res/layout/dialog_sort.xml | 110 ++++++++++++++++ app/src/main/res/layout/item_sort_mode.xml | 29 +++++ app/src/main/res/menu/sort_album.xml | 2 +- app/src/main/res/menu/sort_artist.xml | 2 +- app/src/main/res/menu/sort_genre.xml | 2 +- app/src/main/res/menu/toolbar_home.xml | 2 +- app/src/main/res/navigation/inner.xml | 9 ++ app/src/main/res/values-ar-rIQ/strings.xml | 2 +- app/src/main/res/values-ar/strings.xml | 2 +- app/src/main/res/values-be/strings.xml | 2 +- app/src/main/res/values-cs/strings.xml | 2 +- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fi/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-gl/strings.xml | 2 +- app/src/main/res/values-hi/strings.xml | 2 +- app/src/main/res/values-hr/strings.xml | 2 +- app/src/main/res/values-hu/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-iw/strings.xml | 2 +- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-lt/strings.xml | 2 +- app/src/main/res/values-ml/strings.xml | 2 +- app/src/main/res/values-nb-rNO/strings.xml | 2 +- app/src/main/res/values-nl/strings.xml | 2 +- app/src/main/res/values-pa/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-pt-rBR/strings.xml | 2 +- app/src/main/res/values-pt-rPT/strings.xml | 2 +- app/src/main/res/values-ro/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-sv/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 46 files changed, 429 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/list/sort/SortDialog.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/list/sort/SortModeAdapter.kt create mode 100644 app/src/main/res/layout/dialog_sort.xml create mode 100644 app/src/main/res/layout/item_sort_mode.xml diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 9fb0b25ed..8dc4bb400 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -76,6 +76,7 @@ class AlbumDetailFragment : override val listModel: ListViewModel by activityViewModels() override val musicModel: MusicViewModel by activityViewModels() override val playbackModel: PlaybackViewModel by activityViewModels() + // Information about what album to display is initially within the navigation arguments // as a UID, as that is the only safe way to parcel an album. private val args: AlbumDetailFragmentArgs by navArgs() diff --git a/app/src/main/java/org/oxycblt/auxio/list/Sort.kt b/app/src/main/java/org/oxycblt/auxio/list/Sort.kt index 8a7203182..013c498d2 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/Sort.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/Sort.kt @@ -165,6 +165,8 @@ data class Sort(val mode: Mode, val direction: Direction) { val intCode: Int /** The item ID of this sort mode in menu resources. */ val itemId: Int + /** The string resource of the human-readable name of this sort mode. */ + val stringRes: Int /** * Get a [Comparator] that sorts [Song]s according to this [Mode]. @@ -223,6 +225,9 @@ data class Sort(val mode: Mode, val direction: Direction) { override val itemId: Int get() = R.id.option_sort_name + override val stringRes: Int + get() = R.string.lbl_name + override fun getSongComparator(direction: Direction) = compareByDynamic(direction, BasicComparator.SONG) @@ -251,6 +256,9 @@ data class Sort(val mode: Mode, val direction: Direction) { override val itemId: Int get() = R.id.option_sort_album + override val stringRes: Int + get() = R.string.lbl_album + override fun getSongComparator(direction: Direction): Comparator = MultiComparator( compareByDynamic(direction, BasicComparator.ALBUM) { it.album }, @@ -271,6 +279,9 @@ data class Sort(val mode: Mode, val direction: Direction) { override val itemId: Int get() = R.id.option_sort_artist + override val stringRes: Int + get() = R.string.lbl_artist + override fun getSongComparator(direction: Direction): Comparator = MultiComparator( compareByDynamic(direction, ListComparator.ARTISTS) { it.artists }, @@ -300,6 +311,9 @@ data class Sort(val mode: Mode, val direction: Direction) { override val itemId: Int get() = R.id.option_sort_year + override val stringRes: Int + get() = R.string.lbl_date + override fun getSongComparator(direction: Direction): Comparator = MultiComparator( compareByDynamic(direction, NullableComparator.DATE_RANGE) { it.album.dates }, @@ -322,6 +336,9 @@ data class Sort(val mode: Mode, val direction: Direction) { override val itemId: Int get() = R.id.option_sort_duration + override val stringRes: Int + get() = R.string.lbl_duration + override fun getSongComparator(direction: Direction): Comparator = MultiComparator( compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.SONG)) @@ -357,6 +374,9 @@ data class Sort(val mode: Mode, val direction: Direction) { override val itemId: Int get() = R.id.option_sort_count + override val stringRes: Int + get() = R.string.lbl_song_count + override fun getAlbumComparator(direction: Direction): Comparator = MultiComparator( compareByDynamic(direction) { it.songs.size }, compareBy(BasicComparator.ALBUM)) @@ -388,6 +408,9 @@ data class Sort(val mode: Mode, val direction: Direction) { override val itemId: Int get() = R.id.option_sort_disc + override val stringRes: Int + get() = R.string.lbl_disc + override fun getSongComparator(direction: Direction): Comparator = MultiComparator( compareByDynamic(direction, NullableComparator.DISC) { it.disc }, @@ -407,6 +430,9 @@ data class Sort(val mode: Mode, val direction: Direction) { override val itemId: Int get() = R.id.option_sort_track + override val stringRes: Int + get() = R.string.lbl_track + override fun getSongComparator(direction: Direction): Comparator = MultiComparator( compareBy(NullableComparator.DISC) { it.disc }, @@ -427,6 +453,9 @@ data class Sort(val mode: Mode, val direction: Direction) { override val itemId: Int get() = R.id.option_sort_date_added + override val stringRes: Int + get() = R.string.lbl_date_added + override fun getSongComparator(direction: Direction): Comparator = MultiComparator( compareByDynamic(direction) { it.dateAdded }, compareBy(BasicComparator.SONG)) diff --git a/app/src/main/java/org/oxycblt/auxio/list/menu/MenuItemAdapter.kt b/app/src/main/java/org/oxycblt/auxio/list/menu/MenuItemAdapter.kt index 1fd336c34..d5b5158f4 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/menu/MenuItemAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/menu/MenuItemAdapter.kt @@ -44,7 +44,7 @@ class MenuItemAdapter(private val listener: ClickableListListener) : } /** - * A [DialogRecyclerView.ViewHolder] that displays a list of menu options based on [MenuItem]. + * A [DialogRecyclerView.ViewHolder] that displays a [MenuItem]. * * @author Alexander Capehart (OxygenCobalt) */ @@ -54,7 +54,7 @@ class MenuItemViewHolder private constructor(private val binding: ItemMenuOption * Bind new data to this instance. * * @param item The new [MenuItem] to bind. - * @param listener An [ClickableListListener] to bind interactions to. + * @param listener A [ClickableListListener] to bind interactions to. */ fun bind(item: MenuItem, listener: ClickableListListener) { listener.bind(item, this) diff --git a/app/src/main/java/org/oxycblt/auxio/list/recycler/DialogRecyclerView.kt b/app/src/main/java/org/oxycblt/auxio/list/recycler/DialogRecyclerView.kt index 90a5786f6..9fc255ce1 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/recycler/DialogRecyclerView.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/recycler/DialogRecyclerView.kt @@ -22,7 +22,6 @@ import android.content.Context import android.util.AttributeSet import android.view.View import android.view.ViewGroup -import android.view.WindowInsets import androidx.annotation.AttrRes import androidx.core.view.isInvisible import androidx.core.view.updatePadding @@ -32,7 +31,6 @@ import com.google.android.material.divider.MaterialDivider import org.oxycblt.auxio.R import org.oxycblt.auxio.list.recycler.DialogRecyclerView.ViewHolder import org.oxycblt.auxio.util.getDimenPixels -import org.oxycblt.auxio.util.systemBarInsetsCompat /** * A [RecyclerView] intended for use in Dialogs, adding features such as: @@ -76,13 +74,6 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr invalidateDividers() } - override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets { - // Update the RecyclerView's padding such that the bottom insets are applied - // while still preserving bottom padding. - updatePadding(bottom = insets.systemBarInsetsCompat.bottom) - return insets - } - override fun onScrolled(dx: Int, dy: Int) { super.onScrolled(dx, dy) // Scroll event occurred, need to update the dividers. 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 new file mode 100644 index 000000000..39e14d199 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/list/sort/SortDialog.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Auxio Project + * SortDialog.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.list.sort + +import android.os.Bundle +import android.view.LayoutInflater +import androidx.core.view.updatePadding +import androidx.recyclerview.widget.RecyclerView +import org.oxycblt.auxio.R +import org.oxycblt.auxio.databinding.DialogSortBinding +import org.oxycblt.auxio.list.ClickableListListener +import org.oxycblt.auxio.list.Sort +import org.oxycblt.auxio.list.adapter.UpdateInstructions +import org.oxycblt.auxio.ui.ViewBindingBottomSheetDialogFragment +import org.oxycblt.auxio.util.systemBarInsetsCompat + +abstract class SortDialog : + ViewBindingBottomSheetDialogFragment(), ClickableListListener { + private val modeAdapter = SortModeAdapter(this) + + abstract fun getInitialSort(): Sort + + abstract fun applyChosenSort(sort: Sort) + + abstract fun getModeChoices(): List + + override fun onCreateBinding(inflater: LayoutInflater) = DialogSortBinding.inflate(inflater) + + override fun onBindingCreated(binding: DialogSortBinding, savedInstanceState: Bundle?) { + super.onBindingCreated(binding, savedInstanceState) + + // --- UI SETUP --- + binding.root.setOnApplyWindowInsetsListener { v, insets -> + v.updatePadding(bottom = insets.systemBarInsetsCompat.bottom) + insets + } + + binding.sortModeRecycler.adapter = modeAdapter + + binding.sortCancel.setOnClickListener { dismiss() } + + binding.sortSave.setOnClickListener { + val initial = getInitialSort() + // FIXME: This won't work for the playlist sort dialog. + val mode = modeAdapter.currentMode ?: initial.mode + val direction = + when (binding.sortDirectionGroup.checkedButtonId) { + R.id.sort_direction_asc -> Sort.Direction.ASCENDING + R.id.sort_direction_dsc -> Sort.Direction.DESCENDING + else -> initial.direction + } + applyChosenSort(Sort(mode, direction)) + dismiss() + } + + // --- STATE SETUP --- + val initial = getInitialSort() + + modeAdapter.update(getModeChoices(), UpdateInstructions.Diff) + modeAdapter.setSelected(initial.mode) + + val directionId = + when (initial.direction) { + Sort.Direction.ASCENDING -> R.id.sort_direction_asc + Sort.Direction.DESCENDING -> R.id.sort_direction_dsc + } + binding.sortDirectionGroup.check(directionId) + } + + override fun onClick(item: Sort.Mode, viewHolder: RecyclerView.ViewHolder) { + modeAdapter.setSelected(item) + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/list/sort/SortModeAdapter.kt b/app/src/main/java/org/oxycblt/auxio/list/sort/SortModeAdapter.kt new file mode 100644 index 000000000..d945a61ef --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/list/sort/SortModeAdapter.kt @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Auxio Project + * SortModeAdapter.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.list.sort + +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import org.oxycblt.auxio.databinding.ItemSortModeBinding +import org.oxycblt.auxio.list.ClickableListListener +import org.oxycblt.auxio.list.Sort +import org.oxycblt.auxio.list.adapter.FlexibleListAdapter +import org.oxycblt.auxio.list.recycler.DialogRecyclerView +import org.oxycblt.auxio.util.context +import org.oxycblt.auxio.util.inflater + +/** + * A [FlexibleListAdapter] that displays a list of [Sort.Mode]s. + * + * @param listener A [ClickableListListener] to bind interactions to. + * @author Alexander Capehart (OxygenCobalt) + */ +class SortModeAdapter(private val listener: ClickableListListener) : + FlexibleListAdapter(SortModeViewHolder.DIFF_CALLBACK) { + /** The currently selected [Sort.Mode] item in this adapter. */ + var currentMode: Sort.Mode? = null + private set + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + SortModeViewHolder.from(parent) + + override fun onBindViewHolder(holder: SortModeViewHolder, position: Int) { + throw NotImplementedError() + } + + override fun onBindViewHolder(holder: SortModeViewHolder, position: Int, payload: List) { + val mode = getItem(position) + if (payload.isEmpty()) { + holder.bind(mode, listener) + } + holder.setSelected(mode == currentMode) + } + + /** + * Select a new [Sort.Mode] option, unselecting the prior one. Does nothing if [mode] equals + * [currentMode]. + * + * @param mode The new [Sort.Mode] to select. Should be in the adapter data. + */ + fun setSelected(mode: Sort.Mode) { + if (mode == currentMode) return + val oldMode = currentList.indexOf(currentMode) + val newMode = currentList.indexOf(mode) + currentMode = mode + if (oldMode > -1) { + notifyItemChanged(oldMode, PAYLOAD_SELECTION_CHANGED) + } + notifyItemChanged(newMode, PAYLOAD_SELECTION_CHANGED) + } + + private companion object { + val PAYLOAD_SELECTION_CHANGED = Any() + } +} + +/** + * A [DialogRecyclerView.ViewHolder] that displays a [Sort.Mode]. + * + * @author Alexander Capehart (OxygenCobalt) + */ +class SortModeViewHolder private constructor(private val binding: ItemSortModeBinding) : + DialogRecyclerView.ViewHolder(binding.root) { + /** + * Bind new data to this instance. + * + * @param mode The new [Sort.Mode] to bind. + * @param listener A [ClickableListListener] to bind interactions to. + */ + fun bind(mode: Sort.Mode, listener: ClickableListListener) { + listener.bind(mode, this) + binding.sortRadio.text = binding.context.getString(mode.stringRes) + } + + /** + * Set if this view should be shown as selected or not. + * + * @param selected True if selected, false if not. + */ + fun setSelected(selected: Boolean) { + binding.sortRadio.isChecked = selected + } + + companion object { + fun from(parent: View) = + SortModeViewHolder(ItemSortModeBinding.inflate(parent.context.inflater)) + + val DIFF_CALLBACK = + object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Sort.Mode, newItem: Sort.Mode) = + oldItem == newItem + + override fun areContentsTheSame(oldItem: Sort.Mode, newItem: Sort.Mode) = + oldItem == newItem + } + } +} diff --git a/app/src/main/res/layout/dialog_menu.xml b/app/src/main/res/layout/dialog_menu.xml index b38f3938f..1a5a7a605 100644 --- a/app/src/main/res/layout/dialog_menu.xml +++ b/app/src/main/res/layout/dialog_menu.xml @@ -13,7 +13,7 @@ + android:layout_height="wrap_content"> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_music_dirs.xml b/app/src/main/res/layout/dialog_music_dirs.xml index 49458926a..fa48b8a9c 100644 --- a/app/src/main/res/layout/dialog_music_dirs.xml +++ b/app/src/main/res/layout/dialog_music_dirs.xml @@ -27,7 +27,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="@dimen/spacing_large" - android:layout_marginTop="@dimen/spacing_small" + android:layout_marginTop="@dimen/spacing_tiny" android:layout_marginEnd="@dimen/spacing_large" android:gravity="center" app:layout_constraintTop_toBottomOf="@+id/dirs_mode_header" diff --git a/app/src/main/res/layout/dialog_sort.xml b/app/src/main/res/layout/dialog_sort.xml new file mode 100644 index 000000000..4fa676852 --- /dev/null +++ b/app/src/main/res/layout/dialog_sort.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_sort_mode.xml b/app/src/main/res/layout/item_sort_mode.xml new file mode 100644 index 000000000..91638968e --- /dev/null +++ b/app/src/main/res/layout/item_sort_mode.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/sort_album.xml b/app/src/main/res/menu/sort_album.xml index 44f9744aa..2e172f698 100644 --- a/app/src/main/res/menu/sort_album.xml +++ b/app/src/main/res/menu/sort_album.xml @@ -16,6 +16,6 @@ android:title="@string/lbl_sort_asc" /> + android:title="@string/lbl_sort_dsc" /> \ No newline at end of file diff --git a/app/src/main/res/menu/sort_artist.xml b/app/src/main/res/menu/sort_artist.xml index 50ba847ba..796be63b3 100644 --- a/app/src/main/res/menu/sort_artist.xml +++ b/app/src/main/res/menu/sort_artist.xml @@ -22,6 +22,6 @@ android:title="@string/lbl_sort_asc" /> + android:title="@string/lbl_sort_dsc" /> \ No newline at end of file diff --git a/app/src/main/res/menu/sort_genre.xml b/app/src/main/res/menu/sort_genre.xml index ad5d920c8..0c525bbc9 100644 --- a/app/src/main/res/menu/sort_genre.xml +++ b/app/src/main/res/menu/sort_genre.xml @@ -25,6 +25,6 @@ android:title="@string/lbl_sort_asc" /> + android:title="@string/lbl_sort_dsc" /> \ No newline at end of file diff --git a/app/src/main/res/menu/toolbar_home.xml b/app/src/main/res/menu/toolbar_home.xml index 278206a03..7e27b3a49 100644 --- a/app/src/main/res/menu/toolbar_home.xml +++ b/app/src/main/res/menu/toolbar_home.xml @@ -45,7 +45,7 @@ android:title="@string/lbl_sort_asc" /> + android:title="@string/lbl_sort_dsc" /> diff --git a/app/src/main/res/navigation/inner.xml b/app/src/main/res/navigation/inner.xml index fa9520ec3..f782d8456 100644 --- a/app/src/main/res/navigation/inner.xml +++ b/app/src/main/res/navigation/inner.xml @@ -162,6 +162,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/values-ar-rIQ/strings.xml b/app/src/main/res/values-ar-rIQ/strings.xml index f2bf908ec..9dc9abb35 100644 --- a/app/src/main/res/values-ar-rIQ/strings.xml +++ b/app/src/main/res/values-ar-rIQ/strings.xml @@ -157,7 +157,7 @@ تشغيل كل الاغاني بشكل عشوائي حسنا اعادة الحالة - تنازلي + تنازلي عرض الخصائص مسح الحالة مباشر diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 8689e2dda..fcbf1ee96 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -61,7 +61,7 @@ تاريخ الإضافة فرز تصاعدياً - تنازلياً + تنازلياً يتم الآن تشغيل المُعادِل تشغيل diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index e49035776..1b783cd27 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -77,7 +77,7 @@ Ператасаваць Адмяніць Па ўзрастанні - Па змяншэнні + Па змяншэнні Гуляць далей Дадаць у чаргу Эквалайзер diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 89bc807df..d53bff062 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -279,7 +279,7 @@ Přehrávání Knihovna Perzistence - Sestupně + Sestupně Seznamy skladeb Obrázek seznamu skladeb pro %s Seznam skladeb diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 00b9ac5dc..09d153413 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -270,7 +270,7 @@ Ton und Wiedergabeverhalten konfigurieren Persistenz Lautstärkeanpassung ReplayGain - Absteigend + Absteigend Wiedergabelistenbild für %s Wiedergabeliste Wiedergabelisten diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f46f63bdf..c07b03dc7 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -274,7 +274,7 @@ Cambiar el tema y los colores de la aplicación Personalizar los controles y el comportamiento de la interfaz de usuario Biblioteca - Descendente + Descendente Listas de reproducción Imagen de la lista de reproducción para %s Lista de reproducción diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index d99365815..93cb194d7 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -33,7 +33,7 @@ Levy Raita Lisäyspäivä - Laskevasti + Laskevasti Nyt toistetaan Taajuuskorjain Toista diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 4ee073246..822484268 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -157,7 +157,7 @@ Surveillance de votre bibliothèque musicale pour les changements… Couvertures arrondies Activer les coins arrondis sur des éléments d\'interface utilisateur supplémentaires (nécessite que les couvertures d\'album soient arrondies) - Descendant + Descendant Etat restauré Personnaliser les commandes et le comportement de l\'interface utilisateur Passer au suivant diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index b00782948..5c3288e6a 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -124,7 +124,7 @@ EP en directo EP remix Ascendente - Descendente + Descendente Ecualizador Aleatorio seleccionado Frecuencia de mostraxe diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 61b83f80b..bb80b279f 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -99,7 +99,7 @@ सभी गीतों से चलाएं %s हटाएँ\? इसे पूर्ववत नहीं किया जा सकता। लोड किए गए गाने: %d - अवरोही + अवरोही चयनित चलाएँ फेरबदल का चयन किया गया स्थिति साफ की गई diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 8ab7b06c6..1ce8b10cd 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -255,7 +255,7 @@ Resetiraj ReplayGain izjednačavanje glasnoće Mape - Silazni + Silazni Promijenite temu i boje aplikacije Prilagodite kontrole i ponašanje korisničkog sučelja Upravljajte učitavanjem glazbe i slika diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 254093dda..6c0b11c88 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -74,7 +74,7 @@ Remix EP Név Dátum - Csökkenő + Csökkenő Kiválasztott lejátszása Új lejátszólista Ismeretlen műfaj diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 0107cf837..483d5b02f 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -274,7 +274,7 @@ Persistenza Personalizza controlli e comportamento dell\'UI Configura comportamento di suono e riproduzione - Discendente + Discendente Playlist Playlist Ordinazione intelligente diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index e439eaf5a..31b5677fd 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -36,7 +36,7 @@ תאריך הוספה מיון עולה - יורד + יורד מושמע כעת איקוולייזר ניגון diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index b571ec42e..21849864b 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -60,7 +60,7 @@ ファイル名 追加した日付け サンプルレート - 降順 + 降順 再生 シャフル 選択曲をシャフル diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 2de3ff1bd..6716d4d77 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -270,7 +270,7 @@ 지속 동작 UI 제어 및 동작 커스텀 - 내림차순 + 내림차순 재생목록 재생목록 %s의 재생 목록 이미지 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index f3d63c113..f40489591 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -267,7 +267,7 @@ ReplayGain Aplankalai Pastovumas - Mažėjantis + Mažėjantis Teisingai surūšiuoti pavadinimus, kurie prasideda skaičiais arba žodžiais, tokiais kaip „the“ (geriausiai veikia su anglų kalbos muzika) Išmanusis rūšiavimas Grojaraštis diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 1f05558d8..82f0ffe92 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -90,7 +90,7 @@ കലാകാരനിലേക്ക് പോകുക സവിശേഷതകൾ കാണുക സ്ഥിതി സംരക്ഷിച്ചു - അവരോഹണം + അവരോഹണം സ്ഥിതി പുനഃസ്ഥാപിച്ചു വിക്കി സ്ഥിതി മായ്ച്ചു diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index a7e904bb3..5913008c0 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -201,7 +201,7 @@ %d album %d album - Synkende + Synkende Spor Dato tillagt Sorter diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 4e1cd3ec0..4d29de5df 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -246,7 +246,7 @@ Genre Ampersand (&) Bewerken - Aflopend + Aflopend Kan status niet wissen Afspeellijst-afbeelding voor %s Geen nummers diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 97817e254..8f748482d 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -75,7 +75,7 @@ ਐਂਡਰੌਇਡ ਲਈ ਇੱਕ ਸਰਲ, ਤਰਕਸੰਗਤ ਸੰਗੀਤ ਪਲੇਅਰ। ਖੋਜੋ ਗੀਤ ਦੀ ਗਿਣਤੀ - ਘਟਦੇ ਹੋਏ + ਘਟਦੇ ਹੋਏ ਚੁਣਿਆ ਹੋਇਆ ਚਲਾਓ ਕਲਾਕਾਰ \'ਤੇ ਜਾਓ ਫਾਈਲ ਦਾ ਨਾਮ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index fc0c20794..b4e179c74 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -275,7 +275,7 @@ Muzyka Nie można wyczyścić stanu odtwarzania Nie można zapisać stanu odtwarzania - Malejąco + Malejąco Playlisty Playlista Obraz playlisty %s diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index af9052aa1..ec43dcdc8 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -272,7 +272,7 @@ Persistência Comportamento Pastas - Descendente + Descendente Ignorar artigos ao classificar Ignore palavras como \"the\" ao classificar por nome (funciona melhor com músicas em inglês) \ No newline at end of file diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index d56b5eee1..8140c3ad1 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -260,7 +260,7 @@ %d artistas Equalização de volume ReplayGain - Descendente + Descendente Mude o tema e as cores do app Personalize os controles e o comportamento da interface do usuário Controle como a música e as imagens são carregadas diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index ed696e932..793b4ec7d 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -137,7 +137,7 @@ Redare selecție Listă de redare Liste de redare - Descrescător + Descrescător Selecție aleatorie aleasă Treceți la următoarea Redă de la artist diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 13a837c1b..6f311d10a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -277,7 +277,7 @@ Воспроизведение Папки Состояние воспроизведения - По убыванию + По убыванию Плейлист Плейлисты Обложка плейлиста для %s diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 8b958539b..e9547d169 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -39,7 +39,7 @@ Spår Datum tillagt Stigande - Fallande + Fallande Nu spelar Utjämnare Spela diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3a7d65394..86915531e 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -263,7 +263,7 @@ Oynatma Kütüphane Kalıcılık - Azalan + Azalan Uygulamanın temasını ve renklerini değiştirin Klasörler Arayüz kontrollerini ve davranışını özelleştirin diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a4419687e..a8c4959d6 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -274,7 +274,7 @@ Стан відтворення Налаштуйте звук і поведінку при відтворенні Папки - За спаданням + За спаданням Зображення списку відтворення для %s Список відтворення Списки відтворення diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index c2a6138f2..1b91a7870 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -268,7 +268,7 @@ 文件夹 音乐 配置声音和播放行为 - 降序 + 降序 播放列表 播放列表 %s 的播放列表图片 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0e47fc0f1..d15b90062 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -105,7 +105,7 @@ Sort Ascending - Descending + Descending Now playing Equalizer From 3340914c5167f3ca841a1a542d78f95ae0e695e6 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 25 Jul 2023 16:47:25 -0600 Subject: [PATCH 2/3] detail: switch to sort dialogs Use the new sort dialogs on in detail views. --- .../auxio/detail/AlbumDetailFragment.kt | 26 +------- .../auxio/detail/ArtistDetailFragment.kt | 28 +------- .../oxycblt/auxio/detail/DetailViewModel.kt | 22 +++++-- .../auxio/detail/GenreDetailFragment.kt | 26 +------- .../auxio/detail/sort/AlbumSongSortDialog.kt | 58 +++++++++++++++++ .../auxio/detail/sort/ArtistSongSortDialog.kt | 59 +++++++++++++++++ .../auxio/detail/sort/GenreSongSortDialog.kt | 64 +++++++++++++++++++ .../org/oxycblt/auxio/list/ListFragment.kt | 39 ----------- .../main/java/org/oxycblt/auxio/list/Sort.kt | 5 +- app/src/main/res/menu/sort_album.xml | 21 ------ app/src/main/res/menu/sort_artist.xml | 27 -------- app/src/main/res/menu/sort_genre.xml | 30 --------- app/src/main/res/navigation/inner.xml | 36 ++++++++--- 13 files changed, 230 insertions(+), 211 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/detail/sort/AlbumSongSortDialog.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/detail/sort/ArtistSongSortDialog.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/detail/sort/GenreSongSortDialog.kt delete mode 100644 app/src/main/res/menu/sort_album.xml delete mode 100644 app/src/main/res/menu/sort_artist.xml delete mode 100644 app/src/main/res/menu/sort_genre.xml diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 8dc4bb400..40f5d5796 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -41,7 +41,6 @@ import org.oxycblt.auxio.list.Item import org.oxycblt.auxio.list.ListFragment import org.oxycblt.auxio.list.ListViewModel import org.oxycblt.auxio.list.Menu -import org.oxycblt.auxio.list.Sort import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.MusicParent @@ -196,30 +195,7 @@ class AlbumDetailFragment : } override fun onOpenSortMenu(anchor: View) { - openMenu(anchor, R.menu.sort_album) { - // Select the corresponding sort mode option - val sort = detailModel.albumSongSort - unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true - // Select the corresponding sort direction option - val directionItemId = - when (sort.direction) { - Sort.Direction.ASCENDING -> R.id.option_sort_asc - Sort.Direction.DESCENDING -> R.id.option_sort_dec - } - unlikelyToBeNull(menu.findItem(directionItemId)).isChecked = true - setOnMenuItemClickListener { item -> - item.isChecked = !item.isChecked - detailModel.albumSongSort = - when (item.itemId) { - // Sort direction options - R.id.option_sort_asc -> sort.withDirection(Sort.Direction.ASCENDING) - R.id.option_sort_dec -> sort.withDirection(Sort.Direction.DESCENDING) - // Any other option is a sort mode - else -> sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId))) - } - true - } - } + findNavController().navigateSafe(AlbumDetailFragmentDirections.sort()) } override fun onNavigateToParentArtist() { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 9997496b6..04b3b8c2a 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -41,7 +41,6 @@ import org.oxycblt.auxio.list.Item import org.oxycblt.auxio.list.ListFragment import org.oxycblt.auxio.list.ListViewModel import org.oxycblt.auxio.list.Menu -import org.oxycblt.auxio.list.Sort import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Music @@ -202,32 +201,7 @@ class ArtistDetailFragment : } override fun onOpenSortMenu(anchor: View) { - openMenu(anchor, R.menu.sort_artist) { - // Select the corresponding sort mode option - val sort = detailModel.artistSongSort - unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true - // Select the corresponding sort direction option - val directionItemId = - when (sort.direction) { - Sort.Direction.ASCENDING -> R.id.option_sort_asc - Sort.Direction.DESCENDING -> R.id.option_sort_dec - } - unlikelyToBeNull(menu.findItem(directionItemId)).isChecked = true - setOnMenuItemClickListener { item -> - item.isChecked = !item.isChecked - - detailModel.artistSongSort = - when (item.itemId) { - // Sort direction options - R.id.option_sort_asc -> sort.withDirection(Sort.Direction.ASCENDING) - R.id.option_sort_dec -> sort.withDirection(Sort.Direction.DESCENDING) - // Any other option is a sort mode - else -> sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId))) - } - - true - } - } + findNavController().navigateSafe(ArtistDetailFragmentDirections.sort()) } private fun updateArtist(artist: Artist?) { 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 ea618078c..735c15019 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -109,13 +109,8 @@ constructor( get() = _albumInstructions /** The current [Sort] used for [Song]s in [albumList]. */ - var albumSongSort: Sort + val albumSongSort: Sort get() = musicSettings.albumSongSort - set(value) { - musicSettings.albumSongSort = value - // Refresh the album list to reflect the new sort. - currentAlbum.value?.let { refreshAlbumList(it, true) } - } /** The [PlaySong] instructions to use when playing a [Song] from [Album] details. */ val playInAlbumWith @@ -365,6 +360,11 @@ constructor( } } + fun applyAlbumSongSort(sort: Sort) { + musicSettings.albumSongSort = sort + _currentAlbum.value?.let { refreshAlbumList(it, true) } + } + /** * Set a new [currentArtist] from it's [Music.UID]. [currentArtist] and [artistList] will be * updated to align with the new [Artist]. @@ -380,6 +380,11 @@ constructor( } } + fun applyArtistSongSort(sort: Sort) { + musicSettings.artistSongSort = sort + _currentArtist.value?.let { refreshArtistList(it, true) } + } + /** * Set a new [currentGenre] from it's [Music.UID]. [currentGenre] and [genreList] will be * updated to align with the new album. @@ -395,6 +400,11 @@ constructor( } } + fun applyGenreSongSort(sort: Sort) { + musicSettings.genreSongSort = sort + _currentGenre.value?.let { refreshGenreList(it, true) } + } + /** * Set a new [currentPlaylist] from it's [Music.UID]. If the [Music.UID] differs, * [currentPlaylist] and [currentPlaylist] will be updated to align with the new album. diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index dfb9adc29..49b6f451e 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -41,7 +41,6 @@ import org.oxycblt.auxio.list.Item import org.oxycblt.auxio.list.ListFragment import org.oxycblt.auxio.list.ListViewModel import org.oxycblt.auxio.list.Menu -import org.oxycblt.auxio.list.Sort import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Music @@ -199,30 +198,7 @@ class GenreDetailFragment : } override fun onOpenSortMenu(anchor: View) { - openMenu(anchor, R.menu.sort_genre) { - // Select the corresponding sort mode option - val sort = detailModel.genreSongSort - unlikelyToBeNull(menu.findItem(sort.mode.itemId)).isChecked = true - // Select the corresponding sort direction option - val directionItemId = - when (sort.direction) { - Sort.Direction.ASCENDING -> R.id.option_sort_asc - Sort.Direction.DESCENDING -> R.id.option_sort_dec - } - unlikelyToBeNull(menu.findItem(directionItemId)).isChecked = true - setOnMenuItemClickListener { item -> - item.isChecked = !item.isChecked - detailModel.genreSongSort = - when (item.itemId) { - // Sort direction options - R.id.option_sort_asc -> sort.withDirection(Sort.Direction.ASCENDING) - R.id.option_sort_dec -> sort.withDirection(Sort.Direction.DESCENDING) - // Any other option is a sort mode - else -> sort.withMode(unlikelyToBeNull(Sort.Mode.fromItemId(item.itemId))) - } - true - } - } + findNavController().navigateSafe(GenreDetailFragmentDirections.sort()) } private fun updatePlaylist(genre: Genre?) { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/sort/AlbumSongSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/sort/AlbumSongSortDialog.kt new file mode 100644 index 000000000..8c7290e98 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/detail/sort/AlbumSongSortDialog.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Auxio Project + * AlbumSongSortDialog.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.Album +import org.oxycblt.auxio.util.collectImmediately +import org.oxycblt.auxio.util.logD + +@AndroidEntryPoint +class AlbumSongSortDialog : SortDialog() { + private val detailModel: DetailViewModel by activityViewModels() + + override fun onBindingCreated(binding: DialogSortBinding, savedInstanceState: Bundle?) { + super.onBindingCreated(binding, savedInstanceState) + + // --- VIEWMODEL SETUP --- + collectImmediately(detailModel.currentAlbum, ::updateAlbum) + } + + override fun getInitialSort() = detailModel.albumSongSort + + override fun applyChosenSort(sort: Sort) { + detailModel.applyAlbumSongSort(sort) + } + + override fun getModeChoices() = listOf(Sort.Mode.ByDisc, Sort.Mode.ByTrack) + + private fun updateAlbum(album: Album?) { + if (album == null) { + logD("No album to sort, navigating away") + findNavController().navigateUp() + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/detail/sort/ArtistSongSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/sort/ArtistSongSortDialog.kt new file mode 100644 index 000000000..a745bd11a --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/detail/sort/ArtistSongSortDialog.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Auxio Project + * ArtistSongSortDialog.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.Artist +import org.oxycblt.auxio.util.collectImmediately +import org.oxycblt.auxio.util.logD + +@AndroidEntryPoint +class ArtistSongSortDialog : SortDialog() { + private val detailModel: DetailViewModel by activityViewModels() + + override fun onBindingCreated(binding: DialogSortBinding, savedInstanceState: Bundle?) { + super.onBindingCreated(binding, savedInstanceState) + + // --- VIEWMODEL SETUP --- + collectImmediately(detailModel.currentArtist, ::updateArtist) + } + + override fun getInitialSort() = detailModel.artistSongSort + + override fun applyChosenSort(sort: Sort) { + detailModel.applyArtistSongSort(sort) + } + + override fun getModeChoices() = + listOf(Sort.Mode.ByName, Sort.Mode.ByAlbum, Sort.Mode.ByDate, Sort.Mode.ByDuration) + + private fun updateArtist(artist: Artist?) { + if (artist == null) { + logD("No artist to sort, navigating away") + findNavController().navigateUp() + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/detail/sort/GenreSongSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/sort/GenreSongSortDialog.kt new file mode 100644 index 000000000..d491310c8 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/detail/sort/GenreSongSortDialog.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 Auxio Project + * GenreSongSortDialog.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.Genre +import org.oxycblt.auxio.util.collectImmediately +import org.oxycblt.auxio.util.logD + +@AndroidEntryPoint +class GenreSongSortDialog : SortDialog() { + private val detailModel: DetailViewModel by activityViewModels() + + override fun onBindingCreated(binding: DialogSortBinding, savedInstanceState: Bundle?) { + super.onBindingCreated(binding, savedInstanceState) + + // --- VIEWMODEL SETUP --- + collectImmediately(detailModel.currentGenre, ::updateGenre) + } + + override fun getInitialSort() = detailModel.genreSongSort + + override fun applyChosenSort(sort: Sort) { + detailModel.applyGenreSongSort(sort) + } + + override fun getModeChoices() = + listOf( + Sort.Mode.ByName, + Sort.Mode.ByArtist, + Sort.Mode.ByAlbum, + Sort.Mode.ByDate, + Sort.Mode.ByDuration) + + private fun updateGenre(genre: Genre?) { + if (genre == null) { + logD("No genre to sort, navigating away") + findNavController().navigateUp() + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt b/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt index 4d2d45c14..546b03a49 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt @@ -18,14 +18,9 @@ package org.oxycblt.auxio.list -import android.view.View -import androidx.annotation.MenuRes -import androidx.appcompat.widget.PopupMenu -import androidx.core.view.MenuCompat import androidx.recyclerview.widget.RecyclerView import androidx.viewbinding.ViewBinding import org.oxycblt.auxio.music.Music -import org.oxycblt.auxio.util.logD /** * A Fragment containing a selectable list. @@ -34,14 +29,6 @@ import org.oxycblt.auxio.util.logD */ abstract class ListFragment : SelectionFragment(), SelectableListListener { - private var currentMenu: PopupMenu? = null - - override fun onDestroyBinding(binding: VB) { - super.onDestroyBinding(binding) - currentMenu?.dismiss() - currentMenu = null - } - /** * Called when [onClick] is called, but does not result in the item being selected. This more or * less corresponds to an [onClick] implementation in a non-[ListFragment]. @@ -63,30 +50,4 @@ abstract class ListFragment : final override fun onSelect(item: T) { listModel.select(item) } - - /** - * Open a menu. This menu will be managed by the Fragment and closed when the view is destroyed. - * If a menu is already opened, this call is ignored. - * - * @param anchor The [View] to anchor the menu to. - * @param menuRes The resource of the menu to load. - * @param block A block that is ran within [PopupMenu] that allows further configuration. - */ - protected fun openMenu(anchor: View, @MenuRes menuRes: Int, block: PopupMenu.() -> Unit) { - if (currentMenu != null) { - logD("Menu already present, not launching") - return - } - - logD("Opening popup menu menu") - - currentMenu = - PopupMenu(requireContext(), anchor).apply { - inflate(menuRes) - MenuCompat.setGroupDividerEnabled(menu, true) - block() - setOnDismissListener { currentMenu = null } - show() - } - } } diff --git a/app/src/main/java/org/oxycblt/auxio/list/Sort.kt b/app/src/main/java/org/oxycblt/auxio/list/Sort.kt index 013c498d2..9b453e774 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/Sort.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/Sort.kt @@ -19,6 +19,7 @@ package org.oxycblt.auxio.list import androidx.annotation.IdRes +import java.lang.IllegalStateException import kotlin.math.max import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.R @@ -406,7 +407,7 @@ data class Sort(val mode: Mode, val direction: Direction) { get() = IntegerTable.SORT_BY_DISC override val itemId: Int - get() = R.id.option_sort_disc + get() = throw IllegalStateException() override val stringRes: Int get() = R.string.lbl_disc @@ -428,7 +429,7 @@ data class Sort(val mode: Mode, val direction: Direction) { get() = IntegerTable.SORT_BY_TRACK override val itemId: Int - get() = R.id.option_sort_track + get() = throw IllegalStateException() override val stringRes: Int get() = R.string.lbl_track diff --git a/app/src/main/res/menu/sort_album.xml b/app/src/main/res/menu/sort_album.xml deleted file mode 100644 index 2e172f698..000000000 --- a/app/src/main/res/menu/sort_album.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/sort_artist.xml b/app/src/main/res/menu/sort_artist.xml deleted file mode 100644 index 796be63b3..000000000 --- a/app/src/main/res/menu/sort_artist.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/sort_genre.xml b/app/src/main/res/menu/sort_genre.xml deleted file mode 100644 index 0c525bbc9..000000000 --- a/app/src/main/res/menu/sort_genre.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - \ 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 f782d8456..8956f71fa 100644 --- a/app/src/main/res/navigation/inner.xml +++ b/app/src/main/res/navigation/inner.xml @@ -138,6 +138,9 @@ + @@ -162,11 +165,14 @@ - + + + @@ -198,6 +207,12 @@ app:destination="@id/play_from_genre_dialog" /> + + + @@ -232,6 +250,12 @@ app:destination="@id/play_from_artist_dialog" /> + + - - \ No newline at end of file From 04851893c5cc367233667af0935e5218ee847cdb Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 25 Jul 2023 17:32:03 -0600 Subject: [PATCH 3/3] home: switch to sort dialogs Switch the home view to sort dialogs, and simplify away any listeners that expected popup menus. --- .../auxio/detail/AlbumDetailFragment.kt | 15 +- .../auxio/detail/ArtistDetailFragment.kt | 13 +- .../oxycblt/auxio/detail/DetailViewModel.kt | 96 +++++--- .../auxio/detail/GenreDetailFragment.kt | 13 +- .../auxio/detail/PlaylistDetailFragment.kt | 13 +- .../auxio/detail/list/DetailListAdapter.kt | 4 +- .../auxio/detail/sort/AlbumSongSortDialog.kt | 5 + .../auxio/detail/sort/ArtistSongSortDialog.kt | 5 + .../auxio/detail/sort/GenreSongSortDialog.kt | 5 + .../org/oxycblt/auxio/home/HomeFragment.kt | 103 +------- .../org/oxycblt/auxio/home/HomeViewModel.kt | 231 ++++++++++-------- .../auxio/home/list/AlbumListFragment.kt | 14 +- .../auxio/home/list/ArtistListFragment.kt | 12 +- .../auxio/home/list/GenreListFragment.kt | 12 +- .../auxio/home/list/PlaylistListFragment.kt | 12 +- .../auxio/home/list/SongListFragment.kt | 12 +- .../auxio/home/sort/AlbumSortDialog.kt | 50 ++++ .../auxio/home/sort/ArtistSortDialog.kt | 44 ++++ .../auxio/home/sort/GenreSortDialog.kt | 44 ++++ .../auxio/home/sort/PlaylistSortDialog.kt | 44 ++++ .../oxycblt/auxio/home/sort/SongSortDialog.kt | 50 ++++ .../java/org/oxycblt/auxio/list/Listeners.kt | 5 +- .../main/java/org/oxycblt/auxio/list/Sort.kt | 52 ---- .../oxycblt/auxio/search/SearchFragment.kt | 2 +- app/src/main/res/menu/toolbar_home.xml | 40 +-- app/src/main/res/navigation/inner.xml | 45 ++++ 26 files changed, 546 insertions(+), 395 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/home/sort/AlbumSortDialog.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/home/sort/ArtistSortDialog.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/home/sort/GenreSortDialog.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/home/sort/PlaylistSortDialog.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/home/sort/SongSortDialog.kt diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 40f5d5796..d1b7199f8 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -21,7 +21,6 @@ package org.oxycblt.auxio.detail import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem -import android.view.View import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -111,7 +110,7 @@ class AlbumDetailFragment : adapter = ConcatAdapter(albumHeaderAdapter, albumListAdapter) (layoutManager as GridLayoutManager).setFullWidthLookup { if (it != 0) { - val item = detailModel.albumList.value[it - 1] + val item = detailModel.albumSongList.value[it - 1] item is Divider || item is Header || item is Disc } else { true @@ -123,7 +122,7 @@ class AlbumDetailFragment : // DetailViewModel handles most initialization from the navigation argument. detailModel.setAlbum(args.albumUid) collectImmediately(detailModel.currentAlbum, ::updateAlbum) - collectImmediately(detailModel.albumList, ::updateList) + collectImmediately(detailModel.albumSongList, ::updateList) collect(detailModel.toShow.flow, ::handleShow) collect(listModel.menu.flow, ::handleMenu) collectImmediately(listModel.selected, ::updateSelection) @@ -139,7 +138,7 @@ class AlbumDetailFragment : binding.detailRecycler.adapter = null // Avoid possible race conditions that could cause a bad replace instruction to be consumed // during list initialization and crash the app. Could happen if the user is fast enough. - detailModel.albumInstructions.consume() + detailModel.albumSongInstructions.consume() } override fun onMenuItemClick(item: MenuItem): Boolean { @@ -182,7 +181,7 @@ class AlbumDetailFragment : playbackModel.play(item, detailModel.playInAlbumWith) } - override fun onOpenMenu(item: Song, anchor: View) { + override fun onOpenMenu(item: Song) { listModel.openMenu(R.menu.item_album_song, item, detailModel.playInAlbumWith) } @@ -194,7 +193,7 @@ class AlbumDetailFragment : playbackModel.shuffle(unlikelyToBeNull(detailModel.currentAlbum.value)) } - override fun onOpenSortMenu(anchor: View) { + override fun onOpenSortMenu() { findNavController().navigateSafe(AlbumDetailFragmentDirections.sort()) } @@ -213,7 +212,7 @@ class AlbumDetailFragment : } private fun updateList(list: List) { - albumListAdapter.update(list, detailModel.albumInstructions.consume()) + albumListAdapter.update(list, detailModel.albumSongInstructions.consume()) } private fun handleShow(show: Show?) { @@ -339,7 +338,7 @@ class AlbumDetailFragment : private fun scrollToAlbumSong(song: Song) { // Calculate where the item for the currently played song is - val pos = detailModel.albumList.value.indexOf(song) + val pos = detailModel.albumSongList.value.indexOf(song) if (pos != -1) { // Only scroll if the song is within this album. diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 04b3b8c2a..c50fb16b4 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -21,7 +21,6 @@ package org.oxycblt.auxio.detail import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem -import android.view.View import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -110,7 +109,7 @@ class ArtistDetailFragment : (layoutManager as GridLayoutManager).setFullWidthLookup { if (it != 0) { val item = - detailModel.artistList.value.getOrElse(it - 1) { + detailModel.artistSongList.value.getOrElse(it - 1) { return@setFullWidthLookup false } item is Divider || item is Header @@ -124,7 +123,7 @@ class ArtistDetailFragment : // DetailViewModel handles most initialization from the navigation argument. detailModel.setArtist(args.artistUid) collectImmediately(detailModel.currentArtist, ::updateArtist) - collectImmediately(detailModel.artistList, ::updateList) + collectImmediately(detailModel.artistSongList, ::updateList) collect(detailModel.toShow.flow, ::handleShow) collect(listModel.menu.flow, ::handleMenu) collectImmediately(listModel.selected, ::updateSelection) @@ -140,7 +139,7 @@ class ArtistDetailFragment : binding.detailRecycler.adapter = null // Avoid possible race conditions that could cause a bad replace instruction to be consumed // during list initialization and crash the app. Could happen if the user is fast enough. - detailModel.artistInstructions.consume() + detailModel.artistSongInstructions.consume() } override fun onMenuItemClick(item: MenuItem): Boolean { @@ -183,7 +182,7 @@ class ArtistDetailFragment : } } - override fun onOpenMenu(item: Music, anchor: View) { + override fun onOpenMenu(item: Music) { when (item) { is Song -> listModel.openMenu(R.menu.item_artist_song, item, detailModel.playInArtistWith) @@ -200,7 +199,7 @@ class ArtistDetailFragment : playbackModel.shuffle(unlikelyToBeNull(detailModel.currentArtist.value)) } - override fun onOpenSortMenu(anchor: View) { + override fun onOpenSortMenu() { findNavController().navigateSafe(ArtistDetailFragmentDirections.sort()) } @@ -227,7 +226,7 @@ class ArtistDetailFragment : } private fun updateList(list: List) { - artistListAdapter.update(list, detailModel.artistInstructions.consume()) + artistListAdapter.update(list, detailModel.artistSongInstructions.consume()) } private fun handleShow(show: Show?) { 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 735c15019..09d0cb01d 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -98,17 +98,17 @@ constructor( val currentAlbum: StateFlow get() = _currentAlbum - private val _albumList = MutableStateFlow(listOf()) + private val _albumSongList = MutableStateFlow(listOf()) /** The current list data derived from [currentAlbum]. */ - val albumList: StateFlow> - get() = _albumList + val albumSongList: StateFlow> + get() = _albumSongList - private val _albumInstructions = MutableEvent() - /** Instructions for updating [albumList] in the UI. */ - val albumInstructions: Event - get() = _albumInstructions + private val _albumSongInstructions = MutableEvent() + /** Instructions for updating [albumSongList] in the UI. */ + val albumSongInstructions: Event + get() = _albumSongInstructions - /** The current [Sort] used for [Song]s in [albumList]. */ + /** The current [Sort] used for [Song]s in [albumSongList]. */ val albumSongSort: Sort get() = musicSettings.albumSongSort @@ -123,15 +123,16 @@ constructor( val currentArtist: StateFlow get() = _currentArtist - private val _artistList = MutableStateFlow(listOf()) + private val _artistSongList = MutableStateFlow(listOf()) /** The current list derived from [currentArtist]. */ - val artistList: StateFlow> = _artistList - private val _artistInstructions = MutableEvent() - /** Instructions for updating [artistList] in the UI. */ - val artistInstructions: Event - get() = _artistInstructions + val artistSongList: StateFlow> = _artistSongList - /** The current [Sort] used for [Song]s in [artistList]. */ + private val _artistSongInstructions = MutableEvent() + /** Instructions for updating [artistSongList] in the UI. */ + val artistSongInstructions: Event + get() = _artistSongInstructions + + /** The current [Sort] used for [Song]s in [artistSongList]. */ var artistSongSort: Sort get() = musicSettings.artistSongSort set(value) { @@ -151,15 +152,16 @@ constructor( val currentGenre: StateFlow get() = _currentGenre - private val _genreList = MutableStateFlow(listOf()) + private val _genreSongList = MutableStateFlow(listOf()) /** The current list data derived from [currentGenre]. */ - val genreList: StateFlow> = _genreList - private val _genreInstructions = MutableEvent() - /** Instructions for updating [artistList] in the UI. */ - val genreInstructions: Event - get() = _genreInstructions + val genreSongList: StateFlow> = _genreSongList - /** The current [Sort] used for [Song]s in [genreList]. */ + private val _genreSongInstructions = MutableEvent() + /** Instructions for updating [artistSongList] in the UI. */ + val genreSongInstructions: Event + get() = _genreSongInstructions + + /** The current [Sort] used for [Song]s in [genreSongList]. */ var genreSongSort: Sort get() = musicSettings.genreSongSort set(value) { @@ -179,13 +181,14 @@ constructor( val currentPlaylist: StateFlow get() = _currentPlaylist - private val _playlistList = MutableStateFlow(listOf()) + private val _playlistSongList = MutableStateFlow(listOf()) /** The current list data derived from [currentPlaylist] */ - val playlistList: StateFlow> = _playlistList - private val _playlistInstructions = MutableEvent() - /** Instructions for updating [playlistList] in the UI. */ - val playlistInstructions: Event - get() = _playlistInstructions + val playlistSongList: StateFlow> = _playlistSongList + + private val _playlistSongInstructions = MutableEvent() + /** Instructions for updating [playlistSongList] in the UI. */ + val playlistSongInstructions: Event + get() = _playlistSongInstructions private val _editedPlaylist = MutableStateFlow?>(null) /** @@ -346,7 +349,7 @@ constructor( } /** - * Set a new [currentAlbum] from it's [Music.UID]. [currentAlbum] and [albumList] will be + * Set a new [currentAlbum] from it's [Music.UID]. [currentAlbum] and [albumSongList] will be * updated to align with the new [Album]. * * @param uid The [Music.UID] of the [Album] to update [currentAlbum] to. Must be valid. @@ -360,13 +363,18 @@ constructor( } } + /** + * Apply a new [Sort] to [albumSongList]. + * + * @param sort The [Sort] to apply. + */ fun applyAlbumSongSort(sort: Sort) { musicSettings.albumSongSort = sort _currentAlbum.value?.let { refreshAlbumList(it, true) } } /** - * Set a new [currentArtist] from it's [Music.UID]. [currentArtist] and [artistList] will be + * Set a new [currentArtist] from it's [Music.UID]. [currentArtist] and [artistSongList] will be * updated to align with the new [Artist]. * * @param uid The [Music.UID] of the [Artist] to update [currentArtist] to. Must be valid. @@ -380,13 +388,18 @@ constructor( } } + /** + * Apply a new [Sort] to [artistSongList]. + * + * @param sort The [Sort] to apply. + */ fun applyArtistSongSort(sort: Sort) { musicSettings.artistSongSort = sort _currentArtist.value?.let { refreshArtistList(it, true) } } /** - * Set a new [currentGenre] from it's [Music.UID]. [currentGenre] and [genreList] will be + * Set a new [currentGenre] from it's [Music.UID]. [currentGenre] and [genreSongList] will be * updated to align with the new album. * * @param uid The [Music.UID] of the [Genre] to update [currentGenre] to. Must be valid. @@ -400,6 +413,11 @@ constructor( } } + /** + * Apply a new [Sort] to [genreSongList]. + * + * @param sort The [Sort] to apply. + */ fun applyGenreSongSort(sort: Sort) { musicSettings.genreSongSort = sort _currentGenre.value?.let { refreshGenreList(it, true) } @@ -554,8 +572,8 @@ constructor( } logD("Update album list to ${list.size} items with $instructions") - _albumInstructions.put(instructions) - _albumList.value = list + _albumSongInstructions.put(instructions) + _albumSongList.value = list } private fun refreshArtistList(artist: Artist, replace: Boolean = false) { @@ -617,8 +635,8 @@ constructor( } logD("Updating artist list to ${list.size} items with $instructions") - _artistInstructions.put(instructions) - _artistList.value = list.toList() + _artistSongInstructions.put(instructions) + _artistSongList.value = list.toList() } private fun refreshGenreList(genre: Genre, replace: Boolean = false) { @@ -643,8 +661,8 @@ constructor( list.addAll(genreSongSort.songs(genre.songs)) logD("Updating genre list to ${list.size} items with $instructions") - _genreInstructions.put(instructions) - _genreList.value = list + _genreSongInstructions.put(instructions) + _genreSongList.value = list } private fun refreshPlaylistList( @@ -663,8 +681,8 @@ constructor( } logD("Updating playlist list to ${list.size} items with $instructions") - _playlistInstructions.put(instructions) - _playlistList.value = list + _playlistSongInstructions.put(instructions) + _playlistSongList.value = list } /** diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index 49b6f451e..190ae4c81 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -21,7 +21,6 @@ package org.oxycblt.auxio.detail import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem -import android.view.View import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -108,7 +107,7 @@ class GenreDetailFragment : (layoutManager as GridLayoutManager).setFullWidthLookup { if (it != 0) { val item = - detailModel.genreList.value.getOrElse(it - 1) { + detailModel.genreSongList.value.getOrElse(it - 1) { return@setFullWidthLookup false } item is Divider || item is Header @@ -122,7 +121,7 @@ class GenreDetailFragment : // DetailViewModel handles most initialization from the navigation argument. detailModel.setGenre(args.genreUid) collectImmediately(detailModel.currentGenre, ::updatePlaylist) - collectImmediately(detailModel.genreList, ::updateList) + collectImmediately(detailModel.genreSongList, ::updateList) collect(detailModel.toShow.flow, ::handleShow) collect(listModel.menu.flow, ::handleMenu) collectImmediately(listModel.selected, ::updateSelection) @@ -138,7 +137,7 @@ class GenreDetailFragment : binding.detailRecycler.adapter = null // Avoid possible race conditions that could cause a bad replace instruction to be consumed // during list initialization and crash the app. Could happen if the user is fast enough. - detailModel.genreInstructions.consume() + detailModel.genreSongInstructions.consume() } override fun onMenuItemClick(item: MenuItem): Boolean { @@ -181,7 +180,7 @@ class GenreDetailFragment : } } - override fun onOpenMenu(item: Music, anchor: View) { + override fun onOpenMenu(item: Music) { when (item) { is Artist -> listModel.openMenu(R.menu.item_parent, item) is Song -> listModel.openMenu(R.menu.item_song, item, detailModel.playInGenreWith) @@ -197,7 +196,7 @@ class GenreDetailFragment : playbackModel.shuffle(unlikelyToBeNull(detailModel.currentGenre.value)) } - override fun onOpenSortMenu(anchor: View) { + override fun onOpenSortMenu() { findNavController().navigateSafe(GenreDetailFragmentDirections.sort()) } @@ -212,7 +211,7 @@ class GenreDetailFragment : } private fun updateList(list: List) { - genreListAdapter.update(list, detailModel.genreInstructions.consume()) + genreListAdapter.update(list, detailModel.genreSongInstructions.consume()) } private fun handleShow(show: Show?) { 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 4f25b96f8..9c0088c11 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/PlaylistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/PlaylistDetailFragment.kt @@ -21,7 +21,6 @@ package org.oxycblt.auxio.detail import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem -import android.view.View import androidx.fragment.app.activityViewModels import androidx.navigation.NavController import androidx.navigation.NavDestination @@ -123,7 +122,7 @@ class PlaylistDetailFragment : (layoutManager as GridLayoutManager).setFullWidthLookup { if (it != 0) { val item = - detailModel.playlistList.value.getOrElse(it - 1) { + detailModel.playlistSongList.value.getOrElse(it - 1) { return@setFullWidthLookup false } item is Divider || item is Header @@ -137,7 +136,7 @@ class PlaylistDetailFragment : // DetailViewModel handles most initialization from the navigation argument. detailModel.setPlaylist(args.playlistUid) collectImmediately(detailModel.currentPlaylist, ::updatePlaylist) - collectImmediately(detailModel.playlistList, ::updateList) + collectImmediately(detailModel.playlistSongList, ::updateList) collectImmediately(detailModel.editedPlaylist, ::updateEditedList) collect(detailModel.toShow.flow, ::handleShow) collect(listModel.menu.flow, ::handleMenu) @@ -168,7 +167,7 @@ class PlaylistDetailFragment : binding.detailRecycler.adapter = null // Avoid possible race conditions that could cause a bad replace instruction to be consumed // during list initialization and crash the app. Could happen if the user is fast enough. - detailModel.playlistInstructions.consume() + detailModel.playlistSongInstructions.consume() } override fun onDestinationChanged( @@ -236,7 +235,7 @@ class PlaylistDetailFragment : requireNotNull(touchHelper) { "ItemTouchHelper was not available" }.startDrag(viewHolder) } - override fun onOpenMenu(item: Song, anchor: View) { + override fun onOpenMenu(item: Song) { listModel.openMenu(R.menu.item_playlist_song, item, detailModel.playInPlaylistWith) } @@ -252,7 +251,7 @@ class PlaylistDetailFragment : detailModel.startPlaylistEdit() } - override fun onOpenSortMenu(anchor: View) {} + override fun onOpenSortMenu() {} private fun updatePlaylist(playlist: Playlist?) { if (playlist == null) { @@ -278,7 +277,7 @@ class PlaylistDetailFragment : } private fun updateList(list: List) { - playlistListAdapter.update(list, detailModel.playlistInstructions.consume()) + playlistListAdapter.update(list, detailModel.playlistSongInstructions.consume()) } private fun updateEditedList(editedPlaylist: List?) { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/list/DetailListAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/list/DetailListAdapter.kt index ba350e7b3..08293199f 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/list/DetailListAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/list/DetailListAdapter.kt @@ -82,7 +82,7 @@ abstract class DetailListAdapter( * Called when the button in a [SortHeader] item is pressed, requesting that the sort menu * should be opened. */ - fun onOpenSortMenu(anchor: View) + fun onOpenSortMenu() } protected companion object { @@ -132,7 +132,7 @@ private class SortHeaderViewHolder(private val binding: ItemSortHeaderBinding) : // Add a Tooltip based on the content description so that the purpose of this // button can be clear. TooltipCompat.setTooltipText(this, contentDescription) - setOnClickListener(listener::onOpenSortMenu) + setOnClickListener { listener.onOpenSortMenu() } } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/sort/AlbumSongSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/sort/AlbumSongSortDialog.kt index 8c7290e98..ea1fd15bd 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/sort/AlbumSongSortDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/sort/AlbumSongSortDialog.kt @@ -30,6 +30,11 @@ import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.logD +/** + * A [SortDialog] that controls the [Sort] of [DetailViewModel.albumSongSort]. + * + * @author Alexander Capehart (OxygenCobalt) + */ @AndroidEntryPoint class AlbumSongSortDialog : SortDialog() { private val detailModel: DetailViewModel by activityViewModels() diff --git a/app/src/main/java/org/oxycblt/auxio/detail/sort/ArtistSongSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/sort/ArtistSongSortDialog.kt index a745bd11a..15daaff72 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/sort/ArtistSongSortDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/sort/ArtistSongSortDialog.kt @@ -30,6 +30,11 @@ import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.logD +/** + * A [SortDialog] that controls the [Sort] of [DetailViewModel.artistSongSort]. + * + * @author Alexander Capehart (OxygenCobalt) + */ @AndroidEntryPoint class ArtistSongSortDialog : SortDialog() { private val detailModel: DetailViewModel by activityViewModels() diff --git a/app/src/main/java/org/oxycblt/auxio/detail/sort/GenreSongSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/sort/GenreSongSortDialog.kt index d491310c8..3191557e4 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/sort/GenreSongSortDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/sort/GenreSongSortDialog.kt @@ -30,6 +30,11 @@ import org.oxycblt.auxio.music.Genre 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 GenreSongSortDialog : SortDialog() { private val detailModel: DetailViewModel by activityViewModels() diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt index 834b25fe6..51e25378a 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt @@ -26,7 +26,6 @@ import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.MenuCompat import androidx.core.view.isVisible -import androidx.core.view.iterator import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -57,7 +56,6 @@ import org.oxycblt.auxio.home.tabs.Tab import org.oxycblt.auxio.list.ListViewModel import org.oxycblt.auxio.list.Menu import org.oxycblt.auxio.list.SelectionFragment -import org.oxycblt.auxio.list.Sort import org.oxycblt.auxio.music.IndexingProgress import org.oxycblt.auxio.music.IndexingState import org.oxycblt.auxio.music.Music @@ -76,7 +74,6 @@ import org.oxycblt.auxio.util.lazyReflectedField import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logW import org.oxycblt.auxio.util.navigateSafe -import org.oxycblt.auxio.util.unlikelyToBeNull /** * The starting [SelectionFragment] of Auxio. Shows the user's music library and enables navigation @@ -172,7 +169,7 @@ class HomeFragment : // --- VIEWMODEL SETUP --- collect(homeModel.recreateTabs.flow, ::handleRecreate) collectImmediately(homeModel.currentTabType, ::updateCurrentTab) - collectImmediately(homeModel.songsList, homeModel.isFastScrolling, ::updateFab) + collectImmediately(homeModel.songList, homeModel.isFastScrolling, ::updateFab) collect(listModel.menu.flow, ::handleMenu) collectImmediately(listModel.selected, ::updateSelection) collectImmediately(musicModel.indexingState, ::updateIndexerState) @@ -232,41 +229,22 @@ class HomeFragment : } // Handle sort menu - R.id.submenu_sorting -> { + R.id.action_sort -> { // Junk click event when opening the menu - true - } - R.id.option_sort_asc -> { - logD("Switching to ascending sorting") - item.isChecked = true - homeModel.setSortForCurrentTab( - homeModel - .getSortForTab(homeModel.currentTabType.value) - .withDirection(Sort.Direction.ASCENDING)) - true - } - R.id.option_sort_dec -> { - logD("Switching to descending sorting") - item.isChecked = true - homeModel.setSortForCurrentTab( - homeModel - .getSortForTab(homeModel.currentTabType.value) - .withDirection(Sort.Direction.DESCENDING)) + val directions = + when (homeModel.currentTabType.value) { + MusicType.SONGS -> HomeFragmentDirections.sortSongs() + MusicType.ALBUMS -> HomeFragmentDirections.sortAlbums() + MusicType.ARTISTS -> HomeFragmentDirections.sortArtists() + MusicType.GENRES -> HomeFragmentDirections.sortGenres() + MusicType.PLAYLISTS -> HomeFragmentDirections.sortPlaylists() + } + findNavController().navigateSafe(directions) true } else -> { - val newMode = Sort.Mode.fromItemId(item.itemId) - if (newMode != null) { - // Sorting option was selected, mark it as selected and update the mode - logD("Updating sort mode") - item.isChecked = true - homeModel.setSortForCurrentTab( - homeModel.getSortForTab(homeModel.currentTabType.value).withMode(newMode)) - true - } else { - logW("Unexpected menu item selected") - false - } + logW("Unexpected menu item selected") + false } } } @@ -300,61 +278,6 @@ class HomeFragment : private fun updateCurrentTab(tabType: MusicType) { val binding = requireBinding() - // Update the sort options to align with those allowed by the tab - val isVisible: (Int) -> Boolean = - when (tabType) { - // Disallow sorting by count for songs - MusicType.SONGS -> { - logD("Using song-specific menu options") - ({ id -> id != R.id.option_sort_count }) - } - // Disallow sorting by album for albums - MusicType.ALBUMS -> { - logD("Using album-specific menu options") - ({ id -> id != R.id.option_sort_album }) - } - // Only allow sorting by name, count, and duration for parents - else -> { - logD("Using parent-specific menu options") - ({ id -> - id == R.id.option_sort_asc || - id == R.id.option_sort_dec || - id == R.id.option_sort_name || - id == R.id.option_sort_count || - id == R.id.option_sort_duration - }) - } - } - - val sortMenu = - unlikelyToBeNull(binding.homeNormalToolbar.menu.findItem(R.id.submenu_sorting).subMenu) - val toHighlight = homeModel.getSortForTab(tabType) - - for (option in sortMenu) { - val isCurrentMode = option.itemId == toHighlight.mode.itemId - val isCurrentlyAscending = - option.itemId == R.id.option_sort_asc && - toHighlight.direction == Sort.Direction.ASCENDING - val isCurrentlyDescending = - option.itemId == R.id.option_sort_dec && - toHighlight.direction == Sort.Direction.DESCENDING - // Check the corresponding direction and mode sort options to align with - // the current sort of the tab. - if (isCurrentMode || isCurrentlyAscending || isCurrentlyDescending) { - logD( - "Checking $option option [mode: $isCurrentMode asc: $isCurrentlyAscending dec: $isCurrentlyDescending]") - // Note: We cannot inline this boolean assignment since it unchecks all other radio - // buttons (even when setting it to false), which would result in nothing being - // selected. - option.isChecked = true - } - - // Disable options that are not allowed by the isVisible lambda - option.isVisible = isVisible(option.itemId) - if (!option.isVisible) { - logD("Hiding $option option") - } - } // Update the scrolling view in AppBarLayout to align with the current tab's // scrolling state. This prevents the lift state from being confused as one diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt index a89b73b3d..0d7e1e4d0 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt @@ -55,63 +55,83 @@ constructor( private val musicSettings: MusicSettings ) : ViewModel(), MusicRepository.UpdateListener, HomeSettings.Listener { - private val _songsList = MutableStateFlow(listOf()) + private val _songList = MutableStateFlow(listOf()) /** A list of [Song]s, sorted by the preferred [Sort], to be shown in the home view. */ - val songsList: StateFlow> - get() = _songsList + val songList: StateFlow> + get() = _songList - private val _songsInstructions = MutableEvent() - /** Instructions for how to update [songsList] in the UI. */ - val songsInstructions: Event - get() = _songsInstructions + private val _songInstructions = MutableEvent() + /** Instructions for how to update [songList] in the UI. */ + val songInstructions: Event + get() = _songInstructions - private val _albumsLists = MutableStateFlow(listOf()) - /** A list of [Album]s, sorted by the preferred [Sort], to be shown in the home view. */ - val albumsList: StateFlow> - get() = _albumsLists - - private val _albumsInstructions = MutableEvent() - /** Instructions for how to update [albumsList] in the UI. */ - val albumsInstructions: Event - get() = _albumsInstructions - - private val _artistsList = MutableStateFlow(listOf()) - /** - * A list of [Artist]s, sorted by the preferred [Sort], to be shown in the home view. Note that - * if "Hide collaborators" is on, this list will not include collaborator [Artist]s. - */ - val artistsList: MutableStateFlow> - get() = _artistsList - - private val _artistsInstructions = MutableEvent() - /** Instructions for how to update [artistsList] in the UI. */ - val artistsInstructions: Event - get() = _artistsInstructions - - private val _genresList = MutableStateFlow(listOf()) - /** A list of [Genre]s, sorted by the preferred [Sort], to be shown in the home view. */ - val genresList: StateFlow> - get() = _genresList - - private val _genresInstructions = MutableEvent() - /** Instructions for how to update [genresList] in the UI. */ - val genresInstructions: Event - get() = _genresInstructions - - private val _playlistsList = MutableStateFlow(listOf()) - /** A list of [Playlist]s, sorted by the preferred [Sort], to be shown in the home view. */ - val playlistsList: StateFlow> - get() = _playlistsList - - private val _playlistsInstructions = MutableEvent() - /** Instructions for how to update [genresList] in the UI. */ - val playlistsInstructions: Event - get() = _playlistsInstructions + /** The current [Sort] used for [songList]. */ + val songSort: Sort + get() = musicSettings.songSort /** The [PlaySong] instructions to use when playing a [Song]. */ val playWith get() = playbackSettings.playInListWith + private val _albumList = MutableStateFlow(listOf()) + /** A list of [Album]s, sorted by the preferred [Sort], to be shown in the home view. */ + val albumList: StateFlow> + get() = _albumList + + private val _albumInstructions = MutableEvent() + /** Instructions for how to update [albumList] in the UI. */ + val albumInstructions: Event + get() = _albumInstructions + + /** The current [Sort] used for [albumList]. */ + val albumSort: Sort + get() = musicSettings.albumSort + + private val _artistList = MutableStateFlow(listOf()) + /** + * A list of [Artist]s, sorted by the preferred [Sort], to be shown in the home view. Note that + * if "Hide collaborators" is on, this list will not include collaborator [Artist]s. + */ + val artistList: MutableStateFlow> + get() = _artistList + + private val _artistInstructions = MutableEvent() + /** Instructions for how to update [artistList] in the UI. */ + val artistInstructions: Event + get() = _artistInstructions + + /** The current [Sort] used for [artistList]. */ + val artistSort: Sort + get() = musicSettings.artistSort + + private val _genreList = MutableStateFlow(listOf()) + /** A list of [Genre]s, sorted by the preferred [Sort], to be shown in the home view. */ + val genreList: StateFlow> + get() = _genreList + + private val _genreInstructions = MutableEvent() + /** Instructions for how to update [genreList] in the UI. */ + val genreInstructions: Event + get() = _genreInstructions + + /** The current [Sort] used for [genreList]. */ + val genreSort: Sort + get() = musicSettings.genreSort + + private val _playlistList = MutableStateFlow(listOf()) + /** A list of [Playlist]s, sorted by the preferred [Sort], to be shown in the home view. */ + val playlistList: StateFlow> + get() = _playlistList + + private val _playlistInstructions = MutableEvent() + /** Instructions for how to update [genreList] in the UI. */ + val playlistInstructions: Event + get() = _playlistInstructions + + /** The current [Sort] used for [genreList]. */ + val playlistSort: Sort + get() = musicSettings.playlistSort + /** * A list of [MusicType] corresponding to the current [Tab] configuration, excluding invisible * [Tab]s. @@ -157,12 +177,12 @@ constructor( logD("Refreshing library") // Get the each list of items in the library to use as our list data. // Applying the preferred sorting to them. - _songsInstructions.put(UpdateInstructions.Diff) - _songsList.value = musicSettings.songSort.songs(deviceLibrary.songs) - _albumsInstructions.put(UpdateInstructions.Diff) - _albumsLists.value = musicSettings.albumSort.albums(deviceLibrary.albums) - _artistsInstructions.put(UpdateInstructions.Diff) - _artistsList.value = + _songInstructions.put(UpdateInstructions.Diff) + _songList.value = musicSettings.songSort.songs(deviceLibrary.songs) + _albumInstructions.put(UpdateInstructions.Diff) + _albumList.value = musicSettings.albumSort.albums(deviceLibrary.albums) + _artistInstructions.put(UpdateInstructions.Diff) + _artistList.value = musicSettings.artistSort.artists( if (homeSettings.shouldHideCollaborators) { logD("Filtering collaborator artists") @@ -172,15 +192,15 @@ constructor( logD("Using all artists") deviceLibrary.artists }) - _genresInstructions.put(UpdateInstructions.Diff) - _genresList.value = musicSettings.genreSort.genres(deviceLibrary.genres) + _genreInstructions.put(UpdateInstructions.Diff) + _genreList.value = musicSettings.genreSort.genres(deviceLibrary.genres) } val userLibrary = musicRepository.userLibrary if (changes.userLibrary && userLibrary != null) { logD("Refreshing playlists") - _playlistsInstructions.put(UpdateInstructions.Diff) - _playlistsList.value = musicSettings.playlistSort.playlists(userLibrary.playlists) + _playlistInstructions.put(UpdateInstructions.Diff) + _playlistList.value = musicSettings.playlistSort.playlists(userLibrary.playlists) } } @@ -199,59 +219,58 @@ constructor( } /** - * Get the preferred [Sort] for a given [Tab]. + * Apply a new [Sort] to [songList]. * - * @param tabType The [MusicType] of the [Tab] desired. - * @return The [Sort] preferred for that [Tab] + * @param sort The [Sort] to apply. */ - fun getSortForTab(tabType: MusicType) = - when (tabType) { - MusicType.SONGS -> musicSettings.songSort - MusicType.ALBUMS -> musicSettings.albumSort - MusicType.ARTISTS -> musicSettings.artistSort - MusicType.GENRES -> musicSettings.genreSort - MusicType.PLAYLISTS -> musicSettings.playlistSort - } + fun applySongSort(sort: Sort) { + musicSettings.songSort = sort + _songInstructions.put(UpdateInstructions.Replace(0)) + _songList.value = musicSettings.songSort.songs(_songList.value) + } /** - * Update the preferred [Sort] for the current [Tab]. Will update corresponding list. + * Apply a new [Sort] to [albumList]. * - * @param sort The new [Sort] to apply. Assumed to be an allowed sort for the current [Tab]. + * @param sort The [Sort] to apply. */ - fun setSortForCurrentTab(sort: Sort) { - // Can simply re-sort the current list of items without having to access the library. - when (val type = _currentTabType.value) { - MusicType.SONGS -> { - logD("Updating song [$type] sort mode to $sort") - musicSettings.songSort = sort - _songsInstructions.put(UpdateInstructions.Replace(0)) - _songsList.value = sort.songs(_songsList.value) - } - MusicType.ALBUMS -> { - logD("Updating album [$type] sort mode to $sort") - musicSettings.albumSort = sort - _albumsInstructions.put(UpdateInstructions.Replace(0)) - _albumsLists.value = sort.albums(_albumsLists.value) - } - MusicType.ARTISTS -> { - logD("Updating artist [$type] sort mode to $sort") - musicSettings.artistSort = sort - _artistsInstructions.put(UpdateInstructions.Replace(0)) - _artistsList.value = sort.artists(_artistsList.value) - } - MusicType.GENRES -> { - logD("Updating genre [$type] sort mode to $sort") - musicSettings.genreSort = sort - _genresInstructions.put(UpdateInstructions.Replace(0)) - _genresList.value = sort.genres(_genresList.value) - } - MusicType.PLAYLISTS -> { - logD("Updating playlist [$type] sort mode to $sort") - musicSettings.playlistSort = sort - _playlistsInstructions.put(UpdateInstructions.Replace(0)) - _playlistsList.value = sort.playlists(_playlistsList.value) - } - } + fun applyAlbumSort(sort: Sort) { + musicSettings.albumSort = sort + _albumInstructions.put(UpdateInstructions.Replace(0)) + _albumList.value = musicSettings.albumSort.albums(_albumList.value) + } + + /** + * Apply a new [Sort] to [artistList]. + * + * @param sort The [Sort] to apply. + */ + fun applyArtistSort(sort: Sort) { + musicSettings.artistSort = sort + _artistInstructions.put(UpdateInstructions.Replace(0)) + _artistList.value = musicSettings.artistSort.artists(_artistList.value) + } + + /** + * Apply a new [Sort] to [genreList]. + * + * @param sort The [Sort] to apply. + */ + fun applyGenreSort(sort: Sort) { + musicSettings.genreSort = sort + _genreInstructions.put(UpdateInstructions.Replace(0)) + _genreList.value = musicSettings.genreSort.genres(_genreList.value) + } + + /** + * Apply a new [Sort] to [playlistList]. + * + * @param sort The [Sort] to apply. + */ + fun applyPlaylistSort(sort: Sort) { + musicSettings.playlistSort = sort + _playlistInstructions.put(UpdateInstructions.Replace(0)) + _playlistList.value = musicSettings.playlistSort.playlists(_playlistList.value) } /** diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt index ce8fcfb20..56f3a4d80 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt @@ -21,7 +21,6 @@ package org.oxycblt.auxio.home.list import android.os.Bundle import android.text.format.DateUtils import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.fragment.app.activityViewModels import dagger.hilt.android.AndroidEntryPoint @@ -40,13 +39,13 @@ import org.oxycblt.auxio.list.recycler.AlbumViewHolder import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.MusicParent -import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.formatDurationMs import org.oxycblt.auxio.playback.secsToMs import org.oxycblt.auxio.util.collectImmediately +import org.oxycblt.auxio.util.logD /** * A [ListFragment] that shows a list of [Album]s. @@ -81,7 +80,7 @@ class AlbumListFragment : listener = this@AlbumListFragment } - collectImmediately(homeModel.albumsList, ::updateAlbums) + collectImmediately(homeModel.albumList, ::updateAlbums) collectImmediately(listModel.selected, ::updateSelection) collectImmediately( playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) @@ -97,9 +96,9 @@ class AlbumListFragment : } override fun getPopup(pos: Int): String? { - val album = homeModel.albumsList.value[pos] + val album = homeModel.albumList.value[pos] // Change how we display the popup depending on the current sort mode. - return when (homeModel.getSortForTab(MusicType.ALBUMS).mode) { + return when (homeModel.albumSort.mode) { // By Name -> Use Name is Sort.Mode.ByName -> album.name.thumb @@ -141,12 +140,13 @@ class AlbumListFragment : detailModel.showAlbum(item) } - override fun onOpenMenu(item: Album, anchor: View) { + override fun onOpenMenu(item: Album) { listModel.openMenu(R.menu.item_album, item) } private fun updateAlbums(albums: List) { - albumAdapter.update(albums, homeModel.albumsInstructions.consume()) + logD("Absolute fucking retard") + albumAdapter.update(albums, homeModel.albumInstructions.consume()) } private fun updateSelection(selection: List) { diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt index 8cda450e7..920231d2a 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt @@ -20,7 +20,6 @@ package org.oxycblt.auxio.home.list import android.os.Bundle import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.fragment.app.activityViewModels import dagger.hilt.android.AndroidEntryPoint @@ -38,7 +37,6 @@ import org.oxycblt.auxio.list.recycler.ArtistViewHolder import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.MusicParent -import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel @@ -76,7 +74,7 @@ class ArtistListFragment : listener = this@ArtistListFragment } - collectImmediately(homeModel.artistsList, ::updateArtists) + collectImmediately(homeModel.artistList, ::updateArtists) collectImmediately(listModel.selected, ::updateSelection) collectImmediately( playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) @@ -92,9 +90,9 @@ class ArtistListFragment : } override fun getPopup(pos: Int): String? { - val artist = homeModel.artistsList.value[pos] + val artist = homeModel.artistList.value[pos] // Change how we display the popup depending on the current sort mode. - return when (homeModel.getSortForTab(MusicType.ARTISTS).mode) { + return when (homeModel.artistSort.mode) { // By Name -> Use Name is Sort.Mode.ByName -> artist.name.thumb @@ -117,12 +115,12 @@ class ArtistListFragment : detailModel.showArtist(item) } - override fun onOpenMenu(item: Artist, anchor: View) { + override fun onOpenMenu(item: Artist) { listModel.openMenu(R.menu.item_parent, item) } private fun updateArtists(artists: List) { - artistAdapter.update(artists, homeModel.artistsInstructions.consume()) + artistAdapter.update(artists, homeModel.artistInstructions.consume()) } private fun updateSelection(selection: List) { diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt index bba02ff20..381422748 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt @@ -20,7 +20,6 @@ package org.oxycblt.auxio.home.list import android.os.Bundle import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.fragment.app.activityViewModels import dagger.hilt.android.AndroidEntryPoint @@ -38,7 +37,6 @@ import org.oxycblt.auxio.list.recycler.GenreViewHolder import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.MusicParent -import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel @@ -75,7 +73,7 @@ class GenreListFragment : listener = this@GenreListFragment } - collectImmediately(homeModel.genresList, ::updateGenres) + collectImmediately(homeModel.genreList, ::updateGenres) collectImmediately(listModel.selected, ::updateSelection) collectImmediately( playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) @@ -91,9 +89,9 @@ class GenreListFragment : } override fun getPopup(pos: Int): String? { - val genre = homeModel.genresList.value[pos] + val genre = homeModel.genreList.value[pos] // Change how we display the popup depending on the current sort mode. - return when (homeModel.getSortForTab(MusicType.GENRES).mode) { + return when (homeModel.genreSort.mode) { // By Name -> Use Name is Sort.Mode.ByName -> genre.name.thumb @@ -116,12 +114,12 @@ class GenreListFragment : detailModel.showGenre(item) } - override fun onOpenMenu(item: Genre, anchor: View) { + override fun onOpenMenu(item: Genre) { listModel.openMenu(R.menu.item_parent, item) } private fun updateGenres(genres: List) { - genreAdapter.update(genres, homeModel.genresInstructions.consume()) + genreAdapter.update(genres, homeModel.genreInstructions.consume()) } private fun updateSelection(selection: List) { diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/PlaylistListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/PlaylistListFragment.kt index 164b0da92..a36db3873 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/PlaylistListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/PlaylistListFragment.kt @@ -20,7 +20,6 @@ package org.oxycblt.auxio.home.list import android.os.Bundle import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.fragment.app.activityViewModels import org.oxycblt.auxio.R @@ -36,7 +35,6 @@ import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter import org.oxycblt.auxio.list.recycler.PlaylistViewHolder import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.MusicParent -import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Song @@ -73,7 +71,7 @@ class PlaylistListFragment : listener = this@PlaylistListFragment } - collectImmediately(homeModel.playlistsList, ::updatePlaylists) + collectImmediately(homeModel.playlistList, ::updatePlaylists) collectImmediately(listModel.selected, ::updateSelection) collectImmediately( playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) @@ -89,9 +87,9 @@ class PlaylistListFragment : } override fun getPopup(pos: Int): String? { - val playlist = homeModel.playlistsList.value[pos] + val playlist = homeModel.playlistList.value[pos] // Change how we display the popup depending on the current sort mode. - return when (homeModel.getSortForTab(MusicType.GENRES).mode) { + return when (homeModel.playlistSort.mode) { // By Name -> Use Name is Sort.Mode.ByName -> playlist.name.thumb @@ -114,12 +112,12 @@ class PlaylistListFragment : detailModel.showPlaylist(item) } - override fun onOpenMenu(item: Playlist, anchor: View) { + override fun onOpenMenu(item: Playlist) { listModel.openMenu(R.menu.item_playlist, item) } private fun updatePlaylists(playlists: List) { - playlistAdapter.update(playlists, homeModel.playlistsInstructions.consume()) + playlistAdapter.update(playlists, homeModel.playlistInstructions.consume()) } private fun updateSelection(selection: List) { diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt index 26b9282ea..38e63a5bd 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt @@ -21,7 +21,6 @@ package org.oxycblt.auxio.home.list import android.os.Bundle import android.text.format.DateUtils import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.fragment.app.activityViewModels import dagger.hilt.android.AndroidEntryPoint @@ -38,7 +37,6 @@ import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter import org.oxycblt.auxio.list.recycler.SongViewHolder import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.MusicParent -import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel @@ -78,7 +76,7 @@ class SongListFragment : listener = this@SongListFragment } - collectImmediately(homeModel.songsList, ::updateSongs) + collectImmediately(homeModel.songList, ::updateSongs) collectImmediately(listModel.selected, ::updateSelection) collectImmediately( playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) @@ -94,11 +92,11 @@ class SongListFragment : } override fun getPopup(pos: Int): String? { - val song = homeModel.songsList.value[pos] + val song = homeModel.songList.value[pos] // Change how we display the popup depending on the current sort mode. // Note: We don't use the more correct individual artist name here, as sorts are largely // based off the names of the parent objects and not the child objects. - return when (homeModel.getSortForTab(MusicType.SONGS).mode) { + return when (homeModel.songSort.mode) { // Name -> Use name is Sort.Mode.ByName -> song.name.thumb @@ -140,12 +138,12 @@ class SongListFragment : playbackModel.play(item, homeModel.playWith) } - override fun onOpenMenu(item: Song, anchor: View) { + override fun onOpenMenu(item: Song) { listModel.openMenu(R.menu.item_song, item, homeModel.playWith) } private fun updateSongs(songs: List) { - songAdapter.update(songs, homeModel.songsInstructions.consume()) + songAdapter.update(songs, homeModel.songInstructions.consume()) } private fun updateSelection(selection: List) { diff --git a/app/src/main/java/org/oxycblt/auxio/home/sort/AlbumSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/home/sort/AlbumSortDialog.kt new file mode 100644 index 000000000..82426eb71 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/home/sort/AlbumSortDialog.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Auxio Project + * AlbumSortDialog.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.home.sort + +import androidx.fragment.app.activityViewModels +import dagger.hilt.android.AndroidEntryPoint +import org.oxycblt.auxio.home.HomeViewModel +import org.oxycblt.auxio.list.Sort +import org.oxycblt.auxio.list.sort.SortDialog + +/** + * A [SortDialog] that controls the [Sort] of [HomeViewModel.albumList]. + * + * @author Alexander Capehart (OxygenCobalt) + */ +@AndroidEntryPoint +class AlbumSortDialog : SortDialog() { + private val homeModel: HomeViewModel by activityViewModels() + + override fun getInitialSort() = homeModel.albumSort + + override fun applyChosenSort(sort: Sort) { + homeModel.applyAlbumSort(sort) + } + + override fun getModeChoices() = + listOf( + Sort.Mode.ByName, + Sort.Mode.ByArtist, + Sort.Mode.ByDate, + Sort.Mode.ByDuration, + Sort.Mode.ByCount, + Sort.Mode.ByDateAdded) +} diff --git a/app/src/main/java/org/oxycblt/auxio/home/sort/ArtistSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/home/sort/ArtistSortDialog.kt new file mode 100644 index 000000000..5c02f777b --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/home/sort/ArtistSortDialog.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Auxio Project + * ArtistSortDialog.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.home.sort + +import androidx.fragment.app.activityViewModels +import dagger.hilt.android.AndroidEntryPoint +import org.oxycblt.auxio.home.HomeViewModel +import org.oxycblt.auxio.list.Sort +import org.oxycblt.auxio.list.sort.SortDialog + +/** + * A [SortDialog] that controls the [Sort] of [HomeViewModel.artistList]. + * + * @author Alexander Capehart (OxygenCobalt) + */ +@AndroidEntryPoint +class ArtistSortDialog : SortDialog() { + private val homeModel: HomeViewModel by activityViewModels() + + override fun getInitialSort() = homeModel.artistSort + + override fun applyChosenSort(sort: Sort) { + homeModel.applyArtistSort(sort) + } + + override fun getModeChoices() = + listOf(Sort.Mode.ByName, Sort.Mode.ByDuration, Sort.Mode.ByCount) +} diff --git a/app/src/main/java/org/oxycblt/auxio/home/sort/GenreSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/home/sort/GenreSortDialog.kt new file mode 100644 index 000000000..96093c9a9 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/home/sort/GenreSortDialog.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Auxio Project + * GenreSortDialog.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.home.sort + +import androidx.fragment.app.activityViewModels +import dagger.hilt.android.AndroidEntryPoint +import org.oxycblt.auxio.home.HomeViewModel +import org.oxycblt.auxio.list.Sort +import org.oxycblt.auxio.list.sort.SortDialog + +/** + * A [SortDialog] that controls the [Sort] of [HomeViewModel.genreList]. + * + * @author Alexander Capehart (OxygenCobalt) + */ +@AndroidEntryPoint +class GenreSortDialog : SortDialog() { + private val homeModel: HomeViewModel by activityViewModels() + + override fun getInitialSort() = homeModel.genreSort + + override fun applyChosenSort(sort: Sort) { + homeModel.applyGenreSort(sort) + } + + override fun getModeChoices() = + listOf(Sort.Mode.ByName, Sort.Mode.ByDuration, Sort.Mode.ByCount) +} diff --git a/app/src/main/java/org/oxycblt/auxio/home/sort/PlaylistSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/home/sort/PlaylistSortDialog.kt new file mode 100644 index 000000000..eb89c4bdd --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/home/sort/PlaylistSortDialog.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Auxio Project + * PlaylistSortDialog.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.home.sort + +import androidx.fragment.app.activityViewModels +import dagger.hilt.android.AndroidEntryPoint +import org.oxycblt.auxio.home.HomeViewModel +import org.oxycblt.auxio.list.Sort +import org.oxycblt.auxio.list.sort.SortDialog + +/** + * A [SortDialog] that controls the [Sort] of [HomeViewModel.playlistList]. + * + * @author Alexander Capehart (OxygenCobalt) + */ +@AndroidEntryPoint +class PlaylistSortDialog : SortDialog() { + private val homeModel: HomeViewModel by activityViewModels() + + override fun getInitialSort() = homeModel.playlistSort + + override fun applyChosenSort(sort: Sort) { + homeModel.applyPlaylistSort(sort) + } + + override fun getModeChoices() = + listOf(Sort.Mode.ByName, Sort.Mode.ByDuration, Sort.Mode.ByCount) +} diff --git a/app/src/main/java/org/oxycblt/auxio/home/sort/SongSortDialog.kt b/app/src/main/java/org/oxycblt/auxio/home/sort/SongSortDialog.kt new file mode 100644 index 000000000..bec18719a --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/home/sort/SongSortDialog.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Auxio Project + * SongSortDialog.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.home.sort + +import androidx.fragment.app.activityViewModels +import dagger.hilt.android.AndroidEntryPoint +import org.oxycblt.auxio.home.HomeViewModel +import org.oxycblt.auxio.list.Sort +import org.oxycblt.auxio.list.sort.SortDialog + +/** + * A [SortDialog] that controls the [Sort] of [HomeViewModel.songList]. + * + * @author Alexander Capehart (OxygenCobalt) + */ +@AndroidEntryPoint +class SongSortDialog : SortDialog() { + private val homeModel: HomeViewModel by activityViewModels() + + override fun getInitialSort() = homeModel.songSort + + override fun applyChosenSort(sort: Sort) { + homeModel.applySongSort(sort) + } + + override fun getModeChoices() = + listOf( + Sort.Mode.ByName, + Sort.Mode.ByArtist, + Sort.Mode.ByAlbum, + Sort.Mode.ByDate, + Sort.Mode.ByDuration, + Sort.Mode.ByDateAdded) +} diff --git a/app/src/main/java/org/oxycblt/auxio/list/Listeners.kt b/app/src/main/java/org/oxycblt/auxio/list/Listeners.kt index d728a6142..c7704e503 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/Listeners.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/Listeners.kt @@ -115,9 +115,8 @@ interface SelectableListListener : ClickableListListener { * Called when an item in the list requests that a menu related to it should be opened. * * @param item The [T] item to open a menu for. - * @param anchor The [View] to anchor the menu to. */ - fun onOpenMenu(item: T, anchor: View) + fun onOpenMenu(item: T) /** * Called when an item in the list requests that it be selected. @@ -148,6 +147,6 @@ interface SelectableListListener : ClickableListListener { true } // Map the menu button to the menu opening listener. - menuButton.setOnClickListener { onOpenMenu(item, it) } + menuButton.setOnClickListener { onOpenMenu(item) } } } diff --git a/app/src/main/java/org/oxycblt/auxio/list/Sort.kt b/app/src/main/java/org/oxycblt/auxio/list/Sort.kt index 9b453e774..555fd6b9a 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/Sort.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/Sort.kt @@ -18,8 +18,6 @@ package org.oxycblt.auxio.list -import androidx.annotation.IdRes -import java.lang.IllegalStateException import kotlin.math.max import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.R @@ -164,8 +162,6 @@ data class Sort(val mode: Mode, val direction: Direction) { sealed interface Mode { /** The integer representation of this sort mode. */ val intCode: Int - /** The item ID of this sort mode in menu resources. */ - val itemId: Int /** The string resource of the human-readable name of this sort mode. */ val stringRes: Int @@ -223,9 +219,6 @@ data class Sort(val mode: Mode, val direction: Direction) { override val intCode: Int get() = IntegerTable.SORT_BY_NAME - override val itemId: Int - get() = R.id.option_sort_name - override val stringRes: Int get() = R.string.lbl_name @@ -254,9 +247,6 @@ data class Sort(val mode: Mode, val direction: Direction) { override val intCode: Int get() = IntegerTable.SORT_BY_ALBUM - override val itemId: Int - get() = R.id.option_sort_album - override val stringRes: Int get() = R.string.lbl_album @@ -277,9 +267,6 @@ data class Sort(val mode: Mode, val direction: Direction) { override val intCode: Int get() = IntegerTable.SORT_BY_ARTIST - override val itemId: Int - get() = R.id.option_sort_artist - override val stringRes: Int get() = R.string.lbl_artist @@ -309,9 +296,6 @@ data class Sort(val mode: Mode, val direction: Direction) { override val intCode: Int get() = IntegerTable.SORT_BY_YEAR - override val itemId: Int - get() = R.id.option_sort_year - override val stringRes: Int get() = R.string.lbl_date @@ -334,9 +318,6 @@ data class Sort(val mode: Mode, val direction: Direction) { override val intCode: Int get() = IntegerTable.SORT_BY_DURATION - override val itemId: Int - get() = R.id.option_sort_duration - override val stringRes: Int get() = R.string.lbl_duration @@ -372,9 +353,6 @@ data class Sort(val mode: Mode, val direction: Direction) { override val intCode: Int get() = IntegerTable.SORT_BY_COUNT - override val itemId: Int - get() = R.id.option_sort_count - override val stringRes: Int get() = R.string.lbl_song_count @@ -406,9 +384,6 @@ data class Sort(val mode: Mode, val direction: Direction) { override val intCode: Int get() = IntegerTable.SORT_BY_DISC - override val itemId: Int - get() = throw IllegalStateException() - override val stringRes: Int get() = R.string.lbl_disc @@ -428,9 +403,6 @@ data class Sort(val mode: Mode, val direction: Direction) { override val intCode: Int get() = IntegerTable.SORT_BY_TRACK - override val itemId: Int - get() = throw IllegalStateException() - override val stringRes: Int get() = R.string.lbl_track @@ -451,9 +423,6 @@ data class Sort(val mode: Mode, val direction: Direction) { override val intCode: Int get() = IntegerTable.SORT_BY_DATE_ADDED - override val itemId: Int - get() = R.id.option_sort_date_added - override val stringRes: Int get() = R.string.lbl_date_added @@ -488,27 +457,6 @@ data class Sort(val mode: Mode, val direction: Direction) { ByDateAdded.intCode -> ByDateAdded else -> null } - - /** - * Convert a menu item ID into a [Mode]. - * - * @param itemId The menu resource ID to convert - * @return A [Mode] corresponding to the given ID, or null if the ID is invalid. - * @see itemId - */ - fun fromItemId(@IdRes itemId: Int) = - when (itemId) { - ByName.itemId -> ByName - ByAlbum.itemId -> ByAlbum - ByArtist.itemId -> ByArtist - ByDate.itemId -> ByDate - ByDuration.itemId -> ByDuration - ByCount.itemId -> ByCount - ByDisc.itemId -> ByDisc - ByTrack.itemId -> ByTrack - ByDateAdded.itemId -> ByDateAdded - else -> null - } } } diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt index 27abdeab1..1a3a29d5f 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt @@ -182,7 +182,7 @@ class SearchFragment : ListFragment() { } } - override fun onOpenMenu(item: Music, anchor: View) { + override fun onOpenMenu(item: Music) { when (item) { is Song -> listModel.openMenu(R.menu.item_song, item, searchModel.playWith) is Album -> listModel.openMenu(R.menu.item_album, item) diff --git a/app/src/main/res/menu/toolbar_home.xml b/app/src/main/res/menu/toolbar_home.xml index 7e27b3a49..9aa0360de 100644 --- a/app/src/main/res/menu/toolbar_home.xml +++ b/app/src/main/res/menu/toolbar_home.xml @@ -9,46 +9,10 @@ app:showAsAction="ifRoom" /> - - - - - - - - - - - - - - - - + app:showAsAction="ifRoom" /> + + + + + @@ -65,6 +80,36 @@ app:destination="@id/play_from_genre_dialog" /> + + + + + + + + + +