[Android] Implement the new scheduler in the fast scroller
Nicolas Pomepuy
git at videolan.org
Tue Jul 25 14:57:29 UTC 2023
vlc-android | branch: master | Nicolas Pomepuy <nicolas at videolabs.io> | Thu Jul 20 09:52:50 2023 +0200| [ad9936d20eff2e8b94441f06140814e66cce4294] | committer: Duncan McNamara
Implement the new scheduler in the fast scroller
> https://code.videolan.org/videolan/vlc-android/commit/ad9936d20eff2e8b94441f06140814e66cce4294
---
.../src/org/videolan/vlc/gui/view/FastScroller.kt | 67 +++++++++++-----------
1 file changed, 35 insertions(+), 32 deletions(-)
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/view/FastScroller.kt b/application/vlc-android/src/org/videolan/vlc/gui/view/FastScroller.kt
index 995a6c2c22..b6cc04955f 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/view/FastScroller.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/view/FastScroller.kt
@@ -27,9 +27,7 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
-import android.annotation.SuppressLint
import android.content.Context
-import android.os.Message
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
@@ -40,6 +38,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.lifecycle.Observer
+import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.appbar.AppBarLayout
@@ -50,10 +49,11 @@ import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.delay
import org.videolan.resources.util.HeaderProvider
import org.videolan.resources.util.HeadersIndex
-import org.videolan.tools.WeakHandler
import org.videolan.tools.dp
import org.videolan.vlc.BuildConfig
import org.videolan.vlc.R
+import org.videolan.vlc.util.LifecycleAwareScheduler
+import org.videolan.vlc.util.SchedulerCallback
import org.videolan.vlc.util.scope
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.math.max
@@ -65,13 +65,13 @@ private const val HANDLE_ANIMATION_DURATION = 100
private const val HANDLE_HIDE_DELAY = 1000
private const val SCROLLER_HIDE_DELAY = 3000
-private const val HIDE_HANDLE = 0
-private const val HIDE_SCROLLER = 1
-private const val SHOW_SCROLLER = 2
+private const val HIDE_HANDLE = "hide_handle"
+private const val HIDE_SCROLLER = "hide_scroller"
+private const val SHOW_SCROLLER = "show_scroller"
private const val ITEM_THRESHOLD = 25
-class FastScroller : LinearLayout, Observer<HeadersIndex> {
+class FastScroller : LinearLayout, Observer<HeadersIndex>, SchedulerCallback {
private var currentHeight: Int = 0
private val itemCount: Int
@@ -100,23 +100,7 @@ class FastScroller : LinearLayout, Observer<HeadersIndex> {
private var tryExpandAppbarOnNextScroll = false
private val hiddenTranslationX = 38.dp.toFloat()
- private val handler = @SuppressLint("HandlerLeak")
- object : WeakHandler<FastScroller>(this) {
- override fun handleMessage(msg: Message) {
- when (msg.what) {
- HIDE_HANDLE -> hideBubble()
- HIDE_SCROLLER -> animate().translationX(hiddenTranslationX)
- SHOW_SCROLLER -> {
- if (itemCount < ITEM_THRESHOLD) {
- return
- }
- translationX = 0.dp.toFloat()
- this.removeMessages(HIDE_SCROLLER)
- this.sendEmptyMessageDelayed(HIDE_SCROLLER, SCROLLER_HIDE_DELAY.toLong())
- }
- }
- }
- }
+ lateinit var scheduler: LifecycleAwareScheduler
interface SeparatedAdapter {
fun hasSections(): Boolean
@@ -132,6 +116,7 @@ class FastScroller : LinearLayout, Observer<HeadersIndex> {
private fun initialize(context: Context) {
+ scheduler = LifecycleAwareScheduler(this)
orientation = HORIZONTAL
clipChildren = false
val inflater = LayoutInflater.from(context)
@@ -190,6 +175,7 @@ class FastScroller : LinearLayout, Observer<HeadersIndex> {
* Hides the bubble containing the section letter
*/
private fun hideBubble() {
+ if (BuildConfig.DEBUG) Log.d("LifecycleAwareScheduler", "hideBubble on thread ${Thread.currentThread()}")
currentAnimator = AnimatorSet()
bubble.pivotX = bubble.width.toFloat()
bubble.pivotY = bubble.height.toFloat()
@@ -202,14 +188,14 @@ class FastScroller : LinearLayout, Observer<HeadersIndex> {
super.onAnimationEnd(animation)
bubble.visibility = View.GONE
currentAnimator = null
- handler.sendEmptyMessageDelayed(HIDE_SCROLLER, SCROLLER_HIDE_DELAY.toLong())
+ scheduler.scheduleAction(HIDE_SCROLLER, SCROLLER_HIDE_DELAY.toLong())
}
override fun onAnimationCancel(animation: Animator) {
super.onAnimationCancel(animation)
bubble.visibility = View.INVISIBLE
currentAnimator = null
- handler.sendEmptyMessageDelayed(HIDE_SCROLLER, SCROLLER_HIDE_DELAY.toLong())
+ scheduler.scheduleAction(HIDE_SCROLLER, SCROLLER_HIDE_DELAY.toLong())
}
})
currentAnimator?.start()
@@ -233,7 +219,7 @@ class FastScroller : LinearLayout, Observer<HeadersIndex> {
this.recyclerView = recyclerView
this.layoutManager = recyclerView.layoutManager as LinearLayoutManager
this.recyclerView.removeOnScrollListener(scrollListener)
- handler.sendEmptyMessage(HIDE_HANDLE)
+ scheduler.startAction(HIDE_HANDLE)
if (this::provider.isInitialized) this.provider.liveHeaders.removeObserver(this)
this.provider = provider
provider.liveHeaders.observeForever(this)
@@ -252,16 +238,16 @@ class FastScroller : LinearLayout, Observer<HeadersIndex> {
currentPosition = -1
if (currentAnimator != null)
currentAnimator?.cancel()
- handler.removeMessages(HIDE_SCROLLER)
- handler.removeMessages(HIDE_HANDLE)
+ scheduler.cancelAction(HIDE_SCROLLER)
+ scheduler.cancelAction(HIDE_HANDLE)
if (showBubble && bubble.visibility == View.GONE)
showBubble()
setRecyclerViewPosition(event.y)
return true
} else if (event.action == MotionEvent.ACTION_UP) {
fastScrolling = false
- handler.sendEmptyMessageDelayed(HIDE_HANDLE, HANDLE_HIDE_DELAY.toLong())
- handler.sendEmptyMessageDelayed(HIDE_SCROLLER, SCROLLER_HIDE_DELAY.toLong())
+ scheduler.scheduleAction(HIDE_HANDLE, HANDLE_HIDE_DELAY.toLong())
+ scheduler.scheduleAction(HIDE_SCROLLER, SCROLLER_HIDE_DELAY.toLong())
if (event.y / currentHeight.toFloat() > 0.99f) {
recyclerView.smoothScrollToPosition(itemCount)
}
@@ -391,11 +377,28 @@ class FastScroller : LinearLayout, Observer<HeadersIndex> {
val recyclerviewTotalHeight = recyclerView.computeVerticalScrollRange() - recyclerView.computeVerticalScrollExtent()
val proportion = if (recyclerviewTotalHeight == 0) 0f else verticalScrollOffset / recyclerviewTotalHeight.toFloat()
setPosition(currentHeight * proportion)
- handler.sendEmptyMessage(SHOW_SCROLLER)
+ scheduler.startAction(SHOW_SCROLLER)
actor.trySend(Unit)
}
override fun onChanged(t: HeadersIndex?) {
actor.trySend(Unit)
}
+
+ override fun onTaskTriggered(id: String) {
+ when (id) {
+ HIDE_HANDLE -> hideBubble()
+ HIDE_SCROLLER -> animate().translationX(hiddenTranslationX)
+ SHOW_SCROLLER -> {
+ if (itemCount < ITEM_THRESHOLD) {
+ return
+ }
+ translationX = 0.dp.toFloat()
+ scheduler.cancelAction(HIDE_SCROLLER)
+ scheduler.scheduleAction(HIDE_SCROLLER, SCROLLER_HIDE_DELAY.toLong())
+ }
+ }
+ }
+
+ override fun getLifecycle() = findViewTreeLifecycleOwner()!!.lifecycle
}
More information about the Android
mailing list