0

I tried to create a basic countdown timer android app using kotlin. My app works completely fine in the emulator. But in the device, it is getting stuck when the countdown timer reaches 1 second, it's not becoming 0. Secondly, I added sound after timer completion which is working fine in the emulator but not in the device again. Just for reference, I am testing the app on the device with android 6. I am attaching the kotlin code below. I am new to android development, any help is very much appreciable.

package com.ankan1998.mtimer

import android.annotation.SuppressLint
import android.media.MediaPlayer
import android.os.Bundle
import android.os.CountDownTimer
import android.util.Log
import android.widget.*
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {
    lateinit var xHour: EditText
    lateinit var xMin: EditText
    lateinit var xSec: EditText
    lateinit var xSwitch: Switch
    lateinit var xtimer: TextView
    lateinit var xReset: Button
    private var mTimeLeftInMillis:Long=0
    lateinit var xCountDownTimer: CountDownTimer

    @SuppressLint("SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        xHour = findViewById(R.id.editHour)
        xMin = findViewById(R.id.editMinute)
        xSec = findViewById(R.id.editSecond)
        xtimer = findViewById(R.id.textViewTimer)
        xReset=findViewById(R.id.reset)


        xSwitch = findViewById(R.id.switch_1)

        fun timeDecorator(hr:String,mn:String,sec:String):String{
            val h:String = if (hr.length==1) "0$hr" else hr
            val m:String=if (mn.length==1) "0$mn" else mn
            val s:String=if (sec.length==1) "0$sec" else sec
            return "$h:$m:$s"
        }

        fun playAudio(){
            try{
                val mediaPlayer: MediaPlayer = MediaPlayer.create(applicationContext,R.raw.ding)
                mediaPlayer.start()

            }catch (e: Exception){
                Toast.makeText(applicationContext,"Media Player failed",Toast.LENGTH_SHORT).show()

            }
        }



        fun convertMillisToLocalTime(ztime:Long):String{
            var h = (ztime/1000)/60/60
            var m = ((ztime/1000)/60)%60
            var s = ((ztime/1000)%60)
            Log.d("hour",h.toString())
            Log.d("min",m.toString())
            Log.d("sec",s.toString())
            return timeDecorator(h.toString(),m.toString(),s.toString())
        }

        fun convertStringToMillis(dateTimeString:String): Long {
            var t = dateTimeString.split(":")
            var tMilis:Long = t[0].toLong()*60*60*1000 + t[1].toLong()*60*1000+t[2].toLong()*1000
//            Log.d("millisecond", tMilis.toString())
            return tMilis
        }



        fun startTimerFunctionality(){
            xCountDownTimer = object: CountDownTimer(mTimeLeftInMillis, 1000) {
                override fun onTick(millisUntilFinished: Long) {
                    Log.d("l",millisUntilFinished.toString())
                    mTimeLeftInMillis=millisUntilFinished
                    xtimer.setText(convertMillisToLocalTime(millisUntilFinished))
                }

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

            }

        fun pauseTimer(){
            xCountDownTimer.cancel()

        }

        fun activateEdit(txtController:EditText){
            txtController.setFocusable(true)
            txtController.setFocusableInTouchMode(true)
            txtController.setClickable(true)
        }

        fun deactivateEdit(txtController:EditText){
            txtController.setFocusable(false)
            txtController.setFocusableInTouchMode(false)
            txtController.setClickable(false)
        }



        xSwitch.setOnCheckedChangeListener { _, isChecked ->
            if (isChecked) {
                val h : String = xHour.text.toString().ifEmpty { "00" }
                val m : String = xMin.text.toString().ifEmpty { "00" }
                val s : String = xSec.text.toString().ifEmpty { "00" }

                activateEdit(xHour)
                activateEdit(xMin)
                activateEdit(xSec)

                if (m.toInt()>60){
                    Toast.makeText(this@MainActivity, "Minutes should be less than 60",
                        Toast.LENGTH_SHORT).show()
                    xSwitch.setChecked(false)
                } else if (s.toInt()>60){
                    Toast.makeText(this@MainActivity, "Second should be less than 60",
                        Toast.LENGTH_SHORT).show()
                    xSwitch.setChecked(false)
                }else{

                    if(mTimeLeftInMillis == 0L) {
                        xtimer.text = timeDecorator(h, m, s)
                        mTimeLeftInMillis = convertStringToMillis("$h:$m:$s")
                    }
                    Log.d("timer",mTimeLeftInMillis.toString())
                    startTimerFunctionality()
                    deactivateEdit(xHour)
                    deactivateEdit(xMin)
                    deactivateEdit(xSec)

                }

            } else {
                pauseTimer()

            }


        }


        xReset.setOnClickListener {
            xtimer.text="00:00:00"
            xHour.text.clear()
            xMin.text.clear()
            xSec.text.clear()
            mTimeLeftInMillis=0
            pauseTimer()
            xSwitch.setChecked(false)
            activateEdit(xHour)
            activateEdit(xMin)
            activateEdit(xSec)

        }


    }
}

This is how my app looks for reference enter image description here

Ankan Sharma
  • 61
  • 1
  • 7
  • I think you can log mTimeLeftInMillis above onTick(). Maybe you always are set 0 – kadirgun Mar 28 '23 at 10:14
  • 1
    You're not updating `mTimeLeftInMillis` or setting `xTimer.text` in the `onFinish()` callback, so you're taking a gamble on whether the last call to `onTick` happens to have a value less than 1000 ms left. By the way, it is unconventional that you define all your functions *inside* your other function, `onCreate()`. That shouldn't cause problems, but for readability reasons we typically avoid so much deep nesting (indenting) of code and having such a long function. Those could all be `private` funs defined outside `onCreate()`. – Tenfour04 Mar 28 '23 at 13:16
  • 1
    More info about the `onTick()` here: https://stackoverflow.com/a/12431313/506796 Basically, you should call `onTick(0L)` inside your `onFinish()` function. – Tenfour04 Mar 28 '23 at 13:23
  • @Tenfour04 thanks a lot for your help and also for the linked Stackoverflow. I will try to use a custom countdown timer instead of an inbuilt one due to that delay accumulation not causing the last tick. Will restructure the code too once everything starts working fine. Thank you again. – Ankan Sharma Mar 28 '23 at 16:03

0 Answers0