Add library item customization

Add the ability to customize library items.
This commit is contained in:
OxygenCobalt 2020-12-04 19:48:01 -07:00
parent df637a27aa
commit a49ad5ddad
15 changed files with 193 additions and 74 deletions

View file

@ -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!!)
)
)

View file

@ -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<ShowMode> get() = mShowMode
class LibraryViewModel : ViewModel(), SettingsManager.Callback {
private val mDisplayMode = MutableLiveData(DisplayMode.SHOW_ARTISTS)
val displayMode: LiveData<DisplayMode> get() = mDisplayMode
private val mSortMode = MutableLiveData(SortMode.ALPHA_DOWN)
val sortMode: LiveData<SortMode> 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<BaseModel>()
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
}
}

View file

@ -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<RecyclerView.ViewHolder>() {
@ -26,10 +26,10 @@ class LibraryAdapter(
init {
// Assign the data on startup depending on the type
data = when (showMode) {
ShowMode.SHOW_GENRES -> listOf<Genre>()
ShowMode.SHOW_ARTISTS -> listOf<Artist>()
ShowMode.SHOW_ALBUMS -> listOf<Album>()
data = when (displayMode) {
DisplayMode.SHOW_GENRES -> listOf<Genre>()
DisplayMode.SHOW_ARTISTS -> listOf<Artist>()
DisplayMode.SHOW_ALBUMS -> listOf<Album>()
else -> listOf<Artist>()
}
@ -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<BaseModel>) {
data = newData
if (data != newData) {
data = newData
notifyDataSetChanged()
notifyDataSetChanged()
}
}
}

View file

@ -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<BaseModel> {
return when (showMode) {
ShowMode.SHOW_GENRES -> mGenres
ShowMode.SHOW_ARTISTS -> mArtists
ShowMode.SHOW_ALBUMS -> mAlbums
ShowMode.SHOW_SONGS -> mSongs
fun getListForShowMode(displayMode: DisplayMode): List<BaseModel> {
return when (displayMode) {
DisplayMode.SHOW_GENRES -> mGenres
DisplayMode.SHOW_ARTISTS -> mArtists
DisplayMode.SHOW_ALBUMS -> mAlbums
DisplayMode.SHOW_SONGS -> mSongs
}
}

View file

@ -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<DisplayMode> {
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
}
}
}
}

View file

@ -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

View file

@ -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<ShowMode> {
val vals = values()
return vals.slice(vals.indexOf(this) until vals.size)
}
}

View file

@ -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),

View file

@ -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
}
}
}
}
}

View file

@ -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) {}
}
}

View file

@ -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
}
}
}

View file

@ -11,4 +11,16 @@
<item>LIGHT</item>
<item>DARK</item>
</array>
<array name="lib_display_entries">
<item>@string/label_genres</item>
<item>@string/label_artists</item>
<item>@string/label_albums</item>
</array>
<array name="lib_display_values">
<item>SHOW_GENRES</item>
<item>SHOW_ARTISTS</item>
<item>SHOW_ALBUMS</item>
</array>
</resources>

View file

@ -47,6 +47,7 @@
<string name="setting_edge_desc_on">Edge-to-edge is enabled</string>
<string name="setting_edge_desc_off">Edge-to-edge is disabled</string>
<string name="setting_display">Display</string>
<string name="setting_lib_display">Library Items</string>
<string name="setting_color_notif">Colorize notification</string>]
<string name="setting_color_desc_on">Show album art on notification</string>
<string name="setting_color_desc_off">Hide album art on notification</string>

View file

@ -33,6 +33,15 @@
android:title="@string/setting_display"
android:layout="@layout/item_header">
<ListPreference
app:key="KEY_LIBRARY_DISPLAY_MODE"
android:title="@string/setting_lib_display"
android:icon="@drawable/ic_artist"
app:entries="@array/lib_display_entries"
app:entryValues="@array/lib_display_values"
app:defaultValue="SHOW_ARTISTS"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
app:key="KEY_COLOR_NOTIF"
android:title="@string/setting_color_notif"
@ -47,7 +56,7 @@
app:iconSpaceReserved="false"
app:summaryOn="@string/setting_use_alt_shuffle"
app:summaryOff="@string/setting_use_alt_loop"
android:defaultValue="false"/>
android:defaultValue="false" />
</PreferenceCategory>
</PreferenceScreen>

View file

@ -19,4 +19,35 @@
android:icon="@drawable/ic_accent"
app:summary="@string/setting_accent" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/setting_display"
android:layout="@layout/item_header">
<ListPreference
app:key="KEY_LIBRARY_DISPLAY_MODE"
android:title="@string/setting_lib_display"
android:icon="@drawable/ic_artist"
app:entries="@array/lib_display_entries"
app:entryValues="@array/lib_display_values"
app:defaultValue="SHOW_ARTISTS"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
app:key="KEY_COLOR_NOTIF"
android:title="@string/setting_color_notif"
app:summaryOn="@string/setting_color_desc_on"
app:summaryOff="@string/setting_color_desc_off"
app:iconSpaceReserved="false"
android:defaultValue="true" />
<SwitchPreferenceCompat
app:key="KEY_ALT_NOTIF_ACTION"
android:title="@string/setting_use_alt_action"
app:iconSpaceReserved="false"
app:summaryOn="@string/setting_use_alt_shuffle"
app:summaryOff="@string/setting_use_alt_loop"
android:defaultValue="false" />
</PreferenceCategory>
</PreferenceScreen>