From a49ad5ddad34ef2d982a22e44af93eba469a6867 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Fri, 4 Dec 2020 19:48:01 -0700 Subject: [PATCH] Add library item customization Add the ability to customize library items. --- .../oxycblt/auxio/library/LibraryFragment.kt | 4 +- .../oxycblt/auxio/library/LibraryViewModel.kt | 41 ++++++++++------- .../auxio/library/adapters/LibraryAdapter.kt | 36 ++++++++------- .../org/oxycblt/auxio/music/MusicStore.kt | 14 +++--- .../org/oxycblt/auxio/recycler/DisplayMode.kt | 45 +++++++++++++++++++ .../oxycblt/auxio/recycler/NoLeakThumbView.kt | 14 +++--- .../org/oxycblt/auxio/recycler/ShowMode.kt | 21 --------- .../org/oxycblt/auxio/recycler/SortMode.kt | 3 +- .../auxio/settings/SettingsListFragment.kt | 11 +++++ .../oxycblt/auxio/settings/SettingsManager.kt | 19 +++++++- .../org/oxycblt/auxio/songs/SongsFragment.kt | 4 +- app/src/main/res/values/arrays.xml | 12 +++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml-v27/prefs_main.xml | 11 ++++- app/src/main/res/xml/prefs_main.xml | 31 +++++++++++++ 15 files changed, 193 insertions(+), 74 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt delete mode 100644 app/src/main/java/org/oxycblt/auxio/recycler/ShowMode.kt diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt index dd3f62199..fa16118fd 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -50,7 +50,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { val musicStore = MusicStore.getInstance() val libraryAdapter = LibraryAdapter( - libraryModel.showMode.value!!, + libraryModel.displayMode.value!!, doOnClick = { navToItem(it) }, doOnLongClick = { data, view -> showActionsForItem(data, view) } ) @@ -129,7 +129,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { // Update the adapter with the new data libraryAdapter.updateData( mode.getSortedBaseModelList( - musicStore.getListForShowMode(libraryModel.showMode.value!!) + musicStore.getListForShowMode(libraryModel.displayMode.value!!) ) ) diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt index 7976778d1..bcda94b63 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt @@ -6,14 +6,12 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.oxycblt.auxio.R import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.Header import org.oxycblt.auxio.music.MusicStore -import org.oxycblt.auxio.recycler.ShowMode +import org.oxycblt.auxio.recycler.DisplayMode import org.oxycblt.auxio.recycler.SortMode import org.oxycblt.auxio.settings.SettingsManager @@ -22,10 +20,9 @@ import org.oxycblt.auxio.settings.SettingsManager * functionality. * @author OxygenCobalt */ -class LibraryViewModel : ViewModel() { - // TODO: Move these to prefs when they're added - private val mShowMode = MutableLiveData(ShowMode.SHOW_ARTISTS) - val showMode: LiveData get() = mShowMode +class LibraryViewModel : ViewModel(), SettingsManager.Callback { + private val mDisplayMode = MutableLiveData(DisplayMode.SHOW_ARTISTS) + val displayMode: LiveData get() = mDisplayMode private val mSortMode = MutableLiveData(SortMode.ALPHA_DOWN) val sortMode: LiveData get() = mSortMode @@ -42,12 +39,10 @@ class LibraryViewModel : ViewModel() { private val settingsManager = SettingsManager.getInstance() init { - // The SortMode isn't really that urgent, so throw it into a coroutine because I can - viewModelScope.launch { - mSortMode.value = withContext(Dispatchers.IO) { - settingsManager.librarySortMode - } - } + settingsManager.addCallback(this) + + mDisplayMode.value = settingsManager.libraryDisplayMode + mSortMode.value = settingsManager.librarySortMode } /** @@ -70,10 +65,10 @@ class LibraryViewModel : ViewModel() { viewModelScope.launch { val musicStore = MusicStore.getInstance() val combined = mutableListOf() - val children = showMode.value!!.getChildren() + val children = displayMode.value!!.getChildren() - // If the Library ShowMode supports it, include artists / genres in the search. - if (children.contains(ShowMode.SHOW_GENRES)) { + // If the Library DisplayMode supports it, include artists / genres in the search. + if (children.contains(DisplayMode.SHOW_GENRES)) { val genres = musicStore.genres.filter { it.name.contains(query, true) } if (genres.isNotEmpty()) { @@ -82,7 +77,7 @@ class LibraryViewModel : ViewModel() { } } - if (children.contains(ShowMode.SHOW_ARTISTS)) { + if (children.contains(DisplayMode.SHOW_ARTISTS)) { val artists = musicStore.artists.filter { it.name.contains(query, true) } if (artists.isNotEmpty()) { @@ -137,4 +132,16 @@ class LibraryViewModel : ViewModel() { settingsManager.librarySortMode = mode } } + + // --- OVERRIDES --- + + override fun onCleared() { + super.onCleared() + + settingsManager.removeCallback(this) + } + + override fun onLibDisplayModeUpdate(displayMode: DisplayMode) { + mDisplayMode.value = displayMode + } } diff --git a/app/src/main/java/org/oxycblt/auxio/library/adapters/LibraryAdapter.kt b/app/src/main/java/org/oxycblt/auxio/library/adapters/LibraryAdapter.kt index 88f5e0778..db5e16eb4 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/adapters/LibraryAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/adapters/LibraryAdapter.kt @@ -7,7 +7,7 @@ import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.Genre -import org.oxycblt.auxio.recycler.ShowMode +import org.oxycblt.auxio.recycler.DisplayMode import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder @@ -17,7 +17,7 @@ import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder * @author OxygenCobalt */ class LibraryAdapter( - private val showMode: ShowMode, + private val displayMode: DisplayMode, private val doOnClick: (data: BaseModel) -> Unit, private val doOnLongClick: (data: BaseModel, view: View) -> Unit ) : RecyclerView.Adapter() { @@ -26,10 +26,10 @@ class LibraryAdapter( init { // Assign the data on startup depending on the type - data = when (showMode) { - ShowMode.SHOW_GENRES -> listOf() - ShowMode.SHOW_ARTISTS -> listOf() - ShowMode.SHOW_ALBUMS -> listOf() + data = when (displayMode) { + DisplayMode.SHOW_GENRES -> listOf() + DisplayMode.SHOW_ARTISTS -> listOf() + DisplayMode.SHOW_ALBUMS -> listOf() else -> listOf() } @@ -39,20 +39,20 @@ class LibraryAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { // Return a different View Holder depending on the show type - return when (showMode) { - ShowMode.SHOW_GENRES -> GenreViewHolder.from(parent.context, doOnClick, doOnLongClick) - ShowMode.SHOW_ARTISTS -> ArtistViewHolder.from(parent.context, doOnClick, doOnLongClick) - ShowMode.SHOW_ALBUMS -> AlbumViewHolder.from(parent.context, doOnClick, doOnLongClick) + return when (displayMode) { + DisplayMode.SHOW_GENRES -> GenreViewHolder.from(parent.context, doOnClick, doOnLongClick) + DisplayMode.SHOW_ARTISTS -> ArtistViewHolder.from(parent.context, doOnClick, doOnLongClick) + DisplayMode.SHOW_ALBUMS -> AlbumViewHolder.from(parent.context, doOnClick, doOnLongClick) - else -> error("Bad ShowMode given.") + else -> error("Bad DisplayMode given.") } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - when (showMode) { - ShowMode.SHOW_GENRES -> (holder as GenreViewHolder).bind(data[position] as Genre) - ShowMode.SHOW_ARTISTS -> (holder as ArtistViewHolder).bind(data[position] as Artist) - ShowMode.SHOW_ALBUMS -> (holder as AlbumViewHolder).bind(data[position] as Album) + when (displayMode) { + DisplayMode.SHOW_GENRES -> (holder as GenreViewHolder).bind(data[position] as Genre) + DisplayMode.SHOW_ARTISTS -> (holder as ArtistViewHolder).bind(data[position] as Artist) + DisplayMode.SHOW_ALBUMS -> (holder as AlbumViewHolder).bind(data[position] as Album) else -> return } @@ -60,8 +60,10 @@ class LibraryAdapter( // Update the data, as its an internal value. fun updateData(newData: List) { - data = newData + if (data != newData) { + data = newData - notifyDataSetChanged() + notifyDataSetChanged() + } } } diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt index 33e878f95..a8b71d8a5 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt @@ -8,7 +8,7 @@ import org.oxycblt.auxio.R import org.oxycblt.auxio.music.processing.MusicLoader import org.oxycblt.auxio.music.processing.MusicLoaderResponse import org.oxycblt.auxio.music.processing.MusicSorter -import org.oxycblt.auxio.recycler.ShowMode +import org.oxycblt.auxio.recycler.DisplayMode /** * The main storage for music items. Use [MusicStore.getInstance] to get the single instance of it. @@ -95,12 +95,12 @@ class MusicStore private constructor() { } } - fun getListForShowMode(showMode: ShowMode): List { - return when (showMode) { - ShowMode.SHOW_GENRES -> mGenres - ShowMode.SHOW_ARTISTS -> mArtists - ShowMode.SHOW_ALBUMS -> mAlbums - ShowMode.SHOW_SONGS -> mSongs + fun getListForShowMode(displayMode: DisplayMode): List { + return when (displayMode) { + DisplayMode.SHOW_GENRES -> mGenres + DisplayMode.SHOW_ARTISTS -> mArtists + DisplayMode.SHOW_ALBUMS -> mAlbums + DisplayMode.SHOW_SONGS -> mSongs } } diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt new file mode 100644 index 000000000..34c832591 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt @@ -0,0 +1,45 @@ +package org.oxycblt.auxio.recycler + +import androidx.annotation.DrawableRes +import org.oxycblt.auxio.R +import java.lang.IllegalArgumentException + +/** + * An enum for determining what items to show in a given list. + * @author OxygenCobalt + */ +enum class DisplayMode(@DrawableRes val iconRes: Int) { + SHOW_GENRES(R.drawable.ic_genre), + SHOW_ARTISTS(R.drawable.ic_artist), + SHOW_ALBUMS(R.drawable.ic_album), + SHOW_SONGS(R.drawable.ic_song); + + /** + * Make a slice of all the values that this DisplayMode covers. + * + * ex. SHOW_ARTISTS would return SHOW_ARTISTS, SHOW_ALBUMS, and SHOW_SONGS + * @return The values that this DisplayMode covers. + */ + fun getChildren(): List { + val vals = values() + + return vals.slice(vals.indexOf(this) until vals.size) + } + + companion object { + /** + * A valueOf wrapper that will return a default value if given a null/invalid string. + */ + fun valueOfOrFallback(value: String?): DisplayMode { + if (value == null) { + return SHOW_ARTISTS + } + + return try { + valueOf(value) + } catch (e: IllegalArgumentException) { + SHOW_ARTISTS + } + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/NoLeakThumbView.kt b/app/src/main/java/org/oxycblt/auxio/recycler/NoLeakThumbView.kt index 572148a94..f856e6b73 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/NoLeakThumbView.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/NoLeakThumbView.kt @@ -41,10 +41,10 @@ class NoLeakThumbView @JvmOverloads constructor( ) : ConstraintLayout(context, attrs, defStyleAttr), FastScrollerView.ItemIndicatorSelectedCallback { - private var thumbColor = ColorStateList.valueOf(accent.first.toColor(context)) - private var iconColor = R.color.background.toColor(context) - var textAppearanceRes = R.style.TextAppearance_ThumbIndicator - private var textColor = R.color.background.toColor(context) + private val thumbColor = ColorStateList.valueOf(accent.first.toColor(context)) + private val iconColor = R.color.background.toColor(context) + private val textAppearanceRes = R.style.TextAppearance_ThumbIndicator + private val textColor = R.color.background.toColor(context) private val thumbView: ViewGroup private val textView: TextView @@ -56,7 +56,10 @@ class NoLeakThumbView @JvmOverloads constructor( private val thumbAnimation: SpringAnimation init { - LayoutInflater.from(context).inflate(R.layout.fast_scroller_thumb_view, this, true) + LayoutInflater.from(context).inflate( + R.layout.fast_scroller_thumb_view, this, true + ) + thumbView = findViewById(R.id.fast_scroller_thumb) textView = thumbView.findViewById(R.id.fast_scroller_thumb_text) iconView = thumbView.findViewById(R.id.fast_scroller_thumb_icon) @@ -76,6 +79,7 @@ class NoLeakThumbView @JvmOverloads constructor( @SuppressLint("ClickableViewAccessibility") fun setupWithFastScroller(fastScrollerView: FastScrollerView) { check(!isSetup) { "Only set this view's FastScrollerView once!" } + this.fastScrollerView = fastScrollerView fastScrollerView.itemIndicatorSelectedCallbacks += this diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/ShowMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/ShowMode.kt deleted file mode 100644 index b5cb863ee..000000000 --- a/app/src/main/java/org/oxycblt/auxio/recycler/ShowMode.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.oxycblt.auxio.recycler - -/** - * An enum for determining what items to show in a given list. - * @author OxygenCobalt - */ -enum class ShowMode { - SHOW_GENRES, SHOW_ARTISTS, SHOW_ALBUMS, SHOW_SONGS; - - /** - * Make a slice of all the values that this ShowMode covers. - * - * ex. SHOW_ARTISTS would return SHOW_ARTISTS, SHOW_ALBUMS, and SHOW_SONGS - * @return The values that this ShowMode covers. - */ - fun getChildren(): List { - val vals = values() - - return vals.slice(vals.indexOf(this) until vals.size) - } -} diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt index 19f94f6ff..b2fc5fcf1 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt @@ -1,5 +1,6 @@ package org.oxycblt.auxio.recycler +import androidx.annotation.DrawableRes import org.oxycblt.auxio.R import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist @@ -12,7 +13,7 @@ import org.oxycblt.auxio.music.Song * @property iconRes The icon for this [SortMode] * @author OxygenCobalt */ -enum class SortMode(val iconRes: Int) { +enum class SortMode(@DrawableRes val iconRes: Int) { // Icons for each mode are assigned to the enums themselves NONE(R.drawable.ic_sort_none), ALPHA_UP(R.drawable.ic_sort_alpha_up), diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt index 8439b67b9..5c124cb42 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt @@ -15,6 +15,7 @@ import androidx.recyclerview.widget.RecyclerView import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.customview.customView import org.oxycblt.auxio.R +import org.oxycblt.auxio.recycler.DisplayMode import org.oxycblt.auxio.settings.adapters.AccentAdapter import org.oxycblt.auxio.ui.ACCENTS import org.oxycblt.auxio.ui.accent @@ -78,6 +79,16 @@ class SettingsListFragment : PreferenceFragmentCompat() { true } } + + SettingsManager.Keys.KEY_LIBRARY_DISPLAY_MODE -> { + setIcon(SettingsManager.getInstance().libraryDisplayMode.iconRes) + + onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, value -> + setIcon(DisplayMode.valueOfOrFallback(value as String).iconRes) + + true + } + } } } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt index 8bdad65f6..1d5278f4d 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt @@ -3,6 +3,7 @@ package org.oxycblt.auxio.settings import android.content.Context import android.content.SharedPreferences import androidx.preference.PreferenceManager +import org.oxycblt.auxio.recycler.DisplayMode import org.oxycblt.auxio.recycler.SortMode import org.oxycblt.auxio.ui.ACCENTS @@ -57,6 +58,16 @@ class SettingsManager private constructor(context: Context) : return sharedPrefs.getBoolean(Keys.KEY_USE_ALT_NOTIFICATION_ACTION, false) } + val libraryDisplayMode: DisplayMode + get() { + return DisplayMode.valueOfOrFallback( + sharedPrefs.getString( + Keys.KEY_LIBRARY_DISPLAY_MODE, + DisplayMode.SHOW_ARTISTS.toString() + ) + ) + } + var librarySortMode: SortMode get() { return SortMode.fromInt( @@ -95,6 +106,10 @@ class SettingsManager private constructor(context: Context) : Keys.KEY_USE_ALT_NOTIFICATION_ACTION -> callbacks.forEach { it.onNotifActionUpdate(useAltNotifAction) } + + Keys.KEY_LIBRARY_DISPLAY_MODE -> callbacks.forEach { + it.onLibDisplayModeUpdate(libraryDisplayMode) + } } } @@ -128,12 +143,13 @@ class SettingsManager private constructor(context: Context) : } object Keys { - const val KEY_LIBRARY_SORT_MODE = "KEY_LIBRARY_SORT_MODE" const val KEY_THEME = "KEY_THEME" const val KEY_ACCENT = "KEY_ACCENT" const val KEY_EDGE_TO_EDGE = "KEY_EDGE" const val KEY_COLORIZE_NOTIFICATION = "KEY_COLOR_NOTIF" const val KEY_USE_ALT_NOTIFICATION_ACTION = "KEY_ALT_NOTIF_ACTION" + const val KEY_LIBRARY_DISPLAY_MODE = "KEY_LIBRARY_DISPLAY_MODE" + const val KEY_LIBRARY_SORT_MODE = "KEY_LIBRARY_SORT_MODE" } object EntryNames { @@ -145,5 +161,6 @@ class SettingsManager private constructor(context: Context) : interface Callback { fun onColorizeNotifUpdate(doColorize: Boolean) {} fun onNotifActionUpdate(useAltAction: Boolean) {} + fun onLibDisplayModeUpdate(displayMode: DisplayMode) {} } } diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index e832bd479..17c5b4fdb 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -118,7 +118,8 @@ class SongsFragment : Fragment() { var isGood = true if (concatInterval == -1) { - // If the screen size is too small to contain all the entries, + // If the screen size is too small to contain all the entries, truncate entries + // so that the fast scroller entries fit. val maxEntries = (height / (indicatorTextSize + textPadding)) if (total > maxEntries.toInt()) { @@ -164,7 +165,6 @@ class SongsFragment : Fragment() { binding.songFastScrollThumb.apply { setupWithFastScroller(binding.songFastScroll) - textAppearanceRes = R.style.TextAppearance_ThumbIndicator } } } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 25ec51de5..46e86e14b 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -11,4 +11,16 @@ LIGHT DARK + + + @string/label_genres + @string/label_artists + @string/label_albums + + + + SHOW_GENRES + SHOW_ARTISTS + SHOW_ALBUMS + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e840061e4..e2a1811ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,6 +47,7 @@ Edge-to-edge is enabled Edge-to-edge is disabled Display + Library Items Colorize notification] Show album art on notification Hide album art on notification diff --git a/app/src/main/res/xml-v27/prefs_main.xml b/app/src/main/res/xml-v27/prefs_main.xml index c220ecf85..10eb68138 100644 --- a/app/src/main/res/xml-v27/prefs_main.xml +++ b/app/src/main/res/xml-v27/prefs_main.xml @@ -33,6 +33,15 @@ android:title="@string/setting_display" android:layout="@layout/item_header"> + + + android:defaultValue="false" /> \ No newline at end of file diff --git a/app/src/main/res/xml/prefs_main.xml b/app/src/main/res/xml/prefs_main.xml index b229ea874..447b6f1d9 100644 --- a/app/src/main/res/xml/prefs_main.xml +++ b/app/src/main/res/xml/prefs_main.xml @@ -19,4 +19,35 @@ android:icon="@drawable/ic_accent" app:summary="@string/setting_accent" /> + + + + + + + + + + \ No newline at end of file