0

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.

Arsala Bangash
  • 439
  • 4
  • 11

1 Answers1

0

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.

ephemient
  • 198,619
  • 38
  • 280
  • 391
  • I used the completeable subject and it worked perfectly. There's so much to learn about Reactive Extensions! 1. Would using RX for the countdown timer theoretically increase the performance? 2. I'm having a hard time understanding how the RxTimer would be completed. – Arsala Bangash Jul 30 '17 at 23:03
  • Oh nvm " val timeNanos = timeUnit.toNanos(time).also { require(it >= 0) }" " val tickNanos = tickUnit.toNanos(tick).also { require(it > 0) } " Must be doing the trick – Arsala Bangash Jul 30 '17 at 23:08
  • @ArsalaBangash In this case there is no direct benefit to using an Rx-based timer, but it is more flexible and may be useful if you are hooking it up to other things. – ephemient Jul 31 '17 at 00:12