Skip to content

Commit c8bd26f

Browse files
committed
Improve the timer accuracy. #128
1 parent a0cbeac commit c8bd26f

File tree

9 files changed

+72
-55
lines changed

9 files changed

+72
-55
lines changed

app/dependencies/dogReleaseRuntimeClasspath.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ com.github.DeweyReed:android-job:a04c817792
157157
com.github.DeweyReed:tools:0.8.0
158158
com.github.PhilJay:MPAndroidChart:v3.1.0
159159
com.github.bumptech.glide:disklrucache:4.16.0
160+
com.github.cardinalby:accurate-count-down-timer:1.0
160161
com.github.kizitonwose:CalendarView:1.0.4
161162
com.github.ultimate-deej:twowaynestedscrollview:0.1.2
162163
com.github.zawadz88:MaterialPopupMenu:4.1.0

app/dependencies/googleReleaseRuntimeClasspath.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ com.github.DeweyReed:android-job:a04c817792
167167
com.github.DeweyReed:tools:0.8.0
168168
com.github.PhilJay:MPAndroidChart:v3.1.0
169169
com.github.bumptech.glide:disklrucache:4.16.0
170+
com.github.cardinalby:accurate-count-down-timer:1.0
170171
com.github.kizitonwose:CalendarView:1.0.4
171172
com.github.ultimate-deej:twowaynestedscrollview:0.1.2
172173
com.github.zawadz88:MaterialPopupMenu:4.1.0

app/dependencies/otherReleaseRuntimeClasspath.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ com.github.DeweyReed:android-job:a04c817792
157157
com.github.DeweyReed:tools:0.8.0
158158
com.github.PhilJay:MPAndroidChart:v3.1.0
159159
com.github.bumptech.glide:disklrucache:4.16.0
160+
com.github.cardinalby:accurate-count-down-timer:1.0
160161
com.github.kizitonwose:CalendarView:1.0.4
161162
com.github.ultimate-deej:twowaynestedscrollview:0.1.2
162163
com.github.zawadz88:MaterialPopupMenu:4.1.0

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ coil-compose = "io.coil-kt:coil-compose:2.6.0"
153153
coil-gif = "io.coil-kt:coil-gif:2.6.0"
154154
zoomable = "net.engawapg.lib:zoomable:1.6.1"
155155
twoWayNestedScrollView = "com.github.ultimate-deej:twowaynestedscrollview:0.1.2"
156+
accurateCountDownTimer = "com.github.cardinalby:accurate-count-down-timer:1.0"
156157

157158
[bundles]
158159
mockito-core = ["mockito-kotlin", "mockito-core"]

presentation/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ dependencies {
3232
implementation libs.hilt.android
3333
kapt libs.hilt.compiler
3434

35+
implementation libs.accurateCountDownTimer
3536
}

presentation/src/main/java/xyz/aprildown/timer/presentation/stream/task/CountDownTimerTask.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package xyz.aprildown.timer.presentation.stream.task
22

3-
import android.os.CountDownTimer
3+
import com.github.cardinalby.accuratecountdowntimer.AccurateCountDownTimer
44
import com.github.deweyreed.tools.helper.HandlerHelper
55
import xyz.aprildown.timer.presentation.stream.StreamState
66

@@ -50,17 +50,17 @@ internal class CountDownTimerTask(master: TaskMaster, countDownTime: Long) : Tas
5050

5151
private fun onTick(millisUntilFinished: Long) {
5252
millisLeft = millisUntilFinished
53-
master.onTick(this, millisUntilFinished)
54-
tickListeners.forEach { it.onNewTime(millisUntilFinished) }
53+
master.onTick(this, currentTime)
54+
tickListeners.forEach { it.onNewTime(currentTime) }
5555
}
5656

57-
private inner class MyTimer(countDownTime: Long) : CountDownTimer(countDownTime, 1_000L) {
58-
59-
private var remainingTime = countDownTime
57+
private inner class MyTimer(
58+
countDownTime: Long,
59+
) : AccurateCountDownTimer(countDownTime, 1_000L) {
6060

6161
init {
6262
HandlerHelper.runOnUiThread {
63-
this@CountDownTimerTask.onTick(remainingTime)
63+
this@CountDownTimerTask.onTick(countDownTime)
6464
}
6565
}
6666

@@ -72,8 +72,7 @@ internal class CountDownTimerTask(master: TaskMaster, countDownTime: Long) : Tas
7272

7373
override fun onTick(millisUntilFinished: Long) {
7474
HandlerHelper.runOnUiThread {
75-
this@CountDownTimerTask.onTick(remainingTime)
76-
remainingTime -= 1_000L
75+
this@CountDownTimerTask.onTick(millisUntilFinished.round())
7776
}
7877
}
7978
}
Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package xyz.aprildown.timer.presentation.stream.task
22

3-
import android.os.Handler
4-
import android.os.Looper
5-
import android.os.SystemClock
3+
import com.github.cardinalby.accuratecountdowntimer.AccurateCountDownTimer
4+
import com.github.deweyreed.tools.helper.HandlerHelper
5+
import xyz.aprildown.timer.presentation.stream.StreamState
66

77
/**
88
* You must call [TaskManager.interfere] to move on.
@@ -11,68 +11,75 @@ internal class StopwatchTask(master: TaskMaster) : Task(master) {
1111

1212
private val tickListeners = mutableListOf<TickListener>()
1313

14-
private val handler = Handler(Looper.getMainLooper())
15-
private var currentStartTime = 0L
16-
private var baseElapsedTime = 0L
17-
private var currentElapsedTime = 0L
14+
private var timer = MyTimer()
15+
private var millisPassedBase = 0L
16+
private var millisPassedCurrent = 0L
1817

19-
override val currentTime: Long get() = currentElapsedTime + baseElapsedTime
18+
override val currentTime: Long get() = millisPassedBase + millisPassedCurrent
19+
20+
fun addTickListener(listener: TickListener) {
21+
tickListeners.add(listener)
22+
}
2023

2124
override fun start() {
2225
super.start()
23-
currentStartTime = SystemClock.elapsedRealtime()
24-
currentElapsedTime = 0
25-
handler.post(TickRunnable())
26+
timer.start()
2627
}
2728

2829
override fun pause() {
2930
super.pause()
30-
handler.removeCallbacksAndMessages(null)
31-
baseElapsedTime += currentElapsedTime
32-
currentElapsedTime = 0
31+
timer.cancel()
32+
millisPassedBase += millisPassedCurrent
33+
millisPassedCurrent = 0L
34+
timer = MyTimer()
3335
}
3436

3537
override fun forceStop() {
3638
super.forceStop()
37-
handler.removeCallbacksAndMessages(null)
39+
timer.cancel()
3840
}
3941

4042
override fun adjust(amount: Long, add: Boolean) {
41-
handler.removeCallbacksAndMessages(null)
42-
baseElapsedTime += if (add) currentElapsedTime + amount else amount
43-
currentElapsedTime = 0
43+
timer.cancel()
44+
millisPassedBase = if (add) millisPassedBase + millisPassedCurrent + amount else amount
45+
millisPassedCurrent = 0L
46+
timer = MyTimer()
4447
if (taskState.isRunning) {
45-
start()
46-
} else {
47-
master.onTick(this, currentTime)
48+
timer.start()
4849
}
4950
}
5051

51-
/**
52-
* newElapsedTime pattern: 26, 1026, 2026, 3026...
53-
* So the result is 0, 1, 2, 3
54-
*/
55-
private fun onTick(newElapsedTime: Long) {
56-
currentElapsedTime = newElapsedTime
57-
val time = currentTime
58-
master.onTick(this, time)
59-
tickListeners.forEach { it.onNewTime(time) }
52+
private fun onFinish() {
53+
taskState = StreamState.RESET
54+
master.onTaskDone(this)
6055
}
6156

62-
fun addTickListener(listener: TickListener) {
63-
tickListeners.add(listener)
57+
private fun onTick(millisPassed: Long) {
58+
millisPassedCurrent = millisPassed
59+
master.onTick(this, currentTime)
60+
tickListeners.forEach { it.onNewTime(currentTime) }
6461
}
6562

66-
private inner class TickRunnable : Runnable {
67-
override fun run() {
68-
val tickStartTime = SystemClock.elapsedRealtime()
63+
private inner class MyTimer : AccurateCountDownTimer(DURATION, 1_000L) {
6964

70-
val newElapsedTime = tickStartTime - currentStartTime
71-
onTick(newElapsedTime)
65+
init {
66+
HandlerHelper.runOnUiThread {
67+
this@StopwatchTask.onTick(0L)
68+
}
69+
}
7270

73-
val tickEndTime = SystemClock.elapsedRealtime()
74-
val consume = tickEndTime - tickStartTime
75-
handler.postDelayed(this, 1_000L - (consume % 1_000L))
71+
override fun onFinish() {
72+
HandlerHelper.runOnUiThread {
73+
this@StopwatchTask.onFinish()
74+
}
75+
}
76+
77+
override fun onTick(millisUntilFinished: Long) {
78+
HandlerHelper.runOnUiThread {
79+
this@StopwatchTask.onTick((DURATION - millisUntilFinished).round())
80+
}
7681
}
7782
}
7883
}
84+
85+
private const val DURATION = Long.MAX_VALUE

presentation/src/main/java/xyz/aprildown/timer/presentation/stream/task/Task.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package xyz.aprildown.timer.presentation.stream.task
22

33
import androidx.annotation.CallSuper
44
import xyz.aprildown.timer.presentation.stream.StreamState
5+
import kotlin.math.roundToLong
56

67
internal abstract class Task(protected val master: TaskMaster) {
78

@@ -29,4 +30,8 @@ internal abstract class Task(protected val master: TaskMaster) {
2930
* @param add True to add amount to the current time. False to set current time to amount
3031
*/
3132
abstract fun adjust(amount: Long, add: Boolean)
33+
34+
protected fun Long.round(): Long {
35+
return (toDouble() / 1000f).roundToLong() * 1000L
36+
}
3237
}

settings.gradle

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ dependencyResolutionManagement {
1313
maven {
1414
url "https://jitpack.io"
1515
content {
16-
includeGroup "com.github.DeweyReed"
17-
includeModule "com.github.PhilJay", "MPAndroidChart"
18-
includeModule "com.github.kizitonwose", "CalendarView"
19-
includeModule "com.github.zawadz88", "MaterialPopupMenu"
20-
includeModule "com.github.Carbs0126", "NumberPickerView"
21-
includeModule "com.github.ultimate-deej", "twowaynestedscrollview"
16+
includeGroup("com.github.DeweyReed")
17+
includeModule("com.github.PhilJay", "MPAndroidChart")
18+
includeModule("com.github.kizitonwose", "CalendarView")
19+
includeModule("com.github.zawadz88", "MaterialPopupMenu")
20+
includeModule("com.github.Carbs0126", "NumberPickerView")
21+
includeModule("com.github.ultimate-deej", "twowaynestedscrollview")
22+
includeModule("com.github.cardinalby", "accurate-count-down-timer")
2223
}
2324
}
2425
}

0 commit comments

Comments
 (0)