2

I am writing a game app that uses a ChessClock class. Each of the two players has a clock. The clocks send themselves a stream of messages every tenth of a second, and if running, decrement the time remaining. When the time expires the clock calls a method in the parent view group that stops the clocks and sets a flag that causes further touch events to be ignored so that the users can no longer move the pieces. I am writing in kotlin, but I think this will be clear to a java programmer also:

class ChessClock : TextView  {
   constructor(context: Context) : super(context)
   constructor(context:Context, p:String) : this(context) {
       player = p
   }
   var player = ""
   val RUNNING   = 0
   val PAUSED    = 1
   val IDLE      = 2
   val EXPIRED   = 3
   val mInterval = 100L
   var mTime  = 0L     // in milliseconds
   var mState = IDLE
   val mHandler = Handler()
   val mUpdateTimeTask = object : Runnable {
       override fun run() {
          if (mState == RUNNING) showTime()
          mHandler.postDelayed(this, mInterval)
       }
   }

//    override fun onDetachedFromWindow() {
//        super.onDetachedFromWindow()
//        stop()
//    }

   fun setTime(minutes:Long) {
       mTime = 1000*60*minutes       // minutes to milliseconds
       mState = IDLE
       setTextColor(getResources().getColor(R.color.colorIdle))
       showTime()
       mUpdateTimeTask.run()
   }

   fun pause() {
       mState = PAUSED
       setTextColor(getResources().getColor(R.color.colorPaused))
   }

   fun start() {
        mState = RUNNING
        setTextColor(getResources().getColor(R.color.colorRunning))
   }

   fun expire() {
       mState = EXPIRED
       setTextColor(getResources().getColor(R.color.colorExpired))
       model.timeout(player)
       val aboyne = parent as AboyneView
       aboyne.gameOver(false)
    }

    fun toggle() {
        if (mState == PAUSED) start()
        else if (mState == RUNNING) pause()
    }

    fun stop() {
        mHandler.removeCallbacks(mUpdateTimeTask)
    }

    fun showTime() {
        val tenths = (mTime % 1000) / 100
        var seconds = mTime / 1000
        val minutes = seconds % 3600 / 60
        seconds %= 60
        if (minutes >= 1)
            setText(String.format("%02d:%02d",minutes, seconds))
        else
            setText(String.format("%01d:%02d:%01d",minutes, seconds, 
                 tenths))
       if (mState != RUNNING) return
       if (mTime > 0)
           mTime -= mInterval
       else
           expire()
    }
}

This works fine, but if the user presses the back button and then starts a new game, the clocks apparently keep running, even though the activity's onDestroy method has been called. Then when the running clock expires, the new game is frozen even though the new clocks are still running.

I'm new to android and I don't understand how this comes about. I would have thought that the clocks would be destroyed when the activity was. I've tested my ideas about what is happening with log messages, and have confirmed that the clocks run after onDestroy are called. I've also overridden onDetachedFromWindow to stop the clocks, and it makes the problem go away, but this is overkill. It will also stop the clock if the user gets a phone call, won't it, and this is not what I want.

Can you suggest how to best accomplish what I want? There is no way to access the clocks from the activity is there? I've thought of emitting some event in onDestroy, and having the ChessClock listen for it, but I don't know how to write custom events and listeners yet. Is there an easier way?

jagapathi
  • 1,635
  • 1
  • 19
  • 32
saulspatz
  • 5,011
  • 5
  • 36
  • 47
  • I'm new in kotlin language but if you can transform java code into kotlin i will show you how to get the back button click listener – Bruno Ferreira Jul 18 '17 at 15:24
  • @BrunoFerreira Yes, that would be great. I'm not much of a java programmer, but I should be able to transform the code. – saulspatz Jul 18 '17 at 16:01
  • to detect the back pressed in java just do this public void onBackPressed() { super.onBackPressed();} and if i want to do some thing before back buttons is fired i add code before super.onBackPressed();. – Bruno Ferreira Jul 18 '17 at 16:07
  • @BrunoFerreira But onBackPressed is in the activity. How would I access the clock from there? There doesn't seem to be a function onBackPressed for a View. – saulspatz Jul 18 '17 at 16:29

2 Answers2

0

I do not know how to do it in Kotlin, but in java You can set Timer executing a timertask every X millis and then cancel it if needed, calling mTimer.cancel() You should try using it instead of mHandler.postDelayed(this, mInterval)

here is how You do it: How to call a function after delay in Kotlin?

Pavel B.
  • 805
  • 10
  • 13
  • I don't quite follow this. How would it be any easier to call `mTimer.cancel()` than to stop the clock? If you could show me an example in java, I'm sure I could translate it to kotlin – saulspatz Jul 18 '17 at 16:38
0

I didn't understand how the suggestions made could fix my problem, but I came up with a kludge.

I made a global variable BACK_BUTTON initialized to false. In the activity, I overrode onDestroy to set BACK_BUTTON to true. Then I modified the ChessClock code to check for BACK_BUTTON:

val mUpdateTimeTask = object : Runnable {
    override fun run() {
        if (BACK_BUTTON) expire()
        else {
            if (mState == RUNNING) showTime()
            mHandler.postDelayed(this, mInterval)
        }
    }

Not a great solution, but it will have to do until I figure out custom events. So far as I can see, a custom event and a custom event listener is the only clean way to do this.

saulspatz
  • 5,011
  • 5
  • 36
  • 47