Reminders fired for every due CalendarAlerts row regardless of whether
the event's calendar had been disabled in-app, even though disabled
calendars are hidden from every other surface. Carry the calendar id
through the alert and filter disabled calendars before posting, while
still marking all due alerts fired so the provider stops re-broadcasting
the suppressed ones.
Closes #17
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an [Unreleased] section covering the two user-facing changes in
release/v2.13.0: multiple default reminders per calendar (Codeberg #14,
@moonj) and the fix for editing a single occurrence of a recurring event
(Codeberg #16). The debug-build ribbon is dev-only and intentionally omitted.
Define the [#14] and [#16] links.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the accumulated [Unreleased] section under [2.12.0] — 2026-06-28 and
regenerate fastlane/.../changelogs/21200.txt from it (scripts/
sync_changelog_to_fastlane.sh), so the official F-Droid listing and the
Gitea release notes both cover the release's two changes: the past-event
display settings (agenda screen + widget, month/week dimming) and the
overlapping-event title wrapping. versionName is already 2.12.0/21200.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Issue #12 asks for hiding finished events in the agenda view *and* its
home-screen widget. The in-app screen already honours the Past events
setting (Show / Dim / Hide); extend the same setting to the "Upcoming"
agenda widget.
The widget loads its events unfiltered and applies the mode reactively in
the composition against a captured "now" — mirroring how the range is read
from per-instance Glance state — so toggling the setting reflects on a live
widget without relying on provideGlance's data preamble re-running. Hiding
drops finished events and any day they empty; dimming fades the colour
stripe and lowers the title's emphasis (Glance has no generic alpha
modifier). setPastEventDisplay now pushes the mode into each widget's Glance
state and recomposes, like setAgendaWidgetRange.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The overlapping-title fix already shipped its F-Droid note in
fastlane/.../21200.txt but was never added to CHANGELOG.md, so the
release-cut sync (which regenerates the fastlane note from the changelog's
version section) would have dropped it. Add it under [Unreleased] › Changed
alongside the past-event display settings, and define the missing [#13] link.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds two independent display settings under Settings › Appearance, both
defaulting to the current behaviour (off) so nothing changes until a user
opts in:
• Agenda › "Past events" (Show / Dim / Hide) — events that already ended
today can be left as-is, faded, or dropped from the list. Hiding also
removes any day left empty, falling back to the empty state. Re-evaluated
each minute so rows fade/fall away as they end while the screen is open.
• Calendar › "Dim completed events" — a separate toggle that fades finished
events in the month and week grids, kept independent of the agenda setting.
An event counts as completed once its end is at or before now (in-progress
events are never dimmed; all-day events only after their day is fully over),
via a shared EventInstance.hasEnded(now). The grids read the cut-off through
a new LocalDimCutoff CompositionLocal (mirroring LocalShowHourLines) so only
the event chips recompose on the per-minute tick, and only while dimming is on.
Also adds an "Agenda" section header so the agenda rows stand apart in the
now-busier Appearance screen, and documents the feature in the changelog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Opening the new-event form now puts the cursor in the title field and raises the
keyboard, so the user can type the name straight away instead of tapping the
field first (issue #10). On by default — most events get a title — with a new
"Focus title on new event" switch in Settings → New event form to turn it off.
Only the create form auto-focuses: editing an existing event and opening a
prefilled/imported form never grab focus (guarded by !isEditing && title blank).
- SettingsPrefs: autofocusEventTitle (booleanPreferencesKey), default true.
- Plumbed through SettingsViewModel/UiState (settings switch) and
EventEditViewModel/UiState (read by the form).
- EventEditScreen: a FocusRequester on the title InlineField, requested once per
open from a LaunchedEffect when the guard holds.
- Strings (en + de), unit test for the new pref default/round-trip.
Bumps to 2.11.2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Calendula didn't declare the intent filters launchers and the system use for
calendar actions, so it never appeared in the "default calendar app" chooser —
on every platform, not just GrapheneOS (issue #9). Android exposes no API for an
app to set itself default, so registering these filters is the only way users can
pick it from the system picker.
Adds to MainActivity:
- MAIN + APP_CALENDAR — the "open the calendar app" action the OS/launchers use.
- VIEW on content://com.android.calendar/time/<epochMillis> and the time/epoch
mime type — a launcher/clock date tap. The provider's time Uri is parsed into a
LocalDate and opened on the day view, rooted over the default home view (a new
sourceless WidgetNavRequest.OpenDate). The .ics import path now ignores the
calendar provider host so a date tap isn't mistaken for a file to import.
Bumps to 2.11.1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per review, it shouldn't be a top-level Settings entry: the "Help translate"
link now sits at the top of the full-screen App language picker (OptionPicker
gains an optional header slot). Updated README + changelog wording to match.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Invite community translations: a new Settings > Help translate row (next to
App language) opens the project's Weblate engage page, plus a Translations
section in the README. Documented in the 2.11.0 changelog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the Calendars manager redesign, Notifications reorg, QS-tile move,
event-form field icons, and automatic local-calendar backup (#8) to the
2.11.0 changelog; sync the F-Droid per-version changelog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expand the #4 entry to cover the in-agenda range bar (date header + session
range switch) and add a Changed note for the Appearance regrouping and the
friendlier agenda empty state. Regenerate the F-Droid changelog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump versionName to 2.11.0 (versionCode 21100) and add the 2.11.0
changelog: any-day week start (#3), 12/24-hour time format (#6),
optional timeline hour lines (#5), and agenda range limits (#4).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On-device review follow-ups:
- Month widget grid is now tappable. Day numbers open that day and event
bars open the event's detail, both rooted in the month view so back
returns to the grid. Previously only the prev/next/today header
controls responded — the grid cells were never clickable.
- Pill/drawer view switches now build a visit history instead of
collapsing to the default view. Back retraces the views you moved
through (a not-yet-visited view is pushed; revisiting one collapses the
loop back to it), down to the default, then exits. Widget launches
still reset to their own view context.
Refs #1, #2 (reported by @devinside).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace CalendarHost's single global view slot with a top-level view
back stack rooted at a user-configurable default view. A lateral move
(pill/drawer/widget) replaces the non-home top; a date tap drills the
day view on top; a base-level BackHandler pops one level until only the
home view remains, then the system exits.
Widgets now carry their source view (EXTRA_SOURCE_VIEW) so a launch
roots the stack in that widget's view: backing out of a day/event opened
from the agenda widget returns to Agenda, and from the month widget to
Month, instead of always landing on Week. Reminder taps keep the
separate detail-key channel and leave the base view untouched.
Add a Default view setting (Settings -> Appearance) backing the stack's
home view; defaults to Week so existing users see no change.
Resolves the two Codeberg reports from @devinside:
- #1 [FR] Option to set default view
- #2 [Bug] Widget UX improvement
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump versionName to 2.8.0 (versionCode 20800) — the merge of this to main
is what cuts the release. Move the accumulated 2.8.0 work out of
[Unreleased] into a dated CHANGELOG section and regenerate the F-Droid
per-version changelog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
F-Droid's binary scanner (the fdroiddata `check apk` job) rejects any
"extra signing block". Our release APK carried AGP's dependency-metadata
block (id 0x504b4453, ~8 KB) in the APK Signing Block. Disable it with
`dependenciesInfo { includeInApk = false; includeInBundle = false }`.
The block lives in the signing block, not the zip entries, so removing it
changes no build output — v2.7.5 is functionally identical to 2.7.4 and
still reproduces byte-for-byte. Verified locally: a releaseTest build now
carries only the v2 signature + verity padding (no 0x504b4453 block).
This was the last F-Droid blocker: v2.7.5 now clears vcsInfo (since 2.7.3),
foojay (since 2.7.4) and the dependency-metadata block. Bumps versionName
to 2.7.5 and retargets the official recipe draft at v2.7.5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
F-Droid's official build scanner rejects the foojay-resolver-convention
plugin because it can fetch a JDK over the network during the offline,
reproducible build. The plugin was inert here (no toolchain block ever
invoked it), so removing it leaves the app functionally identical to
2.7.3 and still reproducible byte-for-byte from source.
Bumps versionName to 2.7.4 (the release trigger) and retargets the
official fdroiddata recipe draft at v2.7.4, plus two recipe corrections
the fdroiddata CI caught on the v2.7.3 submission (MR !40967):
- Categories: 'Time' (retired in the current taxonomy) -> 'Calendar & Agenda'
- AutoUpdateMode: 'Version v%v' (invalid per schema) -> 'Version'
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Home-screen widgets were stuck on the Glance loading layout (a blank spinner)
in the minified release build — but worked in debug. Root cause: Glance renders
every widget through a WorkManager worker (androidx.glance.session.SessionWorker),
and WorkManager instantiates the InputMerger reflectively from the fully-qualified
class name persisted in the WorkSpec (Class.newInstance, no-arg ctor). Under R8
full mode (AGP 9 default) the unused no-arg constructor of
androidx.work.OverwritingInputMerger was stripped, so WorkManager threw
"OverwritingInputMerger has no zero argument constructor", the SessionWorker
never ran, and provideContent never executed — leaving the widget on its
initial loading layout forever.
Same R8-reflection family as the v2.7.0 Room keep-rule fix. Keep the name +
constructor of every androidx.work.InputMerger.
Verified on-device with the releaseTest build (R8-minified): the agenda widget,
which showed only a spinner before, now renders its content; the WM-InputMerger
InstantiationException is gone. Closes #18. Cuts v2.7.3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v2.7.1 is taken by the launch-crash fix (calendar observer registered before
the permission gate). Bump this held crash-reporting release to 2.7.2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The repository registers a ContentObserver on the calendar provider eagerly
in its init block, and an activity-scoped SettingsViewModel (which drives the
theme) injects that repository — so the @Singleton is constructed at launch,
above RootScreen's permission gate. On newer Android, registering an observer
on a provider you lack permission for throws SecurityException instead of
silently no-op'ing, so the app crashed instantly on every launch whenever
calendar access wasn't granted (fresh install or revoked permission), before
the permission screen could ever appear.
Guard the registration behind a calendar-permission check and re-attach the
observer lazily on the first calendars()/instances() read, which runs once the
gate opens and screens subscribe. Access to the observer collections is now
synchronized since registration can happen on the main thread (repo init) or
the IO dispatcher (query re-attach).
Verified on-device: permission-denied launch shows the permission screen
instead of crashing; granting it proceeds to the calendar with live updates.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump committed versionCode/versionName to 2.7.1 (20701) and move the
crash-reporting entry under a 2.7.1 CHANGELOG heading. The tag remains the
source of truth; CI derives the published version from it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The minified release build crashed on every launch before any UI:
Unable to get provider androidx.startup.InitializationProvider:
Failed to create an instance of androidx.work.impl.WorkDatabase
The home-screen widgets use Glance, which pulls in WorkManager and its
transitive Room database (room-runtime 2.2.5). Room 2.2.5's bundled keep
rule is `-keep class * extends androidx.room.RoomDatabase` — it keeps the
class but not its constructor. Under R8 full mode (AGP 9) the generated
WorkDatabase_Impl was reduced to a non-instantiable class, so Room's
reflective newInstance() threw InstantiationException at startup.
Add `-keep class * extends androidx.room.RoomDatabase { *; }` so the
generated *_Impl classes keep their constructors. Verified against the
rebuilt release APK: WorkDatabase_Impl is now PUBLIC FINAL with its
<init> present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes v2.7 Branch 2. Wires the import core into the app:
- Manifest ACTION_VIEW/SEND for text/calendar; MainActivity parses the
incoming Uri (content/file only, so calendula:// deep-links don't match)
and routes it through RootScreen → CalendarHost like the other one-shot
intents.
- ImportViewModel reads + parses the file and routes by count: one event →
the prefilled create form for review (EventEditViewModel.openImported,
which freezes the reminder default so the file's reminders win); many →
ImportScreen with a writable-calendar picker, then a bulk import (UID
dedup) and a result summary.
- ImportScreen also surfaces parser warnings (skipped recurrence overrides,
ignored attendees, unknown-timezone fallback). Strings EN+DE.
Package is ui.imports (not ui.import — Java keyword). lint + test +
assembleDebug green. No v2.7 tag until on-device review.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Branch 1 of 2 for v2.7 (the .ics topic). Adds the write side of a
hand-rolled RFC 5545 engine (zero deps, stays on kotlinx-datetime):
- domain/ics: IcsText (escape + 75-octet folding), IcsEvent model,
IcsWriter.writeCalendar. Timezone rule: all-day VALUE=DATE, one-off
timed UTC Z, recurring timed TZID-labelled from EVENT_TIMEZONE (no
VTIMEZONE — import resolves TZID against the OS tz db).
- Single-event share from the detail screen (FileProvider + ACTION_SEND).
- Whole-calendar backup of the writable local calendars to a SAF file
(Settings -> Calendars -> Export as .ics), one combined VCALENDAR.
- insertEvent now writes Events.UID_2445; legacy rows fall back to a
stable synthesised UID at export time so a later restore won't dupe.
- EXDATE / RECURRENCE-ID overrides are deliberately skipped this pass
(documented v1 limit; import will skip them too).
Engine + mapper unit-tested. Import (Branch 2, feat/ics-import) ships in
the same v2.7 release; no tag until both land + on-device review.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundles the unreleased Tier 2/3 work into one release:
- Home-screen widgets (Glance): an "Upcoming" agenda widget and a month-grid
widget, both reusing the in-app grouping/layout (groupAgendaDays,
layoutMonthWeeks) via a Hilt WidgetEntryPoint, honouring hidden-calendar
filters and refreshing on PROVIDER_CHANGED / date rollover.
- App shortcut: launcher long-press "New event", routed through the shared
WidgetNavRequest.Create channel into the create-event form.
- Agenda view and jump-to-date (already merged via #3/#4) are documented here
as part of the shipped version.
Bumps versionCode 20500 / versionName 2.5.0, moves the CHANGELOG Unreleased
section under [2.5.0], updates ROADMAP/STATE, and adds EN+DE strings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Optional per-event color in the event form. The read/render path already
resolved EVENT_COLOR with a calendar fallback; this adds the write side and
the picker.
- Palette-backed calendars (Google, some CalDAV) pick from the account's
Colors (TYPE_EVENT) and write EVENT_COLOR_KEY, so the color round-trips
through sync; local calendars write a raw EVENT_COLOR from the shared
CALENDAR_COLOR_PALETTE. Never writes a raw color to a palette calendar.
- Swatch row + palette extracted to ui/common/ColorSwatchRow.kt (shared with
the calendar editor). Switching calendars resets the choice (keys are
account-scoped); a "Reset" action returns to the calendar color.
- New "Allow colors on unsupported calendars" setting (off by default)
extends the raw path to no-palette synced calendars, with an honest
"may not survive sync" warning on the picker and in Settings.
- Color flows through insert / dirty-checked update / occurrence-exception;
mapper, form, and repository tests added.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One shared Material 3 grouped-list blueprint, modelled on the ReFra gallery app
and extracted to ui/common/GroupedList.kt: CollapsingScaffold (a LargeTopAppBar
whose large title collapses into the bar on scroll) and GroupedRow
(Position-based corner grouping so a run of rows reads as one rounded card, with
press-animated corners and selected/minHeight knobs).
Settings: restructured into a category hub (About card on top, version mark at
the foot) with sliding sub-pages for Appearance, the new-event form and
Notifications. Theme, week-start and language pickers migrated from DropdownMenu
to OptionCard dialogs; token-based icon chips. New ic_gitea.xml (Simple Icons,
verbatim path) for the About "Source" button; en+de strings.
Calendar manager: same collapsing scaffold + grouped rows; shared
CalendarColorChip (neutral chip with a pastelised calendar glyph) replaces the
bright colour swatch.
Navigation drawer: branded header, grouped View switcher (active view
highlighted via secondaryContainer), filter list restyled to grouped rows with a
trailing checkbox; the whole drawer now scrolls as one.
Cards use surfaceContainerHigh for readable contrast against surface. Version
bumped to 2.3.0 / 20300. UI-only; unit tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Day/week: tap an empty slot to open the create form prefilled with that
day and the tapped hour (snapped to the hour, 1 h long). Threaded a start
time through CalendarHost → EventEditScreen → openNew; the FAB keeps its
default.
Local calendars: a full-screen editor from Settings → Calendars to
create/rename/recolor/delete device-only calendars (ACCOUNT_TYPE_LOCAL,
sync-adapter insert) with name, pastel-previewed colour, and a description
(stored in CAL_SYNC1). Synced calendars are listed read-only grouped by
account, each with a "manage in source app" deep-link resolved from the
account's own authenticator (DAVx5/ICSx5/…), plus an add-account shortcut;
a <queries> block makes the source apps launchable. Extracted a shared
InlineTextField into ui.common so the event form and calendar editor share
one borderless input style.
Tests: repository delegation + write-failure, mapper isLocal/description,
fake data source extended. Version bumped to 2.2.0 / 20200.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the per-day dot summary with an event-rich grid. The ViewModel now
splits the grid into week rows and, per row, resolves all-day/multi-day
events into spanning bars (reusing the week view's layoutAllDay lane math)
and single-day timed events into per-day pills.
The grid renders as an overlay: each day gets a rounded surfaceContainer
background (matching the week/day views), spanning bars draw on top so a
multi-day event is one connected bar bridging the cells it covers, and
single-day pills fill the lane slots no bar occupies on that specific day
(top-most first) so a bar-free day isn't pushed down. Up to three rows
show per day, then a "+N" dot row. Today is a filled circle on its number;
neighbour-month days are dimmed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The slide-out panel gains a "View" section mirroring the top-bar switcher
pill: three NavigationDrawerItems (Month/Week/Day) with the current view
highlighted; tapping one selects that view and closes the drawer. The pill
stays as-is for quick cycling.
Centralise each view's label + icon as labelRes/icon extensions on
CalendarView so the pill and the drawer share one mapping. The drawer's
"Today" jump is dropped — the top-bar Today action and error-state retry
still cover it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The event form's state pipeline ran .flowOn(io) over the whole combine,
including the _form round-trip every keystroke depends on. That async hop
handed BasicTextField a lagging value while typing, so Compose kept
correcting the cursor to the stale position.
Scope flowOn(io) to just the calendar/prefs/settings reads and collect the
form -> state -> UI path on the main dispatcher, so keystrokes round-trip
synchronously and the cursor stays put.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Version bumped to 2.0.0 / 13. No code changes beyond the version — 2.0.0
closes out Milestone 2 (write support, v1.1 through v2.0): the final slice
is the save-conflict dialog (external change → overwrite/discard, external
delete → informational close), plus the store refresh: descriptions and
README describe write support and reminders, and fastlane screenshots
(DE+EN, six each) ship for F-Droid. CHANGELOG [2.0.0] carries the details.
Quick-add was cut from scope (the prefilled form covers it); calendar
switching while editing moved to the v3 backlog. Both documented in the
roadmap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
No locking (plan 03, decision 5): openForEdit keeps an EditSnapshot — the
prefilled form plus the raw Events-row times, which the form itself can't
see (it derives its times from the tapped occurrence, so an externally
moved event would otherwise stay invisible). Right before writing,
performSave re-reads the event and compares snapshots: a mismatch parks
the save in SaveUiState.AwaitingConflict carrying the already-chosen
recurring scope, and the dialog offers overwrite / discard / cancel
(OptionCard style). Overwrite still writes only dirty fields, so external
changes to untouched fields survive either way. A deleted event lands in
SaveUiState.Gone — an informational dialog that closes form and detail.
Fields the form can't write (attendees, status, self response, reminder
methods) are excluded from the comparison so sync noise can't fake a
conflict. The load-time zone is pinned in the EditTarget so a device
timezone change mid-edit can't either.
Store metadata: F-Droid descriptions (DE+EN) and the README stop claiming
read-only and now describe write support and reminder delivery. New
fastlane phoneScreenshots (6 per locale: week/month/day/detail/form/
reminder onboarding), captured on-device against demo-only calendars.
Tests: EditSnapshot equality (unchanged event, field change, row-time move
the form can't see, non-writable changes stay quiet).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Version bumped to 1.4.0 / 12. No code changes beyond the version — 1.4.0 is
the reviewed-and-approved reminder slice: the EVENT_REMINDER receiver posting
due CalendarAlerts on a dedicated channel, tap-to-detail, the one-time
onboarding step requesting POST_NOTIFICATIONS with the duplicate-reminders
warning, and the Settings mirror. CHANGELOG [1.4.0] carries the details.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Calendula now posts event reminders itself (the Etar model): the provider
schedules the alarms and broadcasts EVENT_REMINDER, but a calendar app must
turn them into visible notifications — essential for users whose only
calendar app this is. A manifest-registered, exported receiver (data scheme
content://com.android.calendar) wakes us at reminder time; no foreground
service, no own alarm scheduling.
Delivery path (data/reminders/): EventReminderReceiver (Hilt, goAsync) →
ReminderAlertStore queries CalendarAlerts for STATE_SCHEDULED rows with
ALARM_TIME <= now → ReminderNotifier posts one notification per alert on a
dedicated high-importance channel, then best-effort marks rows FIRED
(needs WRITE_CALENDAR; without it a re-broadcast silently replaces — tag
per alert + setOnlyAlertOnce). Swiped notifications never return: FIRED
rows are never re-queried, so no dismiss-intent machinery. Research
(AOSP CalendarAlarmManager): the provider creates alert rows only for
METHOD_ALERT reminders, so the email-reminder filter happens upstream.
Tapping opens the event's detail screen: MainActivity is singleTop now,
parses eventId/begin/end extras (onCreate + onNewIntent) into Compose
state, and CalendarHost consumes the key exactly like an event tap.
Onboarding gained a one-time second step after the calendar grant (shared
OnboardingScaffold extracted from PermissionScreen): explains delivery,
warns that a second calendar app with notifications on duplicates
reminders, requests POST_NOTIFICATIONS (dialog on API 33+ only; minSdk 29).
"Not now" turns the feature off; reminders default ON. Settings mirrors
the toggle in a new Notifications section with the duplicate hint, and
re-requests the permission when enabling. Strings DE+EN.
Deliberately deferred (roadmap): snooze/dismiss actions, BOOT_COMPLETED /
exact-alarm scheduling, battery-exemption prompts.
Tests: reminderTimeText (all-day UTC-midnight reading, exclusive end day,
midnight-crossing ranges), reminders/onboarding pref round-trips.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Version bumped to 1.3.0 / 11. No code changes beyond the version — 1.3.0 is
the reviewed-and-approved edit slice: shared form for editing, scope-at-save
for recurring events (this / this and following / all, exception rows and
series splits), three-way recurring delete, simple recurrence picker with
weekly weekday toggles, and the stale-instances split fix. CHANGELOG [1.3.0]
carries the details.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Version bumped to 1.2.1 / 10. No code changes beyond the version — 1.2.1 is
the reviewed-and-approved form polish: card design system, optional fields
with settings defaults, reworked reminders, OptionCard dialogs app-wide,
expressive theme on standard springs, direction-aware today jump, IME fix.
CHANGELOG [1.2.1] carries the details.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>