How to create a simple countdown timer in Kotlin?

AndroidKotlinCountdowntimer

Android Problem Overview


I know how to create a simple countdown timer in Java. But I'd like to create this one in Kotlin.

package android.os;

new CountDownTimer(20000, 1000) {
    public void onTick(long millisUntilFinished) {
        mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
    }
    public void onFinish() {
        mTextField.setText("Time's finished!");
    }
}.start();

How can I do it using Kotlin?

Android Solutions


Solution 1 - Android

You can use Kotlin objects:

val timer = object: CountDownTimer(20000, 1000) {
    override fun onTick(millisUntilFinished: Long) {...}

    override fun onFinish() {...}
}
timer.start()

Solution 2 - Android

I've solved my problem with timer in Kotlin like this:

class Timer {

    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.Default + job)

    private fun startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: () -> Unit) = scope.launch(Dispatchers.IO) {
        delay(delayMillis)
        if (repeatMillis > 0) {
            while (true) {
                action()
                delay(repeatMillis)
            }
        } else {
            action()
        }
    }

    private val timer: Job = startCoroutineTimer(delayMillis = 0, repeatMillis = 20000) {
        Log.d(TAG, "Background - tick")
        doSomethingBackground()
        scope.launch(Dispatchers.Main) {
            Log.d(TAG, "Main thread - tick")
            doSomethingMainThread()
        }
    }

    fun startTimer() {
        timer.start()
    }

    fun cancelTimer() {
        timer.cancel()
    }
//...
}

I've used Coroutines for a timer.

Solution 3 - Android

Chronometer can be set to count down and it seems to me the easiest way.

Add the Chronometer view in your layout xml, example

<Chronometer  
 android:id="@+id/view_timer"   
 tools:targetApi="24"  
 android:layout_width="wrap_content"  
 android:layout_height="wrap_content"/>

Then in your activity or fragment:

   view_timer.isCountDown = true
   view_timer.base = SystemClock.elapsedRealtime() + 20000
   view_timer.start()
         

Solution 4 - Android

If you want to show a countdown with days hours minutes and seconds

private lateinit var countDownTimer:CountDownTimer
.
.
.
    fun printDifferenceDateForHours() {
    
            val currentTime = Calendar.getInstance().time
            val endDateDay = "03/02/2020 21:00:00"
            val format1 = SimpleDateFormat("dd/MM/yyyy hh:mm:ss",Locale.getDefault())
            val endDate = format1.parse(endDateDay)
    
            //milliseconds
            var different = endDate.time - currentTime.time
            countDownTimer = object : CountDownTimer(different, 1000) {
    
                override fun onTick(millisUntilFinished: Long) {
                    var diff = millisUntilFinished
                    val secondsInMilli: Long = 1000
                    val minutesInMilli = secondsInMilli * 60
                    val hoursInMilli = minutesInMilli * 60
                    val daysInMilli = hoursInMilli * 24
    
                    val elapsedDays = diff / daysInMilli
                    diff %= daysInMilli
    
                    val elapsedHours = diff / hoursInMilli
                    diff %= hoursInMilli
    
                    val elapsedMinutes = diff / minutesInMilli
                    diff %= minutesInMilli
    
                    val elapsedSeconds = diff / secondsInMilli
    
                    txt_timeleft.text = "$elapsedDays days $elapsedHours hs $elapsedMinutes min $elapsedSeconds sec"
                }
    
                override fun onFinish() {
                    txt_timeleft.text = "done!"
                }
            }.start()
        }

If you are navigating to another activity/fragment, make sure to cancel the countdown

countDownTimer.cancel()

Code output

> 51 days 17 hs 56 min 5 sec

Solution 5 - Android

Try to use objects, like this :

var countDownTimer = object : CountDownTimer(2000, 1000) {
    // override object functions here, do it quicker by setting cursor on object, then type alt + enter ; implement members
}

Try this website : https://try.kotlinlang.org/#/Kotlin%20Koans/Introduction/Java%20to%20Kotlin%20conversion/Task.kt

You have a little button "Convert from Java" on the top right that could be useful to you.

EDIT:

Do not forget to start this object when you need it, by adding .start() at the end of the declaration, or wherever in your activity / fragment :

countDownTimer.start()

Solution 6 - Android

class CustomCountDownTimer(var mutableLiveData: MutableLiveData<String>) {

    lateinit var timer: CountDownTimer
    val zone = ZoneId.systemDefault()
    val startDateTime: ZonedDateTime = LocalDateTime.now().atZone(zone)

    fun start(endOn: Long) {
        if (this::timer.isInitialized) {
            return
        }
        timer = object : CountDownTimer(endOn * 1000, 1000) {

            override fun onTick(millisUntilFinished: Long) {

                val stringBuilder = StringBuilder()

                val endDateTime: ZonedDateTime =
                    Instant.ofEpochMilli(millisUntilFinished).atZone(ZoneId.systemDefault())
                        .toLocalDateTime().atZone(zone)

                var diff: Duration = Duration.between(startDateTime, endDateTime)



                if (diff.isZero() || diff.isNegative) {
                    stringBuilder.append("Already ended!")
                } else {
                    val days: Long = diff.toDays()

                    if (days != 0L) {
                        stringBuilder.append("${days}day : ")
                        diff = diff.minusDays(days)
                    }

                    val hours: Long = diff.toHours()
                    stringBuilder.append("${hours}hr : ")
                    diff = diff.minusHours(hours)

                    val minutes: Long = diff.toMinutes()
                    stringBuilder.append("${minutes}min : ")
                    diff = diff.minusMinutes(minutes)

                    val seconds: Long = diff.getSeconds()

                    stringBuilder.append("${seconds}sec")

                }

                mutableLiveData.postValue(stringBuilder.toString())
                //Log.d("CustomCountDownTimer", stringBuilder.toString())
            }

            override fun onFinish() {
            }
        }

        timer.start()
    }


    fun getTimerState(): LiveData<String> {
        return mutableLiveData
    }
}

How to use it:

val liveData: MutableLiveData<String> = MutableLiveData()
val customCountDownTimer = CustomCountDownTimer(liveData)
customCountDownTimer.start(1631638786) //Epoch timestamp
customCountDownTimer.mutableLiveData.observe(this, Observer { counterState ->
            counterState?.let {
                println(counterState)
            }
        })

Output:

22hr : 42min : 51sec //when less than 4hr are remaining

1day : 23hr : 52min : 44sec // in other cases

Solution 7 - Android

use Chronometer for minapi=24:

  <Chronometer
            android:id="@+id/timer_expire_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/spacing_m"
            android:countDown="true"
            android:textColor="@color/white"
            android:textSize="@dimen/text_size_huge"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            tools:targetApi="24" />

and in kotlin:

    binding.timerExpireTime.apply {
        base = SystemClock.elapsedRealtime()
        start()
    }

Solution 8 - Android

CountDownTimer in Kotlin:

object: CountDownTimer(3000, 1000){
    override fun onTick(p0: Long) {}
    override fun onFinish() {
        //add your code here
    }
 }.start()

Solution 9 - Android

For future readers, you may use the built-in timer inline function in Kotlin.

Example:

import kotlin.concurrent.timer
....
....
timer(initialDelay = 1000L, period = 1000L ) {
     launch {
        executeTask()
     }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndy JazzView Question on Stackoverflow
Solution 1 - AndroidDanail AlexievView Answer on Stackoverflow
Solution 2 - AndroidDima KozhevinView Answer on Stackoverflow
Solution 3 - AndroidLiAView Answer on Stackoverflow
Solution 4 - AndroidGastón SaillénView Answer on Stackoverflow
Solution 5 - AndroidMathieuView Answer on Stackoverflow
Solution 6 - AndroidChinmayView Answer on Stackoverflow
Solution 7 - AndroidSana EbadiView Answer on Stackoverflow
Solution 8 - AndroidGhayasView Answer on Stackoverflow
Solution 9 - AndroidAhmed LotfyView Answer on Stackoverflow