-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Expand file tree
/
Copy pathChaptersSeekBar.kt
More file actions
89 lines (76 loc) · 3.06 KB
/
ChaptersSeekBar.kt
File metadata and controls
89 lines (76 loc) · 3.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
* SPDX-FileCopyrightText: 2026 NewPipe contributors <https://newpipe.net>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package org.schabi.newpipe.views
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.util.AttributeSet
import org.schabi.newpipe.extractor.stream.StreamSegment
/**
* A [FocusAwareSeekBar] that renders narrow transparent gaps at chapter boundaries,
* giving the seekbar a segmented "chopped" appearance.
* Call [setChapters] whenever a new stream loads.
*/
class ChaptersSeekBar : FocusAwareSeekBar {
private val gapPaint = Paint().apply {
style = Paint.Style.FILL
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
private var chapters: List<StreamSegment> = emptyList()
private var durationSeconds: Long = 0
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
super(context, attrs, defStyleAttr)
/**
* Stores chapter data for rendering segment gaps.
*
* @param newChapters list of [StreamSegment]s; may be empty but never null
* @param newDurationSecs total duration in seconds; used to compute fractional positions
*/
fun setChapters(newChapters: List<StreamSegment>, newDurationSecs: Long) {
chapters = newChapters
durationSeconds = newDurationSecs
invalidate()
}
override fun onDraw(canvas: Canvas) {
if (chapters.isEmpty() || durationSeconds <= 0) {
super.onDraw(canvas)
return
}
// Draw the seekbar into an offscreen layer so CLEAR mode can punch transparent gaps
val sc = canvas.saveLayer(null, null)
super.onDraw(canvas)
val density = resources.displayMetrics.density
val gapHalfWidth = (GAP_WIDTH_DP * density) / 2f
val left = paddingLeft
val trackWidth = (width - left - paddingRight).toFloat()
if (trackWidth > 0) {
for (seg in chapters) {
val startSec = seg.startTimeSeconds
// Skip the very first position and anything at or past the end
if (startSec <= 0 || startSec.toLong() >= durationSeconds) {
continue
}
val x = left + (startSec.toFloat() / durationSeconds.toFloat()) * trackWidth
canvas.drawRect(x - gapHalfWidth, 0f, x + gapHalfWidth, height.toFloat(), gapPaint)
}
}
canvas.restoreToCount(sc)
// Redraw the thumb on top so it visually overlaps the gaps
val t = thumb
if (t != null) {
val thumbSave = canvas.save()
canvas.translate((paddingLeft - thumbOffset).toFloat(), paddingTop.toFloat())
t.draw(canvas)
canvas.restoreToCount(thumbSave)
}
}
companion object {
private const val GAP_WIDTH_DP = 2f
}
}