package com.peachpasswords.vault import androidx.annotation.VisibleForTesting import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.launch import javax.inject.Inject import javax.inject.Singleton /** * Idle auto-lock timer. The vault locks [VaultSettings.autoLockMinutes] after the * last user interaction; a null (or non-positive) setting means "never". [poke] * restarts the countdown, so only the final interaction before the idle window * elapses triggers the lock. Reads the live setting from [VaultRepository] each * time so a settings change takes effect on the next interaction without rewiring. */ @Singleton class VaultLockTimer @Inject constructor( private val vault: VaultRepository, ) { // Test seam: replaced with a TestScope so the countdown can be driven on // virtual time. Defaults to a process-lifetime background scope in production. @VisibleForTesting internal var scope: CoroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) private var timerJob: Job? = null /** Reset the idle countdown. No-op (and cancels) when auto-lock is off or locked. */ fun poke() { val minutes = vault.settings.value.autoLockMinutes if (minutes == null || minutes <= 0 || !vault.isUnlocked.value) { cancel() return } timerJob?.cancel() timerJob = scope.launch { delay(minutes * 60_000L) vault.lock() } } /** Begin the idle countdown after an unlock. */ fun start() = poke() /** Stop the countdown (manual lock, background lock). */ fun cancel() { timerJob?.cancel() timerJob = null } }