Search code examples
androidkotlincountdowntimer

Creating multiple countdown timers with a for-loop Android Kotlin


I'm currently having a problem with a CountDownTimer with Kotlin.

I am trying to achieve a timer which counts down from say 45 seconds, then 30 seconds for a specified number of times.

What's actually happening is the for-loop goes through all the iterations and when it gets to the last iteration it starts the timer and only runs it once.

I think this probably due to threads, but I'm not 100% sure, and haven't been able to find anything related to this so I may be wrong.

There are a couple of similar questions which I have seen, but neither of them have answers that work:

Here is the class:


import android.os.Bundle
import android.os.CountDownTimer
import com.google.android.material.snackbar.Snackbar
import androidx.appcompat.app.AppCompatActivity
import ee.shanel.hiittimer.timer.HiitData
import ee.shanel.hiittimer.timer.Workout
import ee.shanel.hiittimer.timer.WorkoutSet

import kotlinx.android.synthetic.main.activity_timer.*
import kotlinx.android.synthetic.main.content_timer.*

class TimerActivity : AppCompatActivity() {

    private var previousTimerActive = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_timer)
        setSupportActionBar(toolbar)

        fab.setOnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show()
            val workoutSets = calclateTimer()

            for (workoutSet in workoutSets) {
                startWorkout(workoutSet)
            }
        }
    }

    private fun startWorkout(workoutSet: WorkoutSet) {
        timerStatusText.setText("Go")
        timerStatusText.setText(workoutSet.status)
        object : CountDownTimer(workoutSet.secs, 1000) {
            override fun onTick(millisUntilFinished: Long) {
                val minutesRemaining = millisUntilFinished / 60000
                val secondsRemaining = (millisUntilFinished % 60000) / 1000
                val minutes = appendZero(minutesRemaining)
                val seconds = appendZero(secondsRemaining)
                val timerText = "${minutes} : ${seconds}"
                timer.setText(timerText)
            }
            override fun onFinish() {
            }
        }.start()


    }


    private fun calclateTimer(): ArrayList<WorkoutSet> {
        val hiitData = getIntent().getExtras().getParcelable<HiitData>("hiitData")
        val workout: ArrayList<WorkoutSet> = ArrayList()

        for (i in 0..hiitData.sets) {
            val work = WorkoutSet("Workout", (hiitData.workoutSecs * 1000).toLong())
            val rest = WorkoutSet("Rest", (hiitData.restSecs * 1000).toLong())
            workout.add(work)

            if (i != hiitData.sets) {
                workout.add(rest)
            }
        }
        return workout
    }

    private fun appendZero(time: Long): String {
        val timeString = time.toString()
        return if (time < 10) "0$timeString" else timeString
    }
}

Thanks in advance for any help you can give.


Solution

  • When you start you should take the first one.

        fab.setOnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show()
            val workoutSets = calclateTimer()
    
            workoutSet = workoutSets.get(0)
            startWorkout(workoutSet)
    
        }
    

    Then you can, for example, remove it and take again the first.

    private fun startWorkout(workoutSet: WorkoutSet) {
        timerStatusText.setText("Go")
        timerStatusText.setText(workoutSet.status)
        object : CountDownTimer(workoutSet.secs, 1000) {
            override fun onTick(millisUntilFinished: Long) {
                val minutesRemaining = millisUntilFinished / 60000
                val secondsRemaining = (millisUntilFinished % 60000) / 1000
                val minutes = appendZero(minutesRemaining)
                val seconds = appendZero(secondsRemaining)
                val timerText = "${minutes} : ${seconds}"
                timer.setText(timerText)
            }
            override fun onFinish() {
               workoutSets.remove(workoutSet)
               workoutSet = workoutSets.get(0)
               startWorkout(workoutSet)
            }
        }.start()
    }
    

    There is also the option not to remove it and just take the second but this requires to change the startWorkout(workoutSet: WorkoutSet) function in order to pass also the index of the list.