Commit graph

5 commits

Author SHA1 Message Date
44ea4ed87b Reminders: one grouped card for co-timed meds, acked together
Meds due at the same wall-clock minute now share a single notification
instead of one card each — they resolve to an identical scheduledAtMillis,
which becomes the occurrence (and notification) key. Per the feature
request: "Tatt alle" / "Utsett alle" handle the whole morning batch in
one tap; a single med just renders as a normal card with "Tatt".

- Notification id is per-occurrence (scheduledAtMillis), not per-log, so
  co-timed doses collapse onto one card and re-nags replace it.
- DoseActionReceiver buttons carry the occurrence and call group ops
  (takeAll/snoozeAll/muteAll); DoseActions keeps a single per-dose state
  mutation (takeState/snoozeState/muteState) reused by both the group ops
  and the app's per-dose actions, so the two surfaces can't diverge.
- The card is rebuilt from getActiveAtOccurrence: still-unresolved doses
  with nagCount <= cap. "Ikke forstyrr" now sets nagCount above the cap so
  a muted dose drops off the card, while a naturally-capped dose (== cap)
  stays visible and actionable.
- refreshOccurrence re-renders silently after a single in-app action and
  never creates a card for a not-yet-due occurrence.

Adds DoseActionsGroupTest (instrumented) covering takeAll, muteAll, and
the muted-vs-capped distinction at the data layer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:28:18 +02:00
778a6aec27 Reminders: global repeat toggle + per-dose "Ikke forstyrr"
Two ways to stop the every-10-min nag, per feature request:

- A global "Gjenta påminnelser" switch in Settings. Off → a due dose
  alerts once and goes quiet; only the automatic re-nag chain is gated,
  so an explicit "Utsett 15 min" still gives one more reminder. The gate
  sits at the three automatic arm points (fireOccurrence, escalate
  re-arm, armAll resume-after-reboot), never on Snooze.

- An "Ikke forstyrr" action that appears from the second reminder on
  (nagCount >= 1). It stops nagging this one dose but leaves it PENDING
  so it can still be taken later — distinct from Skip. Implemented by
  jumping to the existing terminal state (nagCount = NAG_CAP) rather than
  a new column, so it needs no migration and survives reboot for free
  (armAll re-arms only nagCount < cap).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:16:16 +02:00
8c3cd0c965 Don't nag for future doses: time-guard escalation resume
'Ta nå' + 'Angre' on a not-yet-due dose leaves a PENDING log with a
future scheduledAt; armAll() (every app start/update) then resumed
'escalation' for it as if it were overdue — nagging hours early. Three
layers: armAll only resumes escalation for past-due logs, the
escalation receiver drops anything scheduled in the future (defense
in depth), and undo resets nagCount + cancels any escalation so stale
counts can't eat the real quota when the dose actually falls due.

Emulator-verified with both cases side by side: past-due PENDING nags
within the minute, future PENDING stays silent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 18:26:46 +02:00
9b5817fcef Inventory split: InventoryItem entity, schema v2 with tested migration, Lager screen
Stock, package size and prescription state move to inventory_item;
medication is now a regimen (itemId FK RESTRICT — deleting a stock item
can never silently take adherence history with it). Supply warnings
aggregate consumption across all regimens sharing an item. New Lager
screen with one-tap '+1 pakning' restock; med editor picks an existing
item or creates one inline via the shared ItemFormState (one form, no
drift). Backup format v2 mirrors the split and still restores v1 files
by performing the same conversion the DB migration does.

MIGRATION_1_2 statements are copied from the exported 2.json and proven
by an instrumented MigrationTestHelper test on the emulator (validates
against the schema AND asserts data survival) before it ever touches
the phone's medical record. Heads-up: connectedAndroidTest must target
the emulator (ANDROID_SERIAL) — the phone's release signature rejects
debug test APKs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 15:13:28 +02:00
46a5d7e98e Reminder subsystem: exact alarms, escalation, boot re-arm — verified on emulator
Milestone 4. AlarmScheduler is the only AlarmManager writer; one stable
PendingIntent requestCode per dose-time means re-arming replaces and can
never stack (the no-drift invariant is OS-enforced). DoseAlarmReceiver
handles both occurrence and escalation alarms idempotently and always
re-arms the next occurrence before anything else so the chain can't
break. nagCount lives in dose_log so the ~6-nag cap survives reboot.
SCHEDULE_EXACT_ALARM added maxSdk 32 (USE_EXACT_ALARM is 33+ only).

Emulator-verified (API 35): exact alarm armed (window=0,
policy_permission), fired on time, HIGH notification with Tatt/Utsett,
escalation armed +10 min, next day re-armed, Tatt → TAKEN + inventory
10→9 + escalation cancelled, reboot → BootReceiver re-armed everything.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:48:39 +02:00