Roll custom fast scroller
Drop the old IndicatorFastScroll library from Auxio and replace it with a hyper-specialized variant designed specifically for Auxio. This not only eliminates a source of hacks/problems/bloat, it also removes a dependency on jcenter (Which is shutting down soon)
This commit is contained in:
parent
816dc2394a
commit
02fed16c31
11 changed files with 376 additions and 229 deletions
|
@ -97,9 +97,6 @@ dependencies {
|
|||
// Material
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
|
||||
// Fast-Scroll
|
||||
implementation 'com.reddit:indicator-fast-scroll:1.3.0'
|
||||
|
||||
// Dialogs
|
||||
implementation 'com.afollestad.material-dialogs:core:3.3.0'
|
||||
implementation 'com.afollestad.material-dialogs:files:3.3.0'
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
package org.oxycblt.auxio.songs
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation
|
||||
import androidx.dynamicanimation.animation.SpringAnimation
|
||||
import androidx.dynamicanimation.animation.SpringForce
|
||||
import com.reddit.indicatorfastscroll.FastScrollItemIndicator
|
||||
import com.reddit.indicatorfastscroll.FastScrollerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.addIndicatorCallback
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
|
||||
/**
|
||||
* A slimmed-down variant of [com.reddit.indicatorfastscroll.FastScrollerThumbView] designed
|
||||
* specifically for Auxio. Also fixes a memory leak that occurs from a bug fix they added.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class CobaltScrollThumb @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = -1
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
private val thumbView: ViewGroup
|
||||
private val textView: TextView
|
||||
private val thumbAnim: SpringAnimation
|
||||
|
||||
init {
|
||||
context.inflater.inflate(R.layout.fast_scroller_thumb_view, this, true)
|
||||
|
||||
val accent = Accent.get().getStateList(context)
|
||||
|
||||
thumbView = findViewById<ViewGroup>(R.id.fast_scroller_thumb).apply {
|
||||
textView = findViewById(R.id.fast_scroller_thumb_text)
|
||||
|
||||
backgroundTintList = accent
|
||||
|
||||
// Workaround for API 21 tint bug
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
|
||||
(background as GradientDrawable).apply {
|
||||
mutate()
|
||||
color = accent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textView.apply {
|
||||
isVisible = true
|
||||
TextViewCompat.setTextAppearance(this, R.style.TextAppearance_ThumbIndicator)
|
||||
}
|
||||
|
||||
thumbAnim = SpringAnimation(thumbView, DynamicAnimation.TRANSLATION_Y).apply {
|
||||
spring = SpringForce().also {
|
||||
it.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
|
||||
}
|
||||
}
|
||||
|
||||
visibility = View.INVISIBLE
|
||||
isActivated = false
|
||||
|
||||
post {
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up this view with a [FastScrollerView]. Should only be called once.
|
||||
*/
|
||||
fun setup(scrollView: FastScrollerView) {
|
||||
scrollView.addIndicatorCallback { indicator, centerY, _ ->
|
||||
thumbAnim.animateToFinalPosition(centerY.toFloat() - (thumbView.measuredHeight / 2))
|
||||
|
||||
if (indicator is FastScrollItemIndicator.Text) {
|
||||
textView.text = indicator.text
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ClickableViewAccessibility")
|
||||
scrollView.setOnTouchListener { _, event ->
|
||||
scrollView.onTouchEvent(event)
|
||||
scrollView.performClick()
|
||||
|
||||
val action = event.actionMasked
|
||||
val actionValid = action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL
|
||||
|
||||
isActivated = if (actionValid) {
|
||||
isPointerOnItem(scrollView, event.y.toInt())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hack that determines whether the pointer is currently on the [scrollView] or not.
|
||||
*/
|
||||
private fun isPointerOnItem(scrollView: FastScrollerView, touchY: Int): Boolean {
|
||||
scrollView.children.forEach { child ->
|
||||
if (touchY in (child.top until child.bottom)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
81
app/src/main/java/org/oxycblt/auxio/songs/FastScrollThumb.kt
Normal file
81
app/src/main/java/org/oxycblt/auxio/songs/FastScrollThumb.kt
Normal file
|
@ -0,0 +1,81 @@
|
|||
package org.oxycblt.auxio.songs
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation
|
||||
import androidx.dynamicanimation.animation.SpringAnimation
|
||||
import androidx.dynamicanimation.animation.SpringForce
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.ViewScrollThumbBinding
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
|
||||
/**
|
||||
* The companion thumb for [FastScrollView]. This does not need any setup, instead pass it as an
|
||||
* argument to [FastScrollView.setup].
|
||||
* This code is fundamentally an adaptation of Reddit's IndicatorFastScroll, targeted towards
|
||||
* Auxio specifically. Check them out here: https://github.com/reddit/IndicatorFastScroll/
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class FastScrollThumb @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = -1
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
private val thumbAnim: SpringAnimation
|
||||
private val binding = DataBindingUtil.inflate<ViewScrollThumbBinding>(
|
||||
context.inflater, R.layout.view_scroll_thumb, this, true
|
||||
)
|
||||
|
||||
init {
|
||||
val accent = Accent.get().getStateList(context)
|
||||
|
||||
binding.thumbLayout.apply {
|
||||
backgroundTintList = accent
|
||||
|
||||
// Workaround for API 21 tint bug
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
|
||||
(background as GradientDrawable).apply {
|
||||
mutate()
|
||||
color = accent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.thumbText.apply {
|
||||
isVisible = true
|
||||
TextViewCompat.setTextAppearance(this, R.style.TextAppearance_ThumbIndicator)
|
||||
}
|
||||
|
||||
thumbAnim = SpringAnimation(binding.thumbLayout, DynamicAnimation.TRANSLATION_Y).apply {
|
||||
spring = SpringForce().also {
|
||||
it.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
|
||||
}
|
||||
}
|
||||
|
||||
visibility = View.INVISIBLE
|
||||
isActivated = false
|
||||
|
||||
post {
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the thumb jump to a new position and update its text to the given [indicator].
|
||||
* This is not meant for use outside of the main [FastScrollView] code, don't use it.
|
||||
*/
|
||||
fun jumpTo(indicator: FastScrollView.Indicator, centerY: Int) {
|
||||
binding.thumbText.text = indicator.char.toString()
|
||||
thumbAnim.animateToFinalPosition(
|
||||
centerY.toFloat() - (binding.thumbLayout.measuredHeight / 2)
|
||||
)
|
||||
}
|
||||
}
|
217
app/src/main/java/org/oxycblt/auxio/songs/FastScrollView.kt
Normal file
217
app/src/main/java/org/oxycblt/auxio/songs/FastScrollView.kt
Normal file
|
@ -0,0 +1,217 @@
|
|||
package org.oxycblt.auxio.songs
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.MotionEvent
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.resolveAttr
|
||||
import org.oxycblt.auxio.ui.toColor
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* A view that allows for quick scrolling through a [RecyclerView] with many items. Unlike other
|
||||
* fast-scrollers, this one displays indicators instead of simply a scroll bar.
|
||||
* This code is fundamentally an adaptation of Reddit's IndicatorFastScroll, targeted towards
|
||||
* Auxio specifically. Check them out here: https://github.com/reddit/IndicatorFastScroll/
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class FastScrollView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = -1
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
// --- BASIC SETUP ---
|
||||
|
||||
private var mRecycler: RecyclerView? = null
|
||||
private var mThumb: FastScrollThumb? = null
|
||||
private var mGetItem: ((Int) -> Char)? = null
|
||||
|
||||
// --- INDICATORS ---
|
||||
|
||||
/** Representation of a single Indicator character in the view */
|
||||
data class Indicator(val char: Char, val pos: Int)
|
||||
|
||||
private var indicators = listOf<Indicator>()
|
||||
|
||||
private val indicatorText: TextView
|
||||
private val activeColor = Accent.get().color.toColor(context)
|
||||
private val inactiveColor = android.R.attr.textColorSecondary.resolveAttr(context)
|
||||
|
||||
// --- STATE ---
|
||||
|
||||
private var hasPostedItemUpdate = false
|
||||
private var lastPos = -1
|
||||
|
||||
init {
|
||||
isFocusableInTouchMode = true
|
||||
isClickable = true
|
||||
gravity = Gravity.CENTER
|
||||
|
||||
val textPadding = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, 4F, resources.displayMetrics
|
||||
)
|
||||
|
||||
// Making this entire view a TextView will cause distortions due to the touch calculations
|
||||
// using a height that is not wrapped to the text.
|
||||
indicatorText = AppCompatTextView(context).apply {
|
||||
gravity = Gravity.CENTER
|
||||
includeFontPadding = false
|
||||
|
||||
TextViewCompat.setTextAppearance(this, R.style.TextAppearance_FastScroll)
|
||||
setLineSpacing(textPadding, lineSpacingMultiplier)
|
||||
setTextColor(inactiveColor)
|
||||
}
|
||||
|
||||
addView(indicatorText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up this view with a [RecyclerView] and a corresponding [FastScrollThumb].
|
||||
*/
|
||||
fun setup(recycler: RecyclerView, thumb: FastScrollThumb, getItem: (Int) -> Char) {
|
||||
check(mRecycler == null) { "Only set up this view once." }
|
||||
|
||||
mRecycler = recycler
|
||||
mThumb = thumb
|
||||
mGetItem = getItem
|
||||
|
||||
postIndicatorUpdate()
|
||||
}
|
||||
|
||||
// --- INDICATOR UPDATES ---
|
||||
|
||||
private fun postIndicatorUpdate() {
|
||||
if (!hasPostedItemUpdate) {
|
||||
hasPostedItemUpdate = true
|
||||
|
||||
post {
|
||||
val recycler = requireNotNull(mRecycler)
|
||||
|
||||
if (recycler.isAttachedToWindow && recycler.adapter != null) {
|
||||
updateIndicators()
|
||||
}
|
||||
|
||||
hasPostedItemUpdate = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateIndicators() {
|
||||
val recycler = requireNotNull(mRecycler)
|
||||
val getItem = requireNotNull(mGetItem)
|
||||
|
||||
indicators = 0.until(recycler.adapter!!.itemCount).mapNotNull { pos ->
|
||||
Indicator(getItem(pos), pos)
|
||||
}.distinctBy { it.char }
|
||||
|
||||
val textHeight = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_SP, 14F, resources.displayMetrics
|
||||
)
|
||||
|
||||
// If the scroller size is too small to contain all the entries, truncate entries
|
||||
// so that the fast scroller entries fit.
|
||||
val maxEntries = height / textHeight
|
||||
|
||||
if (indicators.size > maxEntries.toInt()) {
|
||||
val truncateInterval = ceil(indicators.size / maxEntries).toInt()
|
||||
|
||||
check(truncateInterval > 1) {
|
||||
"Needed to truncate, but truncateInterval was 1 or lower anyway"
|
||||
}
|
||||
|
||||
logD("More entries than screen space, truncating by $truncateInterval.")
|
||||
|
||||
indicators = indicators.filterIndexed { index, _ ->
|
||||
index % truncateInterval == 0
|
||||
}
|
||||
}
|
||||
|
||||
indicatorText.apply {
|
||||
tag = indicators
|
||||
text = indicators.joinToString("\n") { it.char.toString() }
|
||||
}
|
||||
}
|
||||
|
||||
// --- TOUCH ---
|
||||
|
||||
@Suppress("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
performClick()
|
||||
|
||||
val success = handleTouch(event.action, event.y.toInt())
|
||||
|
||||
// Depending on the results, update the visibility of the thumb and the pressed state of
|
||||
// this view.
|
||||
isPressed = success
|
||||
mThumb?.isActivated = success
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun handleTouch(action: Int, touchY: Int): Boolean {
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||
indicatorText.setTextColor(inactiveColor)
|
||||
lastPos = -1
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if (touchY in (indicatorText.top until indicatorText.bottom)) {
|
||||
val textHeight = indicatorText.height / indicators.size
|
||||
val indicatorIndex = min(
|
||||
(touchY - indicatorText.top) / textHeight, indicators.lastIndex
|
||||
)
|
||||
|
||||
val centerY = y.toInt() + (textHeight / 2) + (indicatorIndex * textHeight)
|
||||
|
||||
val touchedIndicator = indicators[indicatorIndex]
|
||||
|
||||
selectIndicator(touchedIndicator, centerY)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun selectIndicator(indicator: Indicator, indicatorCenterY: Int) {
|
||||
if (indicator.pos != lastPos) {
|
||||
lastPos = indicator.pos
|
||||
indicatorText.setTextColor(activeColor)
|
||||
|
||||
// Stop any scroll momentum and snap-scroll to the position
|
||||
mRecycler?.apply {
|
||||
stopScroll()
|
||||
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
|
||||
indicator.pos, 0
|
||||
)
|
||||
}
|
||||
|
||||
mThumb?.jumpTo(indicator, indicatorCenterY)
|
||||
|
||||
performHapticFeedback(
|
||||
if (Build.VERSION.SDK_INT >= 27) {
|
||||
// Dragging across a scroller is closer to moving a text handle
|
||||
HapticFeedbackConstants.TEXT_HANDLE_MOVE
|
||||
} else {
|
||||
HapticFeedbackConstants.KEYBOARD_TAP
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +1,21 @@
|
|||
package org.oxycblt.auxio.songs
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
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.logD
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.addIndicatorCallback
|
||||
import org.oxycblt.auxio.ui.canScroll
|
||||
import org.oxycblt.auxio.ui.fixAnimInfoLeak
|
||||
import org.oxycblt.auxio.ui.getSpans
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* A [Fragment] that shows a list of all songs on the device.
|
||||
|
@ -73,75 +65,21 @@ class SongsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
binding.songFastScroll.setup(binding.songRecycler, binding.songFastScrollThumb)
|
||||
binding.songFastScroll.setup(binding.songRecycler, binding.songFastScrollThumb) { pos ->
|
||||
val char = musicStore.songs[pos].name.first
|
||||
|
||||
if (char.isDigit()) '#' else char
|
||||
}
|
||||
|
||||
logD("Fragment created.")
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the (Frustratingly Long and Complicated) FastScrollerView setup.
|
||||
* TODO: Roll FastScrollerView yourself and eliminate its dependency, you're already customizing it enough as it is.
|
||||
*/
|
||||
private fun FastScrollerView.setup(recycler: RecyclerView, thumb: CobaltScrollThumb) {
|
||||
var truncateInterval: Int = -1
|
||||
val indicatorTextSize = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_SP, 14F,
|
||||
resources.displayMetrics
|
||||
)
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
|
||||
// API 22 and below don't support the state color, so just use the accent.
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
|
||||
textColor = Accent.get().getStateList(requireContext())
|
||||
}
|
||||
|
||||
setupWithRecyclerView(
|
||||
recycler,
|
||||
{ pos ->
|
||||
val char = musicStore.songs[pos].name.first
|
||||
|
||||
FastScrollItemIndicator.Text(
|
||||
// Use "#" if the character is a digit, also has the nice side-effect of
|
||||
// truncating extra numbers.
|
||||
if (char.isDigit()) "#" else char.toString()
|
||||
)
|
||||
},
|
||||
null, false
|
||||
)
|
||||
|
||||
showIndicator = { _, i, total ->
|
||||
if (truncateInterval == -1) {
|
||||
// If the scroller 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()) {
|
||||
truncateInterval = ceil(total / maxEntries).toInt()
|
||||
|
||||
check(truncateInterval > 1) {
|
||||
"Needed to truncate, but truncateInterval was 1 or lower anyway"
|
||||
}
|
||||
|
||||
logD("More entries than screen space, truncating by $truncateInterval.")
|
||||
} else {
|
||||
truncateInterval = 1
|
||||
}
|
||||
}
|
||||
|
||||
// Any items that need to be truncated will be hidden
|
||||
(i % truncateInterval) == 0
|
||||
}
|
||||
|
||||
addIndicatorCallback { _, _, pos ->
|
||||
recycler.apply {
|
||||
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(pos, 0)
|
||||
|
||||
stopScroll()
|
||||
}
|
||||
}
|
||||
|
||||
thumb.setup(this)
|
||||
fixAnimInfoLeak()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.graphics.drawable.AnimatedVectorDrawable
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
|
@ -23,8 +24,6 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.reddit.indicatorfastscroll.FastScrollItemIndicator
|
||||
import com.reddit.indicatorfastscroll.FastScrollerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.logE
|
||||
import kotlin.reflect.KClass
|
||||
|
@ -129,6 +128,24 @@ fun Int.toDrawable(context: Context) = ContextCompat.getDrawable(context, this)
|
|||
*/
|
||||
fun Int.toAnimDrawable(context: Context) = toDrawable(context) as AnimatedVectorDrawable
|
||||
|
||||
/**
|
||||
* Resolve this int into a color as if it was an attribute
|
||||
*/
|
||||
fun Int.resolveAttr(context: Context): Int {
|
||||
// Convert the attribute into its color
|
||||
val resolvedAttr = TypedValue()
|
||||
context.theme.resolveAttribute(this, resolvedAttr, true)
|
||||
|
||||
// Then convert it to a proper color
|
||||
val color = if (resolvedAttr.resourceId != 0) {
|
||||
resolvedAttr.resourceId
|
||||
} else {
|
||||
resolvedAttr.data
|
||||
}
|
||||
|
||||
return color.toColor(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a [Toast] from a [String]
|
||||
* @param context [Context] required to create the toast
|
||||
|
@ -137,22 +154,6 @@ fun String.createToast(context: Context) {
|
|||
Toast.makeText(context.applicationContext, this, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut that allows me to add a indicator callback to [FastScrollerView] without
|
||||
* the nightmarish boilerplate that entails.
|
||||
*/
|
||||
fun FastScrollerView.addIndicatorCallback(
|
||||
callback: (indicator: FastScrollItemIndicator, centerY: Int, pos: Int) -> Unit
|
||||
) {
|
||||
itemIndicatorSelectedCallbacks += object : FastScrollerView.ItemIndicatorSelectedCallback {
|
||||
override fun onItemIndicatorSelected(
|
||||
indicator: FastScrollItemIndicator,
|
||||
indicatorCenterY: Int,
|
||||
itemPosition: Int
|
||||
) = callback(indicator, indicatorCenterY, itemPosition)
|
||||
}
|
||||
}
|
||||
|
||||
// --- CONFIGURATION ---
|
||||
|
||||
/**
|
||||
|
@ -253,8 +254,8 @@ private fun isSystemBarOnBottom(activity: Activity): Boolean {
|
|||
// --- HACKY NIGHTMARES ---
|
||||
|
||||
/**
|
||||
* Use reflection to fix a memory leak in the [Fragment] source code where the focused view will
|
||||
* never be cleared. I can't believe I have to do this.
|
||||
* Use ***REFLECTION*** to fix a memory leak in the [Fragment] source code where the focused view
|
||||
* will never be cleared. I can't believe I have to do this.
|
||||
*/
|
||||
fun Fragment.fixAnimInfoLeak() {
|
||||
try {
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?attr/colorPrimary" android:state_pressed="true" />
|
||||
<item android:color="?android:attr/textColorSecondary" />
|
||||
</selector>
|
|
@ -31,7 +31,7 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/song_toolbar"
|
||||
tools:listitem="@layout/item_song" />
|
||||
|
||||
<com.reddit.indicatorfastscroll.FastScrollerView
|
||||
<org.oxycblt.auxio.songs.FastScrollView
|
||||
android:id="@+id/song_fast_scroll"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
|
@ -39,7 +39,7 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/song_toolbar" />
|
||||
|
||||
<org.oxycblt.auxio.songs.CobaltScrollThumb
|
||||
<org.oxycblt.auxio.songs.FastScrollThumb
|
||||
android:id="@+id/song_fast_scroll_thumb"
|
||||
android:layout_width="@dimen/width_thumb_view"
|
||||
android:layout_height="0dp"
|
||||
|
|
30
app/src/main/res/layout/view_scroll_thumb.xml
Normal file
30
app/src/main/res/layout/view_scroll_thumb.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:layout_height="match_parent"
|
||||
tools:layout_width="@dimen/fast_scroller_thumb_size"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/thumb_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@drawable/thumb_circle"
|
||||
android:elevation="@dimen/fast_scroller_thumb_elevation"
|
||||
android:stateListAnimator="@animator/fast_scroll_thumb"
|
||||
app:layout_constraintDimensionRatio="W,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thumb_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
tools:text="A" />
|
||||
|
||||
</FrameLayout>
|
||||
</merge>
|
||||
</layout>
|
|
@ -16,7 +16,6 @@
|
|||
<item name="popupMenuStyle">@style/Widget.CustomPopup</item>
|
||||
<item name="colorControlNormal">@color/control_color</item>
|
||||
<item name="alertDialogTheme">@style/Theme.CustomDialog</item>
|
||||
<item name="indicatorFastScrollerStyle">@style/FastScrollTheme</item>
|
||||
<item name="colorControlHighlight">@color/selection_color</item>
|
||||
<item name="colorControlActivated">?attr/colorPrimary</item>
|
||||
<item name="cornerRadius">0dp</item>
|
||||
|
@ -99,15 +98,10 @@
|
|||
<item name="android:fontFamily">@font/inter_exbold</item>
|
||||
</style>
|
||||
|
||||
<!-- Fast scroll theme -->
|
||||
<style name="FastScrollTheme" parent="Widget.IndicatorFastScroll.FastScroller">
|
||||
<item name="android:textAppearance">@style/TextAppearance.FastScroll</item>
|
||||
<item name="android:textColor">@color/color_scroll_tints</item>
|
||||
</style>
|
||||
|
||||
<!-- Fast scroll text appearance -->
|
||||
<style name="TextAppearance.FastScroll" parent="TextAppearance.AppCompat.Body2">
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:lineSpacingExtra">@dimen/padding_tiny</item>
|
||||
</style>
|
||||
|
||||
<!-- Fast scroll thumb appearance -->
|
||||
|
|
17
build.gradle
17
build.gradle
|
@ -5,8 +5,15 @@ buildscript {
|
|||
|
||||
repositories {
|
||||
google()
|
||||
jcenter() // TODO: Remove JCenter when Exoplayer migrates
|
||||
mavenCentral()
|
||||
|
||||
// TODO: Eliminate Exoplayer when it migrates to GMaven
|
||||
jcenter {
|
||||
content {
|
||||
includeGroup("org.jetbrains.trove4j")
|
||||
includeGroup("com.google.android.exoplayer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -22,8 +29,14 @@ buildscript {
|
|||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
|
||||
jcenter {
|
||||
content {
|
||||
includeGroup("org.jetbrains.trove4j")
|
||||
includeGroup("com.google.android.exoplayer")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue