package eu.brassepc.fitnessdroid.data import android.content.Context import android.media.RingtoneManager import android.os.VibrationEffect import android.os.VibratorManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch data class RestState( val totalSeconds: Int, val endsAtEpochMs: Long, val secondsLeft: Int, val finished: Boolean = false, ) /** * Vilotimern. Räknar mot en absolut sluttid (överlever att appen pausas) * och larmar enligt inställningarna när tiden är ute. */ class RestTimerController( private val context: Context, private val settingsStore: SettingsStore, private val scope: CoroutineScope, ) { private val _state = MutableStateFlow(null) val state: StateFlow = _state private var ticker: Job? = null fun start(seconds: Int? = null) { scope.launch { val settings = settingsStore.settings.first() val total = seconds ?: settings.defaultRestSeconds _state.value = RestState( totalSeconds = total, endsAtEpochMs = System.currentTimeMillis() + total * 1000L, secondsLeft = total, ) startTicker() } } fun adjust(deltaSeconds: Int) { val current = _state.value ?: return if (current.finished) return _state.value = current.copy( endsAtEpochMs = (current.endsAtEpochMs + deltaSeconds * 1000L) .coerceAtLeast(System.currentTimeMillis()), totalSeconds = (current.totalSeconds + deltaSeconds).coerceAtLeast(5), ) } fun dismiss() { ticker?.cancel() _state.value = null } private fun startTicker() { ticker?.cancel() ticker = scope.launch { while (true) { val current = _state.value ?: return@launch val left = (((current.endsAtEpochMs - System.currentTimeMillis()) + 999) / 1000) .coerceAtLeast(0).toInt() if (left != current.secondsLeft) { _state.value = current.copy(secondsLeft = left) } if (left <= 0) { _state.value = _state.value?.copy(secondsLeft = 0, finished = true) alert() // Låt "klart"-läget synas en stund, försvinn sedan självmant. delay(8_000) if (_state.value?.finished == true) _state.value = null return@launch } delay(250) } } } private suspend fun alert() { when (settingsStore.settings.first().restAlert) { RestAlert.SOUND -> runCatching { val uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) RingtoneManager.getRingtone(context, uri)?.play() } RestAlert.VIBRATE -> runCatching { val vm = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager vm.defaultVibrator.vibrate( VibrationEffect.createWaveform(longArrayOf(0, 350, 150, 350, 150, 500), -1) ) } RestAlert.VISUAL, RestAlert.NONE -> Unit } } }