Persist the exploration, proposal, spec, design, tasks, and verify/archive reports produced during the multi-device EQ, alarm-countdown, and notification-visual-polish SDD cycles.
80 lines
6.6 KiB
Markdown
80 lines
6.6 KiB
Markdown
# 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.
|