Files
FreeTLab bccc5c48b8
Build & Deploy PluriWave / Análisis de código (push) Successful in 38s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m30s
docs(openspec): add SDD artifact trail for recent alarm and EQ changes
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.
2026-07-04 12:42:11 +02:00

80 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks: Alarm Live Countdown
## Review Workload Forecast
| Field | Value |
|-------|-------|
| Estimated changed lines | ~175200 (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.