# Tasks: Alarm Live Countdown ## Review Workload Forecast | Field | Value | |-------|-------| | Estimated changed lines | ~175–200 (additions + deletions) | | 400-line budget risk | Low | | Chained PRs recommended | No | | Suggested split | Single PR | | Delivery strategy | ask-on-risk | | Chain strategy | pending | Decision needed before apply: No Chained PRs recommended: No Chain strategy: pending 400-line budget risk: Low ### Suggested Work Units | Unit | Goal | Likely PR | Notes | |------|------|-----------|-------| | 1 | All changes (l10n + MethodChannel + Kotlin receiver + snooze guard) | PR 1 | Two logically isolated features; same PR acceptable given low line count | --- ## Phase 1: Foundation — ARB Keys and Contracts - [ ] 1.1 Add `preNoticeCountdown` key (value: `"Starts in {minutes} min"`) to `lib/l10n/app_en.arb` as the canonical English source key. - [ ] 1.2 **[RED]** Write a Dart unit test asserting `AppLocalizations.of(ctx).preNoticeCountdown(minutes: 30)` returns the expected English string; confirm it fails (key missing). - [ ] 1.3 Add `preNoticeCountdown` key to all 12 remaining `lib/l10n/app_*.arb` files (es, fr, de, pt, it, ja, ko, zh, ar, ru, nl, pl) using locale-appropriate translation following the `durationMinutesOnly` sentence pattern. Each value must contain the `{minutes}` placeholder. - [ ] 1.4 **[GREEN]** Run `flutter gen-l10n`; confirm test 1.2 now passes. - [ ] 1.5 **[REFACTOR]** Verify ARB placeholder annotations (`"@preNoticeCountdown"` with `placeholders.minutes.type: "int"`) are present in `app_en.arb` per ARB spec convention. ## Phase 2: Flutter Side — MethodChannel Argument - [ ] 2.1 **[RED]** Write a unit/widget test for `ServicioAlarmasAndroid.programar()` asserting the MethodChannel call includes a `preNoticeTemplate` key whose value contains `{minutes}`. - [ ] 2.2 In `lib/servicios/servicio_alarmas_android.dart`, compute `preNoticeTemplate` from `l10n.preNoticeCountdown(minutes: '{minutes}')` (passing the literal placeholder string) and add it to the `scheduleAlarm` MethodChannel arguments map. - [ ] 2.3 **[GREEN]** Confirm test 2.1 passes. - [ ] 2.4 **[REFACTOR]** Ensure the `preNoticeTemplate` argument is added adjacent to the existing `title` localization call; no dead code left over. ## Phase 3: Kotlin — AlarmScheduler and NativeAlarmSpec - [ ] 3.1 **[RED]** Write a Kotlin unit test asserting `AlarmScheduler.scheduleAlarm()` with a `preNoticeTemplate` string stores it in the resulting `NativeAlarmSpec` and embeds it into the PendingIntent extras under key `"preNoticeTemplate"`. - [ ] 3.2 Add `preNoticeTemplate: String? = null` field to `NativeAlarmSpec` data class in `android/.../AlarmScheduler.kt` (nullable for backward compat; schema stays v3). - [ ] 3.3 Update `MainActivity.kt` to read `preNoticeTemplate` from the MethodChannel arguments map and pass it to `AlarmScheduler.scheduleAlarm()`. - [ ] 3.4 In `AlarmScheduler.scheduleAlarm()`, accept `preNoticeTemplate` param and embed it into the pre-notice `Intent` extras as `EXTRA_PRE_NOTICE_TEMPLATE = "preNoticeTemplate"`. - [ ] 3.5 **[GREEN]** Confirm test 3.1 passes. - [ ] 3.6 **[REFACTOR]** Confirm `EXTRA_PRE_NOTICE_TEMPLATE` constant is declared once (in `AlarmScheduler` or a shared constants file) and not duplicated between scheduler and receiver. ## Phase 4: Kotlin — BroadcastReceiver Countdown Computation - [ ] 4.1 **[RED]** Write a Kotlin unit test for `PluriWaveAlarmReceiver.showPreNoticeNotification()` covering: (a) normal case returns template with computed minutes, (b) clamped to 1 when `triggerAtMillis <= currentTimeMillis`, (c) null template falls back to English default text. - [ ] 4.2 In `android/.../PluriWaveAlarmReceiver.kt`, read `EXTRA_PRE_NOTICE_TEMPLATE` from the intent extras. Compute `remaining = max(1L, (triggerAtMillis - System.currentTimeMillis()) / 60_000)`. Replace `{minutes}` placeholder in the template and pass result to `NotificationCompat.Builder.setContentText()`. - [ ] 4.3 Add null-safety fallback: if `preNoticeTemplate` is null or blank, use `"Starts in $remaining min"` as the default English string. - [ ] 4.4 Remove the existing hardcoded Spanish `"Empieza en 30 minutos"` string from the receiver. - [ ] 4.5 **[GREEN]** Confirm all test cases in 4.1 pass. - [ ] 4.6 **[REFACTOR]** Extract the minutes-computation expression into a private function `computeRemainingMinutes(triggerAtMillis: Long): Long` for readability and testability. ## Phase 5: Dart — Snooze Dismiss Guard - [ ] 5.1 **[RED]** Write a widget test for `PantallaAlarmaSonando._posponer()` in `lib/pantallas/pantalla_alarma_sonando.dart` asserting: (a) when `Navigator.canPop()` is true, `Navigator.pop()` is called and `SystemNavigator.pop()` is NOT called; (b) when `canPop()` is false, `SystemNavigator.pop()` is called instead. - [ ] 5.2 In `lib/pantallas/pantalla_alarma_sonando.dart`, replace the bare `Navigator.of(context).pop()` in `_posponer()` with `if (Navigator.of(context).canPop()) { Navigator.of(context).pop(); } else { SystemNavigator.pop(); }`. Apply the same guard to `_detener()` if it also calls `pop()` without a guard. - [ ] 5.3 Verify that `_liberarAudioLocal()`, `radio.audio.pausar()`, and `alarmas.posponerAlarma()` all execute BEFORE the navigation action (ordering unchanged). - [ ] 5.4 **[GREEN]** Confirm tests from 5.1 pass. - [ ] 5.5 **[REFACTOR]** Extract the guard into a private helper `_dismissScreen()` called from both `_posponer()` and `_detener()` to eliminate duplication. ## Phase 6: Integration Verification - [ ] 6.1 Extend existing `scheduleAlarm` integration test (MethodChannel round-trip) to assert `preNoticeTemplate` survives the Flutter → Kotlin boundary correctly. - [ ] 6.2 Confirm `notificationIdForAlarm(alarmId)` is used in `showPreNoticeNotification()` (notification ID stability — re-posting same ID updates in place). No change needed if already correct; add assertion to test if not. - [ ] 6.3 Run full test suite (`flutter test` + Kotlin `./gradlew test`); all pre-existing tests must remain green. - [ ] 6.4 Manual smoke test: schedule an alarm ~2 min out; verify pre-notice notification shows correct locale and computed minutes; tap Snooze from a fresh app launch; confirm screen dismisses cleanly. ## Phase 7: Cleanup - [ ] 7.1 Confirm no hardcoded Spanish strings remain in `PluriWaveAlarmReceiver.kt` notification path (`rg "Empieza" android/`). - [ ] 7.2 Confirm `preNoticeTemplate` is the only new MethodChannel key added; no dead args left in `scheduleAlarm` map. - [ ] 7.3 Update inline code comments in `AlarmScheduler.kt` and `PluriWaveAlarmReceiver.kt` to document the template-at-schedule-time, replace-at-fire-time pattern.