0

Suppose I have an action to be executed after a certain amount of time without the user touching the screen. How can I implement a timer that executes an action when it times out, but starts over every time the user touches the screen?

  • [Getting Touches](https://stackoverflow.com/a/7450074/6783797) and combine it with [Tenfour04](https://stackoverflow.com/a/59005100/6783797)s answer – Tomes Nov 23 '19 at 08:49

2 Answers2

1

I searched through a few answers and couldn't find anything as simple as the solution I wanted for this problem. Heres what worked for me:

I made use of the onUserInteraction() function, which is called every time the user touches the screen. I declared a CountDownTimer that gets started in onCreate() and canceled in onDestroy(). Every time onUserInteraction() is called, the CountDownTimer is canceled, reset, and then started again. When the CountDownTimer finishes, the action is performed.

package com.example.inactivityapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.CountDownTimer
import android.util.Log
import android.widget.Toast

private val TAG = InactivityActivity::class.java.simpleName

private const val INACTIVITY_SECONDS: Int = 5

class InactivityActivity : AppCompatActivity() {

    var inactivitySeconds = INACTIVITY_SECONDS

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_inactivity)

        inactivityCountDownTimer.start()

    }

    override fun onDestroy() {
        super.onDestroy()
        inactivityCountDownTimer.cancel()
    }

    override fun onUserInteraction() {
        super.onUserInteraction()
        Log.i(TAG, "user interaction")
        inactivityCountDownTimer.cancel()
        inactivitySeconds = INACTIVITY_SECONDS
        inactivityCountDownTimer.start()
    }

    var inactivityCountDownTimer =
        object : CountDownTimer(inactivitySeconds * 1000.toLong(), 1000) {
            override fun onTick(millisUntilFinished: Long) {
                Log.i(TAG, inactivitySeconds.toString())
                inactivitySeconds--
            }

            override fun onFinish() {
                Toast.makeText(
                    this@InactivityActivity,
                    "Activity Timeout: Perform timeout task",
                    Toast.LENGTH_LONG
                ).show()
                Log.i(TAG, "activity timeout")
            }
        }
}

Output

1

Here's an untested idea with coroutines:

private const val INACTIVITY_SECONDS: Int = 5

abstract class InactivityActivity : AppCompatActivity() {

    private val counterScope = CoroutineScope(Dispatchers.Main)

    private fun restartCountDown() {
        counterScope.cancel()
        counterScope.launch {
            delay(INACTIVITY_SECONDS * 1000L)
            onInactivity()
        }
    }

    protected abstract fun onInactivity()

    override fun onPause() {
        super.onPause()
        counterScope.cancel()
    }

    override fun onResume() {
        super.onResume()
        restartCountDown()
    }

    override fun onUserInteraction() {
        super.onUserInteraction()
        restartCountDown()
    }
}
Tenfour04
  • 83,111
  • 11
  • 94
  • 154