In-progress artifact sets from the current SDD cycles: exploration, proposal, spec, design, tasks, and verify reports as produced so far. Also drops a leftover working copy of eq-device-disconnect-revert whose contents were already committed under changes/archive/.
4.9 KiB
Delta for native-alarms
ADDED Requirements
Requirement: Manifest declares alarm-eligible foreground service
PluriWaveAlarmService in AndroidManifest.xml MUST use foregroundServiceType="mediaPlayback|alarm" and the app MUST declare FOREGROUND_SERVICE_ALARM, so the service is eligible to start on Android 14+ (API 34+) from a broadcast receiver.
Scenario: Manifest declares required FGS type and permission
Testability: static/grep-verifiable
- GIVEN the built
AndroidManifest.xml - WHEN the
PluriWaveAlarmService<service>element is inspected - THEN
android:foregroundServiceTypeMUST containalarm - AND a
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_ALARM"/>MUST exist
Scenario: Native service starts from broadcast context on Android 14+
Testability: manual on-device QA (Android 14+; agent cannot install/verify)
- GIVEN an alarm is scheduled and the app is not foregrounded
- WHEN
PluriWaveAlarmReceiver.onReceive(ACTION_FIRE)startsPluriWaveAlarmService - THEN it MUST start without
ForegroundServiceTypeException - AND the alarm MUST be audible via the native
USAGE_ALARMplayer
Requirement: Ring-scoped device-volume override
While an alarm is ringing, the system MUST override STREAM_MUSIC to an audible reference level so the alarm is not silenced by device volume 0, and MUST restore the original captured volume when the ring ends through any exit path.
Scenario: Alarm is audible when device media volume is 0
Testability: manual on-device QA (native volume APIs)
- GIVEN device
STREAM_MUSICvolume is 0 - WHEN an alarm fires and the override captures/raises
STREAM_MUSICto an audible level - THEN the alarm MUST be audible for the full ring duration, not only the pre-handoff window
Scenario: Player fade-in ramps within the overridden level
Testability: flutter test with fakes
- GIVEN the override has set an audible
STREAM_MUSICreference level - WHEN the ring's fade-in timer runs
- THEN player volume MUST ramp 5% ->
alarma.volumenoveralarma.fadeInSegundos, unchanged from today
Scenario: Dismiss restores the original captured volume
Testability: flutter test with fakes (Dart->channel call) + manual on-device QA (native restore)
- GIVEN the override captured
STREAM_MUSICat volumeV - WHEN the user dismisses the alarm (
_detener()->_silenciarAudio()) - THEN
STREAM_MUSICMUST be restored to exactlyV
Scenario: Snooze restores the original captured volume
Testability: flutter test with fakes (Dart->channel call) + manual on-device QA (native restore)
- GIVEN the override captured
STREAM_MUSICat volumeV - WHEN the user snoozes the alarm (
_posponer()->_silenciarAudio()) - THEN
STREAM_MUSICMUST be restored to exactlyV
Scenario: Restore is idempotent across double-exit paths
Testability: flutter test with fakes (single/no-op restore call assertion) + manual on-device QA
- GIVEN restore already ran once (
_silenciarAudio()inside_detener()) - WHEN a second exit path also runs restore (e.g.
dispose()firing after) - THEN the second call MUST NOT throw, MUST NOT re-apply a stale value, and MUST leave
STREAM_MUSICunchanged
Scenario: Normal radio playback never triggers the override
Testability: flutter test with fakes (channel never invoked outside a ring) + manual on-device QA
- GIVEN the user is listening to radio with no alarm ringing
- WHEN playback starts, plays, or stops normally
- THEN the override MUST NOT be invoked;
STREAM_MUSICstays fully governed by device controls
Scenario: App killed mid-ring — best-effort restore only
Testability: manual on-device QA; accepted residual gap, not required in automated coverage
- GIVEN an alarm is ringing and the override is active
- WHEN the app process is killed before any exit path runs
- THEN the system SHOULD best-effort restore from service teardown/
onDestroy, but a residual overridden-volume state MAY occur and is an accepted known gap
Requirement: Single fade-in driver across native-to-Flutter handoff
During handoff from the native USAGE_ALARM player to the Flutter media-stream player, only one fade-in ramp MUST drive audible volume at any instant.
Scenario: No double-ramp interleaving at handoff
Testability: manual on-device QA (timing-sensitive, cross-process); flutter test with fakes can assert the Dart ramp only runs when native ownership is inactive, but cannot observe native MediaPlayer.setVolume() timing
- GIVEN the native fade-in is ramping the
USAGE_ALARMplayer - WHEN the Flutter player becomes ready and handoff occurs (
confirmarAudioFlutter) - THEN the native fade-in MUST stop driving audible volume once Flutter takes over; both ramps MUST NOT drive audible volume simultaneously
Non-Functional Notes
- No new user-visible strings; no l10n work required across the 13 supported locales.