home: add fast scroller

Add a new fast-scroller to the home view. This required some annoying
hacks to work, but it seems to work pretty well.
This commit is contained in:
OxygenCobalt 2021-10-13 19:49:20 -06:00
parent 7ef10fa4f8
commit a253cfccc4
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
11 changed files with 131 additions and 26 deletions

View file

@ -86,7 +86,7 @@ dependencies {
// Media
// TODO: Migrate to media2 when I can figure out how to use it
implementation "androidx.media:media:1.4.2"
implementation "androidx.media:media:1.4.3"
// Preferences
implementation "androidx.preference:preference-ktx:1.1.1"
@ -102,6 +102,8 @@ dependencies {
// Material
implementation "com.google.android.material:material:1.5.0-alpha04"
implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
// --- DEBUG ---
// Lint

View file

@ -29,7 +29,6 @@ import org.oxycblt.auxio.databinding.DialogAccentBinding
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.LifecycleDialog
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.resolveColor
/**
* Dialog responsible for showing the list of accents to select.
@ -55,13 +54,9 @@ class AccentDialog : LifecycleDialog() {
binding.accentRecycler.apply {
adapter = AccentAdapter(pendingAccent) { accent ->
pendingAccent = accent
updateAccent()
}
}
updateAccent()
logD("Dialog created.")
return binding.root
@ -90,15 +85,6 @@ class AccentDialog : LifecycleDialog() {
builder.setNegativeButton(android.R.string.cancel, null)
}
private fun updateAccent() {
val accentColor = pendingAccent.color.resolveColor(requireContext())
(requireDialog() as AlertDialog).apply {
getButton(AlertDialog.BUTTON_POSITIVE)?.setTextColor(accentColor)
getButton(AlertDialog.BUTTON_NEGATIVE)?.setTextColor(accentColor)
}
}
companion object {
const val TAG = BuildConfig.APPLICATION_ID + ".tag.ACCENT_PICKER"
const val KEY_PENDING_ACCENT = BuildConfig.APPLICATION_ID + ".key.PENDING_ACCENT"

View file

@ -23,14 +23,17 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.iterator
import androidx.core.view.updatePadding
import androidx.core.view.updatePaddingRelative
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayoutMediator
import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R
@ -46,10 +49,10 @@ import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.ui.SortMode
import org.oxycblt.auxio.ui.memberBinding
import org.oxycblt.auxio.util.applyEdge
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logE
import org.oxycblt.auxio.util.makeScrollingViewFade
/**
* The main "Launching Point" fragment of Auxio, allowing navigation to the detail
@ -63,6 +66,7 @@ import org.oxycblt.auxio.util.makeScrollingViewFade
* @author OxygenCobalt
*/
class HomeFragment : Fragment() {
private val binding: FragmentHomeBinding by memberBinding(FragmentHomeBinding::inflate)
private val detailModel: DetailViewModel by activityViewModels()
private val homeModel: HomeViewModel by activityViewModels()
@ -71,7 +75,6 @@ class HomeFragment : Fragment() {
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = FragmentHomeBinding.inflate(inflater)
val sortItem: MenuItem
// --- UI SETUP ---
@ -82,7 +85,34 @@ class HomeFragment : Fragment() {
binding.homeAppbar.updatePadding(top = bars.top)
}
binding.homeAppbar.makeScrollingViewFade(binding.homeToolbar)
binding.homeAppbar.apply {
addOnOffsetChangedListener(
AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
binding.homeToolbar.alpha = (binding.homeToolbar.height + verticalOffset) /
binding.homeToolbar.height.toFloat()
}
)
post {
// To add our fast scroller, we need to
val vOffset = (
(layoutParams as CoordinatorLayout.LayoutParams)
.behavior as AppBarLayout.Behavior
).topAndBottomOffset
binding.homePager.updatePaddingRelative(
bottom = binding.homeAppbar.totalScrollRange + vOffset
)
binding.homeAppbar.addOnOffsetChangedListener(
AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
binding.homePager.updatePaddingRelative(
bottom = binding.homeAppbar.totalScrollRange + verticalOffset
)
}
)
}
}
binding.homeToolbar.apply {
setOnMenuItemClickListener { item ->

View file

@ -23,11 +23,15 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import me.zhanghai.android.fastscroll.PopupTextProvider
import org.oxycblt.auxio.R
import org.oxycblt.auxio.home.HomeFragmentDirections
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.ui.AlbumViewHolder
import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.ui.SortMode
import org.oxycblt.auxio.ui.newMenu
import org.oxycblt.auxio.ui.sliceArticle
class AlbumListFragment : HomeListFragment() {
override fun onCreateView(
@ -51,6 +55,23 @@ class AlbumListFragment : HomeListFragment() {
return binding.root
}
override val popupProvider: PopupTextProvider
get() = PopupTextProvider { idx ->
val album = homeModel.albums.value!![idx]
when (homeModel.getSortForDisplay(DisplayMode.SHOW_ALBUMS)) {
SortMode.ASCENDING, SortMode.DESCENDING -> album.name.sliceArticle()
.first().uppercase()
SortMode.ARTIST -> album.artist.name.sliceArticle()
.first().uppercase()
SortMode.YEAR -> album.year.toString()
else -> ""
}
}
class AlbumAdapter(
private val doOnClick: (data: Album) -> Unit,
private val doOnLongClick: (view: View, data: Album) -> Unit,

View file

@ -23,11 +23,13 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import me.zhanghai.android.fastscroll.PopupTextProvider
import org.oxycblt.auxio.R
import org.oxycblt.auxio.home.HomeFragmentDirections
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.ui.ArtistViewHolder
import org.oxycblt.auxio.ui.newMenu
import org.oxycblt.auxio.ui.sliceArticle
class ArtistListFragment : HomeListFragment() {
override fun onCreateView(
@ -51,6 +53,11 @@ class ArtistListFragment : HomeListFragment() {
return binding.root
}
override val popupProvider: PopupTextProvider
get() = PopupTextProvider { idx ->
homeModel.artists.value!![idx].name.sliceArticle().first().uppercase()
}
class ArtistAdapter(
private val doOnClick: (data: Artist) -> Unit,
private val doOnLongClick: (view: View, data: Artist) -> Unit,

View file

@ -23,11 +23,13 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import me.zhanghai.android.fastscroll.PopupTextProvider
import org.oxycblt.auxio.R
import org.oxycblt.auxio.home.HomeFragmentDirections
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.ui.GenreViewHolder
import org.oxycblt.auxio.ui.newMenu
import org.oxycblt.auxio.ui.sliceArticle
class GenreListFragment : HomeListFragment() {
override fun onCreateView(
@ -51,6 +53,11 @@ class GenreListFragment : HomeListFragment() {
return binding.root
}
override val popupProvider: PopupTextProvider
get() = PopupTextProvider { idx ->
homeModel.genres.value!![idx].name.sliceArticle().first().uppercase()
}
class GenreAdapter(
private val doOnClick: (data: Genre) -> Unit,
private val doOnLongClick: (view: View, data: Genre) -> Unit,

View file

@ -19,19 +19,25 @@
package org.oxycblt.auxio.home.list
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.RecyclerView
import me.zhanghai.android.fastscroll.FastScrollerBuilder
import me.zhanghai.android.fastscroll.PopupTextProvider
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
import org.oxycblt.auxio.home.HomeViewModel
import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.memberBinding
import org.oxycblt.auxio.util.applySpans
import org.oxycblt.auxio.util.resolveDrawable
open class HomeListFragment : Fragment() {
abstract class HomeListFragment : Fragment() {
protected val binding: FragmentHomeListBinding by memberBinding(
FragmentHomeListBinding::inflate
)
@ -39,6 +45,20 @@ open class HomeListFragment : Fragment() {
protected val homeModel: HomeViewModel by activityViewModels()
protected val playbackModel: PlaybackViewModel by activityViewModels()
abstract val popupProvider: PopupTextProvider
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.homeRecycler.apply {
FastScrollerBuilder(this)
.useMd2Style()
.setPopupTextProvider(popupProvider)
.setTrackDrawable(R.drawable.ui_scroll_track.resolveDrawable(context))
.build()
}
}
protected fun <T : BaseModel, VH : RecyclerView.ViewHolder> setupRecycler(
@IdRes uniqueId: Int,
homeAdapter: HomeAdapter<T, VH>,

View file

@ -23,11 +23,16 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import me.zhanghai.android.fastscroll.PopupTextProvider
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.ItemPlayShuffleBinding
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.ui.SongViewHolder
import org.oxycblt.auxio.ui.SortMode
import org.oxycblt.auxio.ui.newMenu
import org.oxycblt.auxio.ui.sliceArticle
import org.oxycblt.auxio.util.applySpans
import org.oxycblt.auxio.util.inflater
class SongListFragment : HomeListFragment() {
@ -46,10 +51,33 @@ class SongListFragment : HomeListFragment() {
)
setupRecycler(R.id.home_song_list, adapter, homeModel.songs)
binding.homeRecycler.applySpans { it == 0 }
return binding.root
}
override val popupProvider: PopupTextProvider
get() = PopupTextProvider { idx ->
if (idx == 0) {
return@PopupTextProvider ""
}
val song = homeModel.songs.value!![idx]
when (homeModel.getSortForDisplay(DisplayMode.SHOW_SONGS)) {
SortMode.ASCENDING, SortMode.DESCENDING -> song.name.sliceArticle()
.first().uppercase()
SortMode.ARTIST -> song.album.artist.name.sliceArticle()
.first().uppercase()
SortMode.ALBUM -> song.album.name.sliceArticle()
.first().uppercase()
SortMode.YEAR -> song.album.year.toString()
}
}
inner class SongsAdapter(
private val doOnClick: (data: Song) -> Unit,
private val doOnLongClick: (view: View, data: Song) -> Unit,

View file

@ -46,7 +46,6 @@ import org.oxycblt.auxio.util.applyEdge
import org.oxycblt.auxio.util.applySpans
import org.oxycblt.auxio.util.getSystemServiceSafe
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.makeScrollingViewFade
/**
* A [Fragment] that allows for the searching of the entire music library.
@ -81,8 +80,6 @@ class SearchFragment : Fragment() {
binding.searchAppbar.updatePadding(top = bars.top)
}
binding.searchAppbar.makeScrollingViewFade(binding.searchToolbar)
binding.searchToolbar.apply {
val itemId = when (searchModel.filterMode) {
DisplayMode.SHOW_SONGS -> R.id.option_filter_songs

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid
android:color="@android:color/transparent" />
<size android:width="@dimen/spacing_small" />
</shape>

View file

@ -6,6 +6,7 @@
<style name="Theme.Auxio.V27" parent="Theme.Auxio" />
<!-- Android 12 configuration -->
<style name="Theme.Auxio.V31" parent="Theme.Auxio.V27">
<!-- Make sure to apply more accent-friendly values on older versions -->
<item name="colorSecondary">?attr/colorPrimary</item>
<item name="colorOnSecondary">?attr/colorOnPrimary</item>
<item name="colorSecondaryContainer">?attr/colorPrimaryContainer</item>
@ -21,9 +22,6 @@
<item name="colorControlNormal">@color/control</item>
<item name="colorControlHighlight">@color/overlay_selection</item>
<item name="colorControlActivated">?attr/colorPrimary</item>
<item name="android:textColorHighlight">@color/overlay_text_highlight</item>
<item name="android:textColorHighlightInverse">@color/overlay_text_highlight_inverse</item>
</style>
<!-- Base theme -->
@ -31,7 +29,9 @@
<!-- Values -->
<item name="colorOutline">@color/overlay_stroke</item>
<!-- Appearance -->
<!-- Android component magic -->
<item name="android:textColorHighlight">@color/overlay_text_highlight</item>
<item name="android:textColorHighlightInverse">@color/overlay_text_highlight_inverse</item>
<item name="android:colorBackground">?attr/colorSurface</item>
<item name="android:windowBackground">?attr/colorSurface</item>
<item name="android:fontFamily">@font/inter</item>