mirror of
https://github.com/SimpleMobileTools/Simple-Clock.git
synced 2025-04-26 07:58:50 +02:00
Merge pull request #499 from fatihergin/fix/ISSUE-423-stopwatch-is-not-accurate
replace handler with timer for stopwatch tasks
This commit is contained in:
commit
1754a7233c
@ -199,13 +199,17 @@ class StopwatchFragment : Fragment() {
|
|||||||
|
|
||||||
private val updateListener = object : Stopwatch.UpdateListener {
|
private val updateListener = object : Stopwatch.UpdateListener {
|
||||||
override fun onUpdate(totalTime: Long, lapTime: Long, useLongerMSFormat: Boolean) {
|
override fun onUpdate(totalTime: Long, lapTime: Long, useLongerMSFormat: Boolean) {
|
||||||
updateDisplayedText(totalTime, lapTime, useLongerMSFormat)
|
activity?.run {
|
||||||
|
updateDisplayedText(totalTime, lapTime, useLongerMSFormat)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStateChanged(state: Stopwatch.State) {
|
override fun onStateChanged(state: Stopwatch.State) {
|
||||||
updateIcons(state)
|
activity?.run {
|
||||||
binding.stopwatchLap.beVisibleIf(state == Stopwatch.State.RUNNING)
|
updateIcons(state)
|
||||||
binding.stopwatchReset.beVisibleIf(state != Stopwatch.State.STOPPED)
|
binding.stopwatchLap.beVisibleIf(state == Stopwatch.State.RUNNING)
|
||||||
|
binding.stopwatchReset.beVisibleIf(state != Stopwatch.State.STOPPED)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package com.simplemobiletools.clock.helpers
|
package com.simplemobiletools.clock.helpers
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import com.simplemobiletools.clock.models.Lap
|
import com.simplemobiletools.clock.models.Lap
|
||||||
|
import java.util.Timer
|
||||||
|
import java.util.TimerTask
|
||||||
import java.util.concurrent.CopyOnWriteArraySet
|
import java.util.concurrent.CopyOnWriteArraySet
|
||||||
|
|
||||||
private const val UPDATE_INTERVAL = 10L
|
private const val UPDATE_INTERVAL = 10L
|
||||||
|
|
||||||
object Stopwatch {
|
object Stopwatch {
|
||||||
|
|
||||||
private val updateHandler = Handler(Looper.getMainLooper())
|
private var updateTimer = Timer()
|
||||||
private var uptimeAtStart = 0L
|
private var uptimeAtStart = 0L
|
||||||
private var totalTicks = 0
|
private var totalTicks = 0
|
||||||
private var currentTicks = 0 // ticks that reset at pause
|
private var currentTicks = 0 // ticks that reset at pause
|
||||||
@ -27,7 +27,7 @@ object Stopwatch {
|
|||||||
private var updateListeners = CopyOnWriteArraySet<UpdateListener>()
|
private var updateListeners = CopyOnWriteArraySet<UpdateListener>()
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
updateHandler.removeCallbacksAndMessages(null)
|
updateTimer.cancel()
|
||||||
state = State.STOPPED
|
state = State.STOPPED
|
||||||
currentTicks = 0
|
currentTicks = 0
|
||||||
totalTicks = 0
|
totalTicks = 0
|
||||||
@ -39,7 +39,7 @@ object Stopwatch {
|
|||||||
fun toggle(setUptimeAtStart: Boolean) {
|
fun toggle(setUptimeAtStart: Boolean) {
|
||||||
if (state != State.RUNNING) {
|
if (state != State.RUNNING) {
|
||||||
state = State.RUNNING
|
state = State.RUNNING
|
||||||
updateHandler.post(updateRunnable)
|
updateTimer = buildUpdateTimer()
|
||||||
if (setUptimeAtStart) {
|
if (setUptimeAtStart) {
|
||||||
uptimeAtStart = SystemClock.uptimeMillis()
|
uptimeAtStart = SystemClock.uptimeMillis()
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ object Stopwatch {
|
|||||||
state = State.PAUSED
|
state = State.PAUSED
|
||||||
val prevSessionsMS = (totalTicks - currentTicks) * UPDATE_INTERVAL
|
val prevSessionsMS = (totalTicks - currentTicks) * UPDATE_INTERVAL
|
||||||
val totalDuration = SystemClock.uptimeMillis() - uptimeAtStart + prevSessionsMS
|
val totalDuration = SystemClock.uptimeMillis() - uptimeAtStart + prevSessionsMS
|
||||||
updateHandler.removeCallbacksAndMessages(null)
|
updateTimer.cancel()
|
||||||
currentTicks = 0
|
currentTicks = 0
|
||||||
totalTicks--
|
totalTicks--
|
||||||
for (listener in updateListeners) {
|
for (listener in updateListeners) {
|
||||||
@ -97,23 +97,26 @@ object Stopwatch {
|
|||||||
updateListeners.remove(updateListener)
|
updateListeners.remove(updateListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val updateRunnable = object : Runnable {
|
private fun buildUpdateTimer(): Timer {
|
||||||
override fun run() {
|
return Timer().apply {
|
||||||
if (state == State.RUNNING) {
|
scheduleAtFixedRate(object : TimerTask() {
|
||||||
if (totalTicks % 10 == 0) {
|
override fun run() {
|
||||||
for (listener in updateListeners) {
|
if (state == State.RUNNING) {
|
||||||
listener.onUpdate(
|
if (totalTicks % 10 == 0) {
|
||||||
totalTicks * UPDATE_INTERVAL,
|
for (listener in updateListeners) {
|
||||||
lapTicks * UPDATE_INTERVAL,
|
listener.onUpdate(
|
||||||
false
|
totalTicks * UPDATE_INTERVAL,
|
||||||
)
|
lapTicks * UPDATE_INTERVAL,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalTicks++
|
||||||
|
currentTicks++
|
||||||
|
lapTicks++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
totalTicks++
|
}, 0, UPDATE_INTERVAL)
|
||||||
currentTicks++
|
|
||||||
lapTicks++
|
|
||||||
updateHandler.postAtTime(this, uptimeAtStart + currentTicks * UPDATE_INTERVAL)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import android.os.Handler
|
|||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.ServiceCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.simplemobiletools.clock.R
|
import com.simplemobiletools.clock.R
|
||||||
import com.simplemobiletools.clock.extensions.getFormattedDuration
|
import com.simplemobiletools.clock.extensions.getFormattedDuration
|
||||||
@ -18,7 +19,6 @@ import com.simplemobiletools.clock.helpers.Stopwatch
|
|||||||
import com.simplemobiletools.clock.helpers.Stopwatch.State
|
import com.simplemobiletools.clock.helpers.Stopwatch.State
|
||||||
import com.simplemobiletools.clock.helpers.Stopwatch.UpdateListener
|
import com.simplemobiletools.clock.helpers.Stopwatch.UpdateListener
|
||||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||||
import com.simplemobiletools.commons.helpers.isNougatPlus
|
|
||||||
import com.simplemobiletools.commons.helpers.isOreoPlus
|
import com.simplemobiletools.commons.helpers.isOreoPlus
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
@ -54,15 +54,15 @@ class StopwatchService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
|
||||||
bus.unregister(this)
|
bus.unregister(this)
|
||||||
Stopwatch.removeUpdateListener(updateListener)
|
Stopwatch.removeUpdateListener(updateListener)
|
||||||
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onMessageEvent(event: StopwatchStopService) {
|
fun onMessageEvent(event: StopwatchStopService) {
|
||||||
isStopping = true
|
isStopping = true
|
||||||
stopForeground(true)
|
stopForegroundService()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getServiceNotificationBuilder(
|
private fun getServiceNotificationBuilder(
|
||||||
@ -98,17 +98,29 @@ class StopwatchService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val updateListener = object : UpdateListener {
|
private val updateListener = object : UpdateListener {
|
||||||
|
private val MIN_NOTIFICATION_UPDATE_INTERVAL = 500L
|
||||||
|
private var lastUpdateTime = 0L
|
||||||
override fun onUpdate(totalTime: Long, lapTime: Long, useLongerMSFormat: Boolean) {
|
override fun onUpdate(totalTime: Long, lapTime: Long, useLongerMSFormat: Boolean) {
|
||||||
if (!isStopping) {
|
if (!isStopping && shouldNotificationBeUpdated()) {
|
||||||
|
lastUpdateTime = System.currentTimeMillis()
|
||||||
updateNotification(totalTime)
|
updateNotification(totalTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStateChanged(state: State) {
|
override fun onStateChanged(state: State) {
|
||||||
if (state == State.STOPPED && isNougatPlus()) {
|
if (state == State.STOPPED) {
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
stopForegroundService()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shouldNotificationBeUpdated(): Boolean {
|
||||||
|
return (System.currentTimeMillis() - lastUpdateTime) > MIN_NOTIFICATION_UPDATE_INTERVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopForegroundService() {
|
||||||
|
ServiceCompat.stopForeground(this@StopwatchService, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||||
|
stopSelf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user