Search code examples
androidkotlinrx-javarx-kotlin

How to notify Observable when CountdownTimer is finished


I have a custom Android TextView which shows the amount of time left in a game via a CountDownTimer

class CountdownTextView(context: Context, attrs: AttributeSet) : TextView(context, attrs) {

    private lateinit var countDownTimer: CountDownTimer
    private lateinit var onFinishObservable: Observable<Unit>

    fun setTime(initTime: Int) {
        this.text = "$initTime:00"
        countDownTimer = object : CountDownTimer((initTime *1000).toLong(), 1000) {
            override fun onTick(millisUntilFinished: Long) {
            val minutes = millisUntilFinished / 60000
            val seconds = (millisUntilFinished % 60000) / 1000
            if (seconds / 10 > 0) {
                text = "$minutes:${(millisUntilFinished % 60000) / 1000}"
            } else {
                text = "$minutes:0${(millisUntilFinished % 60000) / 1000}"
            }
        }

        override fun onFinish() {

        }
    }


    fun startCountdown() {
        countDownTimer.start()
    }
}

How do I set up an observable that emits a value when the countDownTimer's onFinish() method is called? I need this so that on the main activity, I can subscribe to that observable and perform the necessary actions when the countdowntimer expires.


Solution

  • You could provide a Subject.

    val onFinishObservable = CompletableSubject.create()
    
    override fun onFinish() {
        onFinishObservable.onComplete()
    }
    

    Or you could use Rx for the timer instead of CountDownTimer.

    fun countDownTimer(
            time: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS,
            tick: Long = 1, tickUnit: TimeUnit = TimeUnit.MILLISECONDS
    ): Observable<Long> {
        val timeNanos = timeUnit.toNanos(time).also { require(it >= 0) }
        val tickNanos = tickUnit.toNanos(tick).also { require(it > 0) }
        val ticks = timeNanos / tickNanos
        return Observable
            .intervalRange(
                1L, ticks, timeNanos % tickNanos, tickNanos, TimeUnit.NANOSECONDS)
            .map { ticks - it }
            .startWith(ticks)
    }
    
    fun start(time: Long, timeUnit: TimeUnit = TimeUnit.SECONDS): Completable {
        timerSubscription?.dispose()
        val timer = countDownTimer(time, timeUnit, tickUnit = TimeUnit.SECONDS)
        timerSubscription = timer.subscribe {
            text = String.format("%d:%02d", it / 60, it % 60)
        }
        return timer.ignoreElements()
    }
    

    Either way, the caller can subscribe to that Completable.