From 3fca28dd20cbe2a8ed3994b57937159565a208df Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Wed, 25 Nov 2020 16:27:25 -0700 Subject: [PATCH] Add fast scrolling to SongsFragment Add a fast-scroll bar to the list of songs in SongsFragment. --- app/build.gradle | 13 ++- .../org/oxycblt/auxio/songs/SongAdapter.kt | 2 +- .../org/oxycblt/auxio/songs/SongsFragment.kt | 90 +++++++++++++++++-- app/src/main/res/layout/fragment_songs.xml | 33 +++++-- app/src/main/res/values/styles.xml | 8 ++ 5 files changed, 127 insertions(+), 19 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f5dcee567..23c89d394 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,16 +77,21 @@ dependencies { // --- THIRD PARTY --- + // ExoPlayer + def exoplayer_version = "2.12.1" + implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" + implementation "com.google.android.exoplayer:extension-mediasession:$exoplayer_version" + // Image loading implementation 'io.coil-kt:coil:0.13.0' // Material implementation 'com.google.android.material:material:1.3.0-alpha03' - // ExoPlayer - def exoplayer_version = "2.12.1" - implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" - implementation "com.google.android.exoplayer:extension-mediasession:$exoplayer_version" + // Fast-Scroll [Too lazy to make it myself] + implementation 'com.reddit:indicator-fast-scroll:1.3.0' + + // --- DEV --- // Lint ktlint "com.pinterest:ktlint:0.37.2" diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongAdapter.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongAdapter.kt index f19663ca5..b26c563a8 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongAdapter.kt @@ -7,7 +7,7 @@ import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.recycler.viewholders.SongViewHolder class SongAdapter( - private val data: List, + val data: List, private val doOnClick: (data: Song) -> Unit, private val doOnLongClick: (data: Song, view: View) -> Unit ) : RecyclerView.Adapter() { 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 4a74eabb1..0a3d6cdff 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -1,5 +1,6 @@ package org.oxycblt.auxio.songs +import android.content.res.ColorStateList import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -8,12 +9,17 @@ import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.LinearLayoutManager +import com.reddit.indicatorfastscroll.FastScrollItemIndicator +import com.reddit.indicatorfastscroll.FastScrollerView import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentSongsBinding import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.state.PlaybackMode +import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.setupSongActions +import org.oxycblt.auxio.ui.toColor /** * A [Fragment] that shows a list of all songs on the device. Contains options to search/shuffle @@ -32,8 +38,17 @@ class SongsFragment : Fragment() { val musicStore = MusicStore.getInstance() + val songAdapter = SongAdapter( + musicStore.songs, + doOnClick = { playbackModel.playSong(it, PlaybackMode.ALL_SONGS) }, + doOnLongClick = { data, view -> + PopupMenu(requireContext(), view).setupSongActions( + data, requireContext(), playbackModel + ) + } + ) + // TODO: Add option to search songs [Or just make a dedicated tab] - // TODO: Fast scrolling? // --- UI SETUP --- @@ -45,16 +60,75 @@ class SongsFragment : Fragment() { } binding.songRecycler.apply { - adapter = SongAdapter( - musicStore.songs, - doOnClick = { playbackModel.playSong(it, PlaybackMode.ALL_SONGS) }, - doOnLongClick = { data, view -> - PopupMenu(requireContext(), view).setupSongActions( - data, requireContext(), playbackModel + adapter = songAdapter + setHasFixedSize(true) + } + + binding.songFastScroll.apply { + var hasAddedNumber = false + var iters = 0 + + setupWithRecyclerView( + binding.songRecycler, + { pos -> + val item = musicStore.songs[pos] + iters++ + + var char = item.name[0].toUpperCase() + + // If the item starts with "the"/"a", then actually use the character after that + // as its initial. Yes, this is stupidly anglo-centric but the code [hopefully] + // shouldn't run with other languages. + if (item.name.length > 5 && + item.name.startsWith("the ", ignoreCase = true) + ) { + char = item.name[4].toUpperCase() + } else if (item.name.length > 3 && + item.name.startsWith("a ", ignoreCase = true) + ) { + char = item.name[2].toUpperCase() + } + + // Check if this song starts with a number, if so, then concat it with a single + // "Numeric" item if haven't already. + // This check only occurs on the second time the fast scroller is polled for items. + if (iters >= musicStore.songs.size) { + if (char.isDigit()) { + if (!hasAddedNumber) { + hasAddedNumber = true + } else { + return@setupWithRecyclerView null + } + } + } + + FastScrollItemIndicator.Text( + char.toString() ) } ) - setHasFixedSize(true) + + textAppearanceRes = R.style.TextAppearance_FastScroll + textColor = ColorStateList.valueOf(accent.first.toColor(requireContext())) + useDefaultScroller = false + + itemIndicatorSelectedCallbacks.add( + object : FastScrollerView.ItemIndicatorSelectedCallback { + override fun onItemIndicatorSelected( + indicator: FastScrollItemIndicator, + indicatorCenterY: Int, + itemPosition: Int + ) { + val layoutManager = binding.songRecycler.layoutManager + as LinearLayoutManager + + layoutManager.scrollToPositionWithOffset(itemPosition, 0) + } + } + ) + + binding.songFastScrollThumb.setupWithFastScroller(this) + binding.songFastScrollThumb.textAppearanceRes = R.style.TextAppearance_ThumbIndicator } Log.d(this::class.simpleName, "Fragment created.") diff --git a/app/src/main/res/layout/fragment_songs.xml b/app/src/main/res/layout/fragment_songs.xml index 4d42dedca..b8d3a257e 100644 --- a/app/src/main/res/layout/fragment_songs.xml +++ b/app/src/main/res/layout/fragment_songs.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" tools:context=".songs.SongsFragment"> - + app:title="@string/label_all_songs" + app:titleTextAppearance="@style/TextAppearance.Toolbar.Header" /> - + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index c80002eae..0f3f78348 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -47,6 +47,14 @@ @color/background + + + +