@@ -4,13 +4,15 @@ import android.annotation.SuppressLint
44import android.content.Context
55import android.util.AttributeSet
66import android.view.MotionEvent
7+ import android.view.ViewConfiguration
78import androidx.media3.common.Player
89import androidx.media3.common.util.UnstableApi
910import androidx.media3.ui.DefaultTimeBar
1011import androidx.media3.ui.PlayerControlView
1112import androidx.media3.ui.TimeBar
1213import androidx.media3.ui.TimeBar.OnScrubListener
1314import com.github.libretube.extensions.dpToPx
15+ import kotlin.math.abs
1416
1517@UnstableApi
1618open class DismissableTimeBar (
@@ -19,7 +21,13 @@ open class DismissableTimeBar(
1921): DefaultTimeBar(context, attributeSet) {
2022 private var shouldAddListener = false
2123 var exoPlayer: Player ? = null
22- private var lastYPosition = 0f
24+
25+ // Drag-only seeking state
26+ private var initialX: Float = 0f
27+ private var initialY: Float = 0f
28+ private var waitingForDrag: Boolean = false
29+ private var dragStarted: Boolean = false
30+ private val touchSlopPx: Int = ViewConfiguration .get(context).scaledTouchSlop
2331
2432 init {
2533 addSeekBarListener(object : OnScrubListener {
@@ -28,14 +36,72 @@ open class DismissableTimeBar(
2836 override fun onScrubMove (timeBar : TimeBar , position : Long ) = Unit
2937
3038 override fun onScrubStop (timeBar : TimeBar , position : Long , canceled : Boolean ) {
31- if (lastYPosition > MINIMUM_ACCEPTED_HEIGHT .dpToPx()) exoPlayer?.seekTo(position)
39+ if (canceled) return
40+ // Ignore if gesture started too far above the bar (keep original behavior)
41+ if (initialY <= MINIMUM_ACCEPTED_HEIGHT .dpToPx()) return
42+ exoPlayer?.seekTo(position)
3243 }
3344 })
3445 }
3546
3647 @SuppressLint(" ClickableViewAccessibility" )
3748 override fun onTouchEvent (event : MotionEvent ): Boolean {
38- lastYPosition = event.y
49+ when (event.actionMasked) {
50+ MotionEvent .ACTION_DOWN -> {
51+ initialX = event.x
52+ initialY = event.y
53+ waitingForDrag = true
54+ dragStarted = false
55+ // Consume without forwarding to prevent tap-to-seek or thumb jump
56+ return true
57+ }
58+
59+ MotionEvent .ACTION_MOVE -> {
60+ if (waitingForDrag) {
61+ // make sure that the user dragged at least touchSlopPx pixels
62+ // which is the minimum amount to trigger a drag event
63+ val dx = abs(event.x - initialX)
64+ val dy = abs(event.y - initialY)
65+ if (dx > touchSlopPx || dy > touchSlopPx) {
66+ // Begin scrubbing now by synthesizing a DOWN at the current progress X
67+ val fakeDown = MotionEvent .obtain(event).apply {
68+ action = MotionEvent .ACTION_DOWN
69+ setLocation(event.x, event.y)
70+ }
71+ super .onTouchEvent(fakeDown)
72+ fakeDown.recycle()
73+
74+ waitingForDrag = false
75+ dragStarted = true
76+ } else {
77+ // Still not considered a drag; consume
78+ return true
79+ }
80+ }
81+
82+ // Forward MOVE only after we've started dragging, with relative X
83+ if (dragStarted) {
84+ return super .onTouchEvent(event)
85+ }
86+ return true
87+ }
88+
89+ MotionEvent .ACTION_UP , MotionEvent .ACTION_CANCEL -> {
90+ // If we started a drag, forward the terminal event to finish scrubbing
91+ if (dragStarted) {
92+ waitingForDrag = false
93+ dragStarted = false
94+ return super .onTouchEvent(event)
95+ }
96+
97+ // It's a tap without drag: do nothing (prevent seek and thumb jump)
98+ waitingForDrag = false
99+ dragStarted = false
100+ // Optionally report click for accessibility without side effects
101+ performClick()
102+ return true
103+ }
104+ }
39105
40106 return super .onTouchEvent(event)
41107 }
0 commit comments