Skip to content

Instantly share code, notes, and snippets.

@mahendranv
Created June 18, 2021 14:13
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mahendranv/16c4354be67215f0e24c78da9882eef1 to your computer and use it in GitHub Desktop.
Save mahendranv/16c4354be67215f0e24c78da9882eef1 to your computer and use it in GitHub Desktop.
A timer that race to a given time and declutters activity.
import android.os.CountDownTimer
import androidx.lifecycle.*
/**
* Any lifecycle owner that need a timer should implement this.
*/
interface TimerCallback : LifecycleOwner {
fun onTick(millisUntilFinished: Long)
fun onTimeOut()
}
/**
* Countdown timer that is aware of lifecycle of activity/fragment. It avoids need to implement
* lifecycle methods on the caller end and enforce to implement `onTick` and `onTimeOut`. Also delivers
* the timeout when activity/fragment resumed if the time is past.
*
* Caller should make sure to keep reference to the timer and start & discard to make sure duplicate
* timers not running in parallel. Once the timer finish it will off-hook itself.
*
*
* Usage:
* ```kotlin
* val timer: LifecycleAwareTimer? = null
*
* fun startMyTimer()
* {
* val duration = 10*1000 // 10 sec timer
* timer?.discardTimer() // discard old timer to avoid
* timer = LifecycleAwareTimer(duration = duration, interval = 1000, this@SomeActivity/Fragment)
* timer?.startTimer()
* }
* ```
*/
class LifecycleAwareTimer(
private val duration: Long,
private val interval: Long,
private val callback: TimerCallback
) : LifecycleObserver {
private val stopAt: Long = System.currentTimeMillis() + duration
private var timer: CountDownTimer? = null
private val expired: Boolean
get() = (stopAt - System.currentTimeMillis()) <= 0
init {
callback.lifecycle.addObserver(this)
}
/**
* Create and start a CountDownTimer if needed. Also discards the previous timer (since timer
* cannot be resumed and always start at the initial eta).
*/
fun startTimer() {
timer?.cancel()
timer = null
val eta = stopAt - System.currentTimeMillis()
timer = object : CountDownTimer(
eta, interval) {
override fun onTick(millisUntilFinished: Long) {
callback.onTick(millisUntilFinished)
}
override fun onFinish() {
callback.onTimeOut()
callback.lifecycle.removeObserver(this@LifecycleAwareTimer)
}
}
timer?.start()
}
/**
* Cancels the timer and off-hook from lifecycle callbacks
*/
fun discardTimer() {
timer?.cancel()
callback.lifecycle.removeObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
if (expired) {
callback.onTimeOut()
discardTimer()
} else {
startTimer()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
timer?.cancel()
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
discardTimer()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment