mirror of
				https://github.com/SimpleMobileTools/Simple-Clock.git
				synced 2025-06-05 22:19:17 +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:
		| @@ -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) { | ||||||
|  |             activity?.run { | ||||||
|                 updateDisplayedText(totalTime, lapTime, useLongerMSFormat) |                 updateDisplayedText(totalTime, lapTime, useLongerMSFormat) | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         override fun onStateChanged(state: Stopwatch.State) { |         override fun onStateChanged(state: Stopwatch.State) { | ||||||
|  |             activity?.run { | ||||||
|                 updateIcons(state) |                 updateIcons(state) | ||||||
|                 binding.stopwatchLap.beVisibleIf(state == Stopwatch.State.RUNNING) |                 binding.stopwatchLap.beVisibleIf(state == Stopwatch.State.RUNNING) | ||||||
|                 binding.stopwatchReset.beVisibleIf(state != Stopwatch.State.STOPPED) |                 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,7 +97,9 @@ object Stopwatch { | |||||||
|         updateListeners.remove(updateListener) |         updateListeners.remove(updateListener) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private val updateRunnable = object : Runnable { |     private fun buildUpdateTimer(): Timer { | ||||||
|  |         return Timer().apply { | ||||||
|  |             scheduleAtFixedRate(object : TimerTask() { | ||||||
|                 override fun run() { |                 override fun run() { | ||||||
|                     if (state == State.RUNNING) { |                     if (state == State.RUNNING) { | ||||||
|                         if (totalTicks % 10 == 0) { |                         if (totalTicks % 10 == 0) { | ||||||
| @@ -112,9 +114,10 @@ object Stopwatch { | |||||||
|                         totalTicks++ |                         totalTicks++ | ||||||
|                         currentTicks++ |                         currentTicks++ | ||||||
|                         lapTicks++ |                         lapTicks++ | ||||||
|                 updateHandler.postAtTime(this, uptimeAtStart + currentTicks * UPDATE_INTERVAL) |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |             }, 0, UPDATE_INTERVAL) | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     enum class State { |     enum class State { | ||||||
|   | |||||||
| @@ -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() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user