release: v2.13.0 — special dates, custom fonts, quick-switch, es/it translations #58
Reference in New Issue
Block a user
Delete Branch "release/v2.13.0"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Publishes v2.13.0 (versionCode 21300). Merging this mints the tag via the release pipeline.
Added
Fixed
Release checklist
🤖 Generated with Claude Code
Saving "only this event" on a recurring event inserts a modified-occurrence exception at Events.CONTENT_EXCEPTION_URI. buildOccurrenceExceptionValues set the occurrence's end as DTEND, but the provider rejects that outright — CalendarProvider2.checkAllowedInException throws IllegalArgumentException: Exceptions can't overwrite dtend so the insert failed, the save surfaced as SaveUiState.Failed, and the edit screen reappeared with no change applied. "This and following" and "all events" never go through the exception insert, which is why only "only this event" broke (Codeberg #16, verified on a Pixel 10 / Android 16). An exception is a single instance whose end the provider derives from DTSTART + DURATION (clearing the inherited RRULE itself), so carry the length as DURATION and drop DTEND — the same shape AOSP Calendar/Etar use. Verified on-device: editing one occurrence now applies to just that occurrence and leaves the rest of the series untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>A dedicated sub-page under Settings drives the mirror: - master enable toggle — requests READ_CONTACTS contextually (only here, never at startup), runs an immediate sync on enable, and on disable confirms then tears the managed calendars down; - per-type toggles (Birthdays / Anniversaries / Other), each confirm-guarded since turning one off deletes its calendar; - an editable title template ({name}/{age}) and a Show-age toggle; - a "Sync now" row with the last-run time, a paused/permission banner with a re-grant button, and a pointer to per-calendar colour/visibility/reminders. Exposed via a separate SpecialDatesUiState flow (the main settings combine is already at capacity); the ViewModel gained the engine/scheduler wiring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>- German translations for the special-dates sub-page, the managed-field editor hint, and the calendar/title strings. - Store listing (en/de): a paragraph on the optional, on-device, one-way contact special-dates feature. - Soften the calendar-permission rationale ("all it asks for up front") now that an optional contacts permission exists, without weakening the privacy promise. - CHANGELOG entry for #15. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>A FREQ=YEARLY event has one static title, so {age} is only a sync-time snapshot and can look wrong on far-future occurrences. Keep it out of the default templates ({name}'s birthday / anniversary), and when a user does add {age} in the title-format editor, show a short disclaimer about the snapshot behaviour. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>A FREQ=YEARLY event has one static title, so a per-occurrence age is impossible without heavy per-year exception rows. Replace {age} with {year} — the birth year (or an anniversary's start year) — which is static and correct on every occurrence, and shows everywhere (widgets, other apps, exports). - renderSpecialDateTitle substitutes {year}; drop the age snapshot computation and the sync `today` parameter. - Default templates become "{name}'s birthday ({year})" / "…anniversary ({year})". - Rename the setting to "Show year" (prefs specialDatesShowYear) and remove the now-unneeded {age} snapshot disclaimer in the template editor. - Update tests, CHANGELOG and the design-doc note. lint + unit tests + assembleDebug green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>The mirror keyed each event on (contact, type) only, and the read deduped on that same pair from an unordered query. So a contact with two custom dates ('Wedding', 'Graduation') mirrored just one — and, the winner being order-dependent, the single event could ping-pong between the two dates across syncs. A Custom date's UID now carries a discriminator (its label, else its month-day), the read dedups on the full UID, and the query is ordered by Data._ID so a genuine duplicate resolves the same way every time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>