Android How to Implement Timeout Lockscreen

Aug 20, 2018
Return to Main Activity if application idle for N minutes

I am building a security sensitive app, and I need the app to return to login screen if the app is idle for N minutes.

First, I need to implement a global activity watcher for all activities.

  • Timer start when a activity is started (and timer stop when activity is stopped)
  • Later we will implemenent a mechanism to check for user interaction (touch event).
  • When activeCount > 0, it means the application is still in foreground/active, thus launch the LoginActivity when timeout. Make sure to clear activity task/stack when launching LoginActivity to prevent ability to go back.
  • When activeCount <= 0, it means the application is send to background, thus we just finish/exit. Launching an activity when application in background will cause the activity to force appear and interrupt the user's current app.
  • Skip this process if current activity is LoginActivity.
class ActivityWatcher : Application.ActivityLifecycleCallbacks {    companion object {        private const val TAG = "ActivityWatcher"    }    private var activeCount: Int = 0    private var lockTimer: Timer = Timer()    private var sleep: Long = 5000    fun initSleep() {    }    fun startLockTimer(activity: Activity?) {        // val sharedPref = PreferenceManager.getDefaultSharedPreferences(activity?.applicationContext)        // var sleep = sharedPref.getString(activity?.getString(R.string.pref_key_auto_lock_duration), "0").toLong() * 1000                Log.d(TAG, "startLockTimer=$sleep")        if (sleep > 0) {            lockTimer.cancel()            lockTimer = Timer()            lockTimer.schedule(sleep) {                Log.d(TAG, "Timer triggered")                activity?.apply {                    // just to be safe                    try {                        if (activeCount > 0) {                            finish()                            val intent = Intent(this, LoginActivity::class.java)                            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK                            startActivity(intent)                        } else {                            // moveTaskToBack(true)                            finishAffinity()                        }                    }                    catch (e: Exception) {                        e.printStackTrace()                        Crashlytics.logException(e)                    }                }            }        }    }    private fun stopLockTimer() {        Log.d(TAG, "stopLockTimer")        lockTimer.cancel()        lockTimer = Timer()    }    override fun onActivityCreated(activity: Activity?, bundle: Bundle?) {    }    override fun onActivityStarted(activity: Activity?) {        activeCount++        Log.d(TAG, "onActivityStarted=$activeCount")        Log.d(TAG, activity?.javaClass?.name)        if (activity !is LoginActivity) {            startLockTimer(activity)        }        else {            // just in case            stopLockTimer()        }    }    override fun onActivityPaused(activity: Activity) {    }    override fun onActivityResumed(activity: Activity?) {    }    override fun onActivityStopped(activity: Activity?) {        activeCount--        Log.d(TAG, "onActivityStopped=$activeCount")        if (activeCount > 0 && activity !is LoginActivity) {            // when switch activity, will trigger start then stop (end up not starting any)            // stopLockTimer()        }    }    override fun onActivityDestroyed(activity: Activity?) {    }    override fun onActivitySaveInstanceState(activity: Activity?, p1: Bundle?) {    }}

Extend Application and call registerActivityLifecycleCallbacks.

class LuaApp : MultiDexApplication() {    override fun onCreate() {        super.onCreate()        val watcher = ActivityWatcher()        // store in global singleton        App.activityWatcher = watcher        registerActivityLifecycleCallbacks(watcher)            }}

NOTE: Refer to Android Register/Extend Application Class.

Create App singleton class to hold ActivityWatcher reference.

object App {    lateinit var activityWatcher: ActivityWatcher}

Create a base activity class to listen to onUserInteraction.

  • Whenever user interaction happens (not idle), the timer is restarted.
open class LuaActivity: AppCompatActivity() {    companion object {        private const val TAG : String = "LuaActivity"    }    override fun onUserInteraction() {        Log.d(TAG, "onUserInteraction")        super.onUserInteraction()        if (activity !is LoginActivity) { // no            App.activityWatcher.startLockTimer(this)        }    }    }

Make sure every activity extend LuaActivity instead of the usual AppCompatActivity.

class MainActivity : LuaActivity() {}

NOTE: Refer to Kotlin Singleton.

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.