⏱️ Timer

Uniwersalny system timerów - countdown, stopwatch, cooldown, interval.

Przegląd

Moduł Timer zapewnia kompleksowy system timerów przydatny dla:

Countdown (odliczanie)

Podstawowe odliczanie

// Proste odliczanie 60 sekund
ADict.Timer.countdown(
    id = "offer_expires",
    durationMs = 60_000L,
    onTick = { remainingMs ->
        val seconds = remainingMs / 1000
        tvCountdown.text = "Pozostało: ${seconds}s"
    },
    onFinish = {
        hideOffer()
        showExpiredMessage()
    }
)

Kontrola countdown

// Pauza
ADict.Timer.pauseCountdown("offer_expires")

// Wznowienie
ADict.Timer.resumeCountdown("offer_expires")

// Anulowanie
ADict.Timer.cancelCountdown("offer_expires")

// Sprawdź pozostały czas
val remaining = ADict.Timer.getCountdownRemaining("offer_expires")

// Czy aktywny
val isActive = ADict.Timer.isCountdownActive("offer_expires")

Obserwowanie stanu (Flow)

lifecycleScope.launch {
    ADict.Timer.getCountdownFlow("offer_expires").collect { state ->
        when (state) {
            is TimerState.Running -> {
                progressBar.progress = (state.progress * 100).toInt()
                tvTime.text = formatTime(state.remainingMs)
            }
            is TimerState.Paused -> {
                showPausedUI()
            }
            is TimerState.Finished -> {
                showFinishedUI()
            }
            is TimerState.Cancelled -> {
                hideTimerUI()
            }
            is TimerState.Idle -> {
                // Nie uruchomiony
            }
        }
    }
}

Stopwatch (stoper)

Podstawowy stoper

// Start stopera
ADict.Timer.startStopwatch("session_time")

// Pobierz aktualny czas
val elapsed = ADict.Timer.getElapsed("session_time")
Log.d("Timer", "Czas sesji: ${elapsed}ms")

// Pauza
ADict.Timer.pauseStopwatch("session_time")

// Wznów
ADict.Timer.resumeStopwatch("session_time")

// Reset (bez zatrzymywania)
ADict.Timer.resetStopwatch("session_time")

// Stop i pobierz całkowity czas
val totalTime = ADict.Timer.stopStopwatch("session_time")

Obserwowanie stopera

lifecycleScope.launch {
    ADict.Timer.getStopwatchFlow("session_time").collect { elapsedMs ->
        tvElapsed.text = ADict.Timer.formatTime(elapsedMs, TimeFormat.HH_MM_SS)
    }
}

Cooldown

System cooldownów do ograniczania częstotliwości akcji (np. reklamy, przyciski).

// Sprawdź czy akcja dostępna i uruchom cooldown
fun showRewardedAd() {
    if (ADict.Timer.isCooldownReady("ad_request")) {
        // Pokaż reklamę
        loadAndShowAd()

        // Rozpocznij 30-sekundowy cooldown
        ADict.Timer.startCooldown("ad_request", 30_000L)
    } else {
        val remaining = ADict.Timer.getCooldownRemaining("ad_request")
        val seconds = remaining / 1000
        showMessage("Poczekaj jeszcze ${seconds} sekund")
    }
}

// Anuluj cooldown (np. po zakupie premium)
ADict.Timer.cancelCooldown("ad_request")

Przykład: Przycisk z cooldownem

class CooldownButton(context: Context) : AppCompatButton(context) {

    private val cooldownId = "button_${hashCode()}"

    init {
        setOnClickListener {
            if (ADict.Timer.isCooldownReady(cooldownId)) {
                performAction()
                ADict.Timer.startCooldown(cooldownId, 5_000L)
                startCooldownUI()
            }
        }
    }

    private fun startCooldownUI() {
        isEnabled = false

        ADict.Timer.countdown(
            id = "${cooldownId}_ui",
            durationMs = 5_000L,
            onTick = { remaining ->
                text = "Czekaj (${remaining / 1000}s)"
            },
            onFinish = {
                text = "Kliknij"
                isEnabled = true
            }
        )
    }
}

Interval (powtarzający się timer)

// Heartbeat co 5 sekund
ADict.Timer.interval(
    id = "heartbeat",
    intervalMs = 5_000L,
    initialDelayMs = 0L
) {
    sendHeartbeat()
}

// Auto-save co minutę
ADict.Timer.interval(
    id = "auto_save",
    intervalMs = 60_000L,
    initialDelayMs = 10_000L // zacznij po 10s
) {
    saveDocument()
}

// Zatrzymaj interval
ADict.Timer.cancelInterval("heartbeat")

Formatowanie czasu

val ms = 125_500L // 2 min 5.5 sek

// Różne formaty
ADict.Timer.formatTime(ms, TimeFormat.SS)       // "125"
ADict.Timer.formatTime(ms, TimeFormat.MM_SS)    // "02:05"
ADict.Timer.formatTime(ms, TimeFormat.HH_MM_SS) // "00:02:05"
ADict.Timer.formatTime(ms, TimeFormat.MM_SS_MS) // "02:05.500"

Opóźnione wykonanie

// Proste opóźnienie (one-shot)
ADict.Timer.delay(2_000L) {
    showWelcomeMessage()
}

// Opóźnienie z ID (anulowalny)
ADict.Timer.delayWithId("welcome", 3_000L) {
    showTutorial()
}

Czyszczenie

// W onDestroy Activity/Fragment
override fun onDestroy() {
    super.onDestroy()

    // Wyczyść wszystkie timery
    ADict.Timer.clearAll()

    // Lub selektywnie
    ADict.Timer.cancelCountdown("my_timer")
    ADict.Timer.stopStopwatch("session")
    ADict.Timer.cancelInterval("heartbeat")
}

Przykład: Gra z limitem czasu

class GameActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        startGame()
    }

    private fun startGame() {
        // Rozpocznij sesję gry
        ADict.Timer.startStopwatch("game_session")

        // Limit czasu 2 minuty
        ADict.Timer.countdown(
            id = "game_timer",
            durationMs = 120_000L,
            tickIntervalMs = 100L,
            onTick = { remaining ->
                tvTimer.text = ADict.Timer.formatTime(remaining, TimeFormat.MM_SS_MS)

                // Efekt wizualny gdy mało czasu
                if (remaining < 10_000L) {
                    tvTimer.setTextColor(Color.RED)
                }
            },
            onFinish = {
                endGame()
            }
        )
    }

    private fun pauseGame() {
        ADict.Timer.pauseCountdown("game_timer")
        ADict.Timer.pauseStopwatch("game_session")
        showPauseMenu()
    }

    private fun resumeGame() {
        ADict.Timer.resumeCountdown("game_timer")
        ADict.Timer.resumeStopwatch("game_session")
        hidePauseMenu()
    }

    private fun endGame() {
        val totalTime = ADict.Timer.stopStopwatch("game_session")
        showResults(score, totalTime)
    }

    override fun onDestroy() {
        super.onDestroy()
        ADict.Timer.cancelCountdown("game_timer")
        ADict.Timer.stopStopwatch("game_session")
    }
}