Secretary

Apps/calendar/changelog
Login

Apps/calendar/changelog

Secretary Calendar (Android) - Changelog

Every released version of the Secretary Calendar app, newest first.

App documentation: Apps/calendar

Status

Status 2026-10-01 (100)

Secretary Calendar 100 (versionCode 100): die Sync-Aktion ist ohne Konto weg, Passwortfelder zeigen Klartext. Beides sitzt im gemeinsamen Code und trifft damit alle acht Clients.

(1) Keine Sync-Aktion ohne Konto. AppHeader (Kit.kt) blendet die Sync-Aktion bereits aus, wenn onSync null ist - das war die vorhandene Konvention, nur wurde sie an keiner Stelle benutzt. OfflineBanner (StatusBanners.kt) verlangte dagegen einen nicht-nullbaren onSync und zeichnete den Refresh-Button bedingungslos; der Parameter ist jetzt (() -> Unit)?, und ohne Konto fehlt der Knopf (die Meldung "offline" bleibt stehen). Alle Aufrufstellen reichen den Callback nur noch durch, wenn wirklich ein Konto existiert: loggedIn der jeweiligen ViewModel, im Kalender der collected isLoggedIn-StateFlow. Das Journal ist der einzige Client, bei dem das sichtbar war - es hat keine Login-Sperre und laeuft im lokalen Modus (localMode = !repo.isConfigured()) ohne Konto weiter, zeigte den Sync-Knopf aber trotzdem. Beim Secrets-Tresor war der Knopf vorhanden und nur deaktiviert (enabled = state.connected); er wird jetzt gar nicht erst gezeichnet, der Spinner waehrend state.syncing bleibt. Am Launcher aendert sich nichts: die Server-Sync-Sektion im Settings-Screen war schon an account != null gehaengt.

(2) Passwortfelder zeigen Klartext. Alle Eingabefelder fuer Passwoerter nehmen VisualTransformation.None; die Tastatur bleibt KeyboardType.Password. Betroffen sind der gemeinsame Login-Screen (SecretaryLoginScreen), die Journal-Passwortdialoge (PasswordDialogs: Setzen, Aendern, Abfragen) und das Entschperren (UnlockScreen) sowie im Tresor die Felder fuer Recovery-Passphrase, Entschperr-Passphrase, KDBX-Import und KDBX-Export (SecretsApp) und das Passwortfeld im Eintrags-Editor (Browse). Anlass war die Darstellung, nicht die Sicherheit: die Maske besteht aus U+25CF (●), und die gebundelte Typewriter-Schrift Special Elite deckt dieses Zeichen nicht ab - die Felder zeigten unlesbare Ersatzglyphen. In den Tresor-Dialogen ist der Sichtbarkeits-Umschalter damit ueberfluessig und mit ihm die Felder passwordVisible/visible, der Eye-Button bzw. der Textbutton Anzeigen/Verbergen und die Visibility/VisibilityOff-Imports weg. Der Reveal-Pfad fuer gespeicherte Secrets in Browse (FieldRow) bleibt unberuehrt: das ist eine Anzeige, keine Eingabe.

Tests: keine neuen, kein JVM-Verhalten geaendert. 662 Tests gruen (Kalender 55, common 100, Kontakte 44, Launcher 12, Secrets 405, Tasks 46), verifiziert ueber gradle test; alle neun Module kompilieren (compileDebugKotlin, offline), und die Neuuebersetzung meldet in keiner der geaenderten Dateien eine Warnung. versionCode 99 -> 100.

Status 2026-09-30 (99)

Secretary Calendar 99 (versionCode 99): keine Code-Aenderung - der Quelltext ist derselbe wie bei 98. Veraendert wurde in diesem Zug nur der gemeinsame SecretarySettingsScreen in common (SRS common 1.3.0), den auch Calendar traegt: neue Versionszeile am Ende (aus der PackageInfo des eigenen Pakets) und die Namensraum-Karte raus, die hier ohnehin nie benutzt wurde - die Karte Sammlungspräfix bleibt, weil der Wert dort konfigurierbar ist. Am Calendar-Code aendert sich keine Zeile. Die Nummer geht trotzdem hoch, weil ein Release alle acht Clients und beide Serverpakete zusammen ausliefert: das Buildsystem ueberspringt einen Lauf, dessen Zieldateinamen alle bereits im Archiv liegen, und ein gleichnamiges Paket mit gleichem Inhalt waere auf dem Geraet nicht von 98 zu unterscheiden - bei einer APK zusaetzlich ein versionCode, den ein Geraet nicht neu installiert. versionCode 98 -> 99.

Status 2026-09-30 (98)

Secretary Calendar 98 (versionCode 98): keine Code-Aenderung - der Quelltext ist derselbe wie bei 97. Veraendert wurde in diesem Zug nur der Launcher (Korrektur am Konfigurations-Sync, dort 460). Die Nummer geht trotzdem hoch, weil ein Release alle acht Clients und beide Serverpakete zusammen ausliefert: das Buildsystem ueberspringt einen Lauf, dessen Zieldateinamen alle bereits im Archiv liegen, und ein gleichnamiges Paket mit gleichem Inhalt waere auf dem Geraet nicht von 97 zu unterscheiden - bei einer APK zusaetzlich ein versionCode, den ein Geraet nicht neu installiert. Server und check-links gehen mit auf 0.24.0. versionCode 97 -> 98.

Status 2026-09-29 (97)

Secretary Calendar 97 (versionCode 97): Statisches App-Symbol + Header ohne Doppel-Inset. Das Launcher-Symbol ist jetzt ein normales Adaptive-Icon wie bei den übrigen sieben Apps: [##] in Special Elite, weiß auf der Kalender-Akzentfarbe #1A6B52 (drawable/ic_launcher_background.xml

Status 2026-09-16 (89)

Secretary Calendar 89 (versionCode 89): Monats-Raster entfernt – die fortlaufende Tagesliste ist jetzt die einzige Hauptansicht. Das zwischen Liste und Monats-Raster umschaltbare Raster (Hoch-Wischen) ist vollständig entfernt: showGrid-Zustand und der Wisch-Geste-Toggle (CalendarListScreen), die bedingte Raster-Renderung mit CalendarMonthGrid, die Komponenten CalendarMonthGrid/DayCell (GRID_ROWS/GRID_COLS-Konstanten) sowie die zugehörigen Raster-Einstellungen: Sektion „Raster" im SettingsScreen (ColorSelect für Heute/Auswahl, Titelgröße, Zeilenumbruch) und CalendarRepository.gridTodayColor/gridSelectedColor/gridTitleSize/ gridLineBreak + save*-Setter. Die Horizontal-Wisch-Navigation (previousMonth/nextMonth) bleibt erhalten (verschiebt das Listenfenster um den Monat ±12 Monate) und der Monogramm-Tipp scrollt weiterhin zur heutigen Zeile; die nicht mehr benötigten Imports (sp, luminance, TextAlign, KeyboardArrowLeft/Right, DateRange, Tune, Switch, CalendarRepository in CalendarListScreen) sind entfernt, der Übersichts- und Listenaufbau ist unverändert. versionCode 88 -> 89.

Status 2026-09-16 (88)

Secretary Calendar 88 (versionCode 88): Offline-Sync-Engine (secretary-common-android v0.30.0). backgroundSync und refreshFromServer nutzen jetzt pro Kalender die gemeinsame OfflineSyncEngine (CrudItemRemote

Status 2026-09-13 (87)

Secretary Calendar 87 (versionCode 87): Kategorie-Parsing gehärtet (secretary-common-android v0.26.0). VEventParser schreibt CATEGORIES jetzt RFC-5545-escaped (Komma in einem Kategorie-Wert als \,) und liest die Liste nur noch an unescaped-gespeicherten Kommas; jedes Token wird dabei dekodiert. Vorher wurde bei jedem Komma geschnitten – eine Kategorie, deren Value ein Komma enthielt (z. B. RFC-escapte \,-Werte aus importierten .ics-Dateien), wurde also in zwei Kategorien zerrissen. Bestehende, von den alten Writern nie escapte Daten werden identisch gelesen. versionCode 86 -> 87.

Status 2026-09-12 (83)

Secretary Calendar 83 (versionCode 83): Terminliste als fortlaufende, typografische Tagesliste (Standardansicht) – mit Vergangenheit, Wortumbruch und „heute"-Navigation. Die Hauptansicht startet jetzt in der Liste statt im Monats-Raster (Hoch-Wischen togglet weiterhin zwischen Liste und Raster). Die Liste ist eine kontinuierliche Tagesliste im list.txt-Format: Wochentag TT\tTermin…, sie beginnt am ersten Tag des 12. Vormonats des angezeigten Monats und läuft bis zum Ende des 12. Folgemonats – Scrollen in Vergangenheit und Zukunft funktioniert frei. Monatsnamen erscheinen natürlich in der Liste und bleiben beim Scrollen oben pinnen (stickyHeader). Jedes Monatsband ist halbton-verschattet (alternierende ISO-Wochen, Wochenende-Stempel in Fehlerfarbe, Heute mit *-Marker + Unterstreichung in Akzentfarbe). Termine: Wortumbruch mit automatischen Zeilenumbrüchen (keine Ellipsen mehr); mehrere Termine pro Tag liegen als mehrere Zeilen untereinander, der Tages-Stempel („Sa 12") erscheint nur einmal pro Tag (oben, auf Höhe der ersten Eintragszeile); der Farbbalken zieht sich über die volle Eintragszeile (IntrinsicSize). Beim Öffnen und bei jedem Tipp auf das Monogramm (Tageszahl) im Header scrollt die Liste programmatisch zur heutigen Zeile (rememberLazyListState#scrollToItem, Index = vergangene Tage + Monatsköpfe). Monats-Köpfe und Tagesstempel nutzen die Typewriter-Schrift Special Elite. Umsetzung im CalendarViewModel: listEventsByDay (Occurrence-Expansion über das ganze Listenfenster inkl. Serien/All-Tag-Terminen), listStart/listEnd (Monat ± 12 Monate, LIST_PAST_MONTHS/LIST_MONTHS_AHEAD); CalendarListScreen rendert pro Tag eine Zeile (leere Tage nur Stempel) mit dem bestehenden Langdruck-Kontextmenü, Antippen des Tages öffnet die Tagesansicht. versionCode 83 == versionName 83.

Status 2026-09-12 (85)

Secretary Calendar 85 (versionCode 85): System-Integration wie der LineageOS-Kalender: "Termin hinzufügen"-Intents + Terminerinnerungen (EVENT_REMINDER + Benachrichtigungen). Der Kalender beantwortet jetzt die Standard-Kalender-Intents, die andere Apps (und die System-Kalender-App) senden, und zeigt Terminerinnerungen als Benachrichtigung. Intents (analog zum Etar-Manifest des LineageOS-Kalenders): neue Filter auf MainActivity + neuer Dispatcher handleCalendarIntent – ACTION_INSERT/ ACTION_EDIT auf vnd.android.cursor.dir/event und vnd.android.cursor.item/event ("Termin hinzufügen" aus Drittanbieter-Apps mit Titel/Ort/Beschreibung/ BeginTime/EndTime/allDay; bei item-URIs wird der vorhandene Termin im Cache aufgelöst und geöffnet), ACTION_VIEW auf vnd.android.cursor.item/event (Termin aus einer URI öffnen, z. B. Tipp auf eine Erinnerung), ACTION_VIEW auf time/epoch (content://com.android.calendar/time/<epoch> springt zur Tagesansicht des Datums), zusätzliche ICS-Mime-Typen text/x-vcalendar + application/ics, Kategorie APP_CALENDAR auf dem Launcher-Intent. Umsetzung: MainActivity (createEventFromInsert/openEventFromUri – ContentUris.parseId, resolve über CALENDAR_ID→NAME→Cache), CalendarViewModel (pendingEditorEvent/pendingFocusDate-Endpunkte, requestNewEventFromInsert, openEventFromExternal, consume…), NavGraph (Login-geschützte LaunchedEffects navigieren zu EDIT bzw. day/{date}). Terminerinnerungen: der Sync-Adapter spiegelt die VALARM-Erinnerungen als CalendarContract.Reminders-Zeilen (mit caller_is_syncadapter, da Konto- Zeilen schreibgeschützt sind; Mapping NOTIFICATION→METHOD_ALERT, EMAIL→ METHOD_EMAIL, SMS→METHOD_SMS) und setzt Events.HAS_ALARM; der neue system/AlertReceiver (exportiert, Filter EVENT_REMINDER + BOOT_COMPLETED + MY_PACKAGE_REPLACED) zeigt die fällige Erinnerung als Benachrichtigung (Channel event_reminders, Kalenderfarbe, Zeit/Ort; Tipp öffnet den Termin via item/event-Intent) und stößt nach Boot den System-Sync neu an (der Provider plant die Wecker selbst neu). Neue Berechtigungen POST_NOTIFICATIONS (wird zur Laufzeit zusammen mit den Kalender-Berechtigungen erfragt), RECEIVE_BOOT_COMPLETED, VIBRATE. Weiterhin bewusst nicht übernommen (Out-of-Scope): Google- event-Deeplinks, Widgets, globale Suche und PROVIDER_CHANGED-Receiver (Schleifen-Risiko mit dem eigenen Sync-Adapter). versionCode 85 == versionName 85.

Status 2026-09-12 (86)

Secretary Calendar 86 (versionCode 86): Alle verbliebenen Etar-/LineageOS- Integrationen – exakte Wecker, Schummern + Schnellantwort, globale Suche, App-Widget, App-Shortcuts + dynamische Icons, Batterie-Optimierungs-Ausnahme (alles aus dem Etar-Manifest außer Google-Calendar-Deeplinks). Exakte Wecker: neuer system/AlarmScheduler plant die VALARM-Erinnerungen als setExactAndAllowWhileIdle-Wecker (bei entzogener SCHEDULE_EXACT_ALARM- Berechtigung API 31+ Fallback auf 10-Minuten-setWindow); Wiederholungen über Recurrence.nextAfter, UID→System-_id per SYNC_DATA3; Re-Arm bei jedem Sync, nach BOOT_COMPLETED/MY_PACKAGE_REPLACED/TIME_SET/ LOCALE_CHANGED und nach jedem Auslösen; Cancel/Re-Arm über eine alarm_schedule-Präferenz (JSON) mit RequestCode-Mapping. Schummern + Schnellantwort: AlertReceiver beantwortet zusätzlich ACTION_FIRE (intern) und ACTION_SNOOZE; die Benachrichtigung hat jetzt die Aktionen Schummern (10 min, eigener Wecker) und Antworten → alerts/QuickResponseActivity (Dialog mit Teilnehmer-E-Mail-Adressen, Tipp versendet die Einladung als .ics per E-Mail über den FileProvider). Globale Suche: search/SearchActivity (per android.app.default_searchable

Status 2026-09-09 (80)

Secretary Calendar 80 (versionCode 80): Alte Kalender-Aktivierung entfernt (kein doppelter Übersichts-Bereich mehr). Die Einstellungen hatten nach v79 zwei Listen, die sich auf der Übersicht überlagerten: der alte „Kalender"-Block (aktivieren/deaktivieren, welche Sammlungen die App überhaupt übernimmt) und der neue „Übersicht"-Block („In Übersicht anzeigen"). Die alte Sammlungs- Aktivierung ist obsolet (die Übersicht steuert jetzt per Kalender, was eingemischt wird; deaktivieren hieße nur noch „nicht angezeigt"). Entfernt: CalendarChecklistSection aus dem SettingsScreen, im CalendarViewModel die Zustände _disabledCalendarIds/disabledCalendarIds, toggleCalendarEnabled und die Disabled-Logik in deleteCalendar, sowie CalendarRepository.enabledCalendarIds/saveEnabledCalendarIds (Präferenzschlüssel enabled_calendars wird nicht mehr gelesen; vorhandene Einträge sind wirkungslos). applyEnabledFilter übernimmt jetzt einfach alle bekannten Kalender; enabled-Flag der Collections bleibt (System-Sync VISIBLE/SYNC_EVENTS) immer aktiv. Die Einstellungen zeigen nur noch die Sektion „Übersicht". versionCode 80 == versionName 80.

Status 2026-09-09 (79)

Secretary Calendar 79 (versionCode 79): Übersicht-Filter + Kalender- Farben + Spezialsammlungen in secretary-common-android v0.20.0. In den Einstellungen gibt es jetzt eine neue Sektion „Übersicht": pro aktivem Kalender eine Checkbox „In Übersicht anzeigen" (overview_excluded_calendars -CalendarRepository, _overviewExcludedIds im CalendarViewModel, toggleIncludeInOverview). Ausgeschaltete Kalender bleiben im Kalender-Editor und in der Synchronisation, werden aber nicht mehr in die zusammengefügte Übersicht gemischt (loadEvents, backgroundSync und refreshFromServer nutzen das neue overviewCalendars()-Filter). Kalender-Farben: die Kalenderlisten-Karte hat jetzt im Langdruck-Menü den Eintrag Farbe (ergänzt um den gemeinsamen onColor-CollectionContextMenu-Parameter), der den gemeinsamen ColorSelectDialog öffnet (Palette + „Keine Farbe"); setCalendarColor setzt die Farbe via CrudManager.setCollectionColor (PROPPATCH calendar-color, Apple-Namespace) mit Offline-Fallback (setCollectionColorOffline → GAction.SetCollectionColor in der Write-Behind-Queue). PROPFIND liefert calendar-color jetzt mit und färbt die Kalender-Akzentfarbe + Termin-Balken entsprechend. Der Übersicht-Eintrag nutzt SpecialCollections.OVERVIEW_LABEL aus dem gemeinsamen Code. versionCode 79 == versionName 79.

Status 2026-09-09 (78)

Secretary Calendar 78 (versionCode 78): Fix: Start-Crash (NullPointerException). Beim App-Start stürzt die App sofort mit NullPointerException: Attempt to invoke interface method 'void MutableStateFlow.setValue(Object)' on a null object reference in CalendarViewModel.recomputePending (Zeile 323) ab. Ursache: Der init-Block startet viewModelScope.launch { loadStartupCache() }, das auf dem Main-Thread (Dispatchers.Main.immediate) synchron innerhalb des Konstruktors ausgeführt wird; loadStartupCache → recomputePending schrieb in _pendingCollectionIds, das erst unterhalb des init-Blocks deklariert war und zu diesem Zeitpunkt noch null ist. Fix: Die _pendingCollectionIds-/pendingCollectionIds-Deklaration in den View-State-Block vor dem init-Block verschoben (alle übrigen StateFlows, die der Startpfad berührt, liegen darunter — scan der nachfolgenden Felder ergab keine weiteren Kandidaten). versionCode 78 == versionName 78.

Status 2026-09-09 (77)

Secretary Calendar 77 (versionCode 77): Kalender-Sammlungen: Langdruck- Kontextmenü + Anlegen (secretary-common-android v0.19.0). Die Kalender-Auswahl (CollectionsScreen) zeigt alle aktiven Kalender als Karten mit Long-Press-Kontextmenü (CollectionContextMenu): Umbenennen / Kopieren / Löschen. Neue CalendarViewModel-Aktionen createCalendar/renameCalendar/copyCalendar/deleteCalendar über den gemeinsamen CrudManager (offline via Write-Behind-Queue, „<Name> (Kopie)" inkl. aller Termine), dazu pendingCollectionIds-Marker (PendingSync.snapshotState) und collectionBusy-Guard gegen überlappende Operationen. Neuer FAB „Kalender" legt eine neue Kalender-Sammlung an (CreateCollectionDialog); umbenennen/löschen über die gemeinsamen Dialoge. Die Übersicht-Zeile („Übersicht", Anzahl der Ereignisse) bleibt ohne Kontextmenü wie bisher. versionCode 77 == versionName 77.

Status 2026-09-09 (76)

Secretary Calendar 76 (versionCode 76): Rebuild auf secretary-common-android v0.18.0. Kein App-Code geändert; der APK enthält jetzt die 412-Auto-Recovery (bedingte PUTs mit veraltetem If-Match-ETag holen einmalig die aktuelle Server-ETag und wiederholen den Schreibvorgang statt des „Notiz wurde auf dem Server geaendert"-Fehlers). versionCode 76 == versionName 76.

Status 2026-09-08 (75)

Secretary Calendar 75 (versionCode 75): Konfigurierbares Kontextmenü. Die Aktionen Teilen / Kopieren / Verschieben / Löschen des Langdruck-Menüs lassen sich in den Einstellungen (gemeinsame Sektion „Langdruck-Kontextmenü" aus secretary-common-android v0.16.0) per Checkbox aktivieren/deaktivieren (UiPreferences, Default: alle aktiv). ItemContextMenu blendet deaktivierte Aktionen aus und sperrt das Menü komplett, wenn alle vier aus sind (dann verhält sich die Karte wie eine gewöhnliche klickbare Karte). versionCode 75 == versionName 75.

Status 2026-09-08 (74)

Secretary Calendar 74 (versionCode 74): Gemeinsames Kontextmenü für Termine (Kopieren/Verschieben/Teilen/Löschen). Termine in der Terminliste (AppointmentList, CalendarListScreen) und der Tagesansicht (DayScreen) haben jetzt das gemeinsame Langdruck-Kontextmenü (ItemContextMenu aus secretary-common-android v0.15.0) statt des einzelnen Kopier-Icons: Bearbeiten (Editor), Teilen (ICS via Share-Sheet, bestehende shareEventAsIcs), Kopieren (sofortiges Duplikat in derselben Kalender-Collection via CrudManager.copyItem/copyItemOffline – neue UID + neuer href), Verschieben… (MoveCollectionDialog, nur bei mehreren Kalendern; CrudManager.moveItem/moveItemOffline mit Recurrence-Cache- und System-Sync-Refresh) und Löschen (ConfirmDeleteDialog). Kopfzeile und FAB bleiben unverändert. Version 74, versionCode 74 == versionName 74.

Status 2026-09-08 (73)

Secretary Calendar 73 (versionCode 73): Fix: Übersicht leert sich nach Sync (calendarId war immer leer). CalendarCodec.decodeItem (secretary-common-android) setzte die calendarId von geladenen Terminen nie – alle Events liefen mit calendarId="". Damit greifen in der Übersicht beide Filter der Refresh-Schleife ins Leere: removeAll { calendarId == cal.id } entfernt nichts (Termine werden doppelt eingefügt, 1220 → 2409/2440), und das Abschluss-Pruning filter { calendarId in known } verwirft alles (Übersicht springt auf 0 Termine, ohne laufende Berechnung zu zeigen – genau der gemeldete Effekt „nach Sync 0 Termine, kein Rechen-Hinweis"). Fix: CalendarCodec.decodeItem leitet die calendarId jetzt aus dem Resource-Href ab (/calendars/calendar-main/x.ics → calendar-main); damit matchen Dedupe und Pruning korrekt, die Übersicht bleibt gefüllt und heilt sich beim nächsten Sync (Cache-Einträge mit leerer calendarId werden durch getaggte ersetzt). Zusätzlich bekam der Unit-Test VEventTest die fehlenden Imports (Attendee, Reminder, ReminderMethod, EventStatus, BusyFree – die Tests kompilierten deshalb nie) plus Regressionstest decodeItemSetsCalendarIdFromHref. Alle 55 Unit-Tests grün. versionCode 73 == versionName 73.

Status 2026-09-07 (70)

Secretary Calendar 70 (versionCode 70): Aufgeräumter Header ohne Status-Chip. Der moderne Haupt-Header (ModernAppHeader in secretary-common-android) zeigt keinen Online-Status-Chip mehr; die Aktionsleiste (Sync, Kalender, Einstellungen) sitzt jetzt rechts oben direkt im Hero-Header. Die Kalender-Schaltfläche erscheint nur, wenn mehrere Kalender vorhanden sind. Alle Überschriften verwenden die neue Typewriter-Schrift Special Elite (gebündelt in secretary-common-android, res/font/special_elite.ttf, Apache 2.0). versionCode 70 == versionName 70.

Status 2026-09-07 (69)

Secretary Calendar 69 (versionCode 69): Kalender-Auswahl in den Einstellungen. Unter Einstellungen gibt es jetzt eine Checkliste aller auf dem Server gefundenen Kalender-Sammlungen. Jede Sammlung kann per Checkbox aktiviert/deaktiviert werden: aktive Kalender erscheinen in der Kalenderliste (CollectionsScreen) und werden in die „Übersicht" gemischt, deaktivierte werden aus Liste und Übersicht ausgeblendet und nicht mehr synchronisiert. Umsetzung: CalendarRepository persistiert die deaktivierten Ids (enabled_calendars), der CalendarViewModel führt die gefundenen Sammlungen als allCalendars und die aktivierten als calendars (applyEnabledFilter), nur aktive Kalender werden geladen/abgefragt (backgroundSync, refreshFromServer); neue toggleCalendarEnabled(calendarId, enabled) ist für die UI gedacht; wird der aktuell gewählte Kalender deaktiviert, springt die Ansicht zur Übersicht zurück. Neue CalendarChecklistSection in SettingsScreen (klassisch + modern). versionCode 69 == versionName 69.

Status 2026-09-07 (68)

Secretary Calendar 68 (versionCode 68): Fix: Termine verschwinden beim Öffnen und kommen ~30 s später wieder. Beim App-Start lief backgroundSync() (bzw. bei manueller Aktualisierung refreshFromServer()) in der Übersichtsansicht: die kombinierte Event-Liste wurde in jedem Durchlauf mit leerer Liste neu aufgebaut und nach jedem abgeschlossenen Kalender-Einzelfetch als setEvents(all) veröffentlicht. Dadurch ersetzte der erste Kalender die vollständig geladene Cache-Ansicht, und alle Termine der noch nicht gefetchten Kalender verschwanden, bis deren Fetch (≈30 s) fertig war – „mehr als die Hälfte der Einträge verschwindet beim Öffnen und kommt später wieder, ohne Info was die App macht". Fix in CalendarViewModel: Die aktuelle Liste wird als Basis übernommen (statt leere Liste) und die frisch geladenen Termine je Kalender in-place ersetzt bzw. ergänzt (removeAll { calendarId == cal.id } + addAll), sodass Einträge laufender Fetches sichtbar bleiben; nach dem Durchlauf werden nur noch verwaiste Termine nicht mehr vorhandener Kalender entfernt. Zusätzlich setzt backgroundSync() jetzt _isLoading, sodass der Banner „Kalender werden synchronisiert…" angezeigt wird (vorher kein Hinweis, dass ein Hintergrund-Sync läuft). versionCode 68 == versionName 68.

Status 2026-09-07 (67)

Secretary Calendar 67 (versionCode 67): Spiegel-Kalender geschützt. Der Sync-Adapter löscht Kalender nicht mehr, die den geschützten Namen der Spiegel-Kalender secretary-tasks / secretary-journal tragen (SystemCalendarMirror.PROTECTED_NAMES in secretary-common-android). Damit werden die von den Apps Secretary Tasks und Secretary Journal automatisch gepflegten Kalender nie fälschlich mit entfernt. versionCode 67 == versionName 67.

Status 2026-09-03 (66)

Secretary Calendar 66 (versionCode 66): Monats-Raster ohne experimentelle Karussell-Navigation. Der zwischenzeitlich ausgebaute experimentelle Karussell-Code wurde wieder entfernt – die Dateien CalendarCarousel.kt und HorizontalDayTimeline.kt wurden gelöscht und alle betroffenen Quelldateien auf den Monats-Raster-Stand von v56 zurückgesetzt (echtes Revert, kein Rebuild): CalendarListScreen.kt (Monats-Raster mit CalendarMonthGrid/DayCell, 6×7-Tage-Gitter Mo–So, heute/ausgewählt hervorgehoben, Raster-Farben/Textumbruch aus den Einstellungen, Quer-Wischen blättert den Monat), SettingsScreen.kt, CalendarRepository.kt und CalendarViewModel.kt – also die klassische Kalender-Rasteransicht wie im LineageOS-Kalender. Die experimentellen Karussell-Zustände (CarouselLevel, CalendarNavigatorState, CarouselTileSettings) und die carousel_*/compress_night_hours/ work_start_hour/work_end_hour-Einstellungen entfallen wieder. versionCode 66 == versionName 66.

Status 2026-09-02 (56)

Secretary Calendar 56 (versionCode 56): Zurück zum modernen Monats-Raster (echtes Revert der v54-v55-Timeline/Gantt). Die vier von der Timeline-Umstellung (v54/v55) betroffenen Quelldateien wurden auf den Stand von v53 zurückgesetzt – also dem letzten Release mit Monats-Raster und mit modernem Stil (modernes UI seit v51): CalendarListScreen.kt (Monats-Raster mit CalendarMonthGrid/DayCell, 6×7-Tage-Gitter Mo–So, heute/ausgewählt hervorgehoben, Raster-Farben/Textumbruch/Schriftgröße aus den Einstellungen, Quer-Wischen blättert den Monat, Hoch-Wischen togglet zwischen Raster und Terminliste), SettingsScreen.kt (Raster-Einstellungen wieder eingeführt), CalendarRepository.kt (gridTodayColor, gridSelectedColor, gridTitleSize, gridLineBreak) und CalendarViewModel.kt (ohne die Timeline-Zustände timelineDays/timelineToday). Timeline/Gantt-Komponenten (WeekTimeline, TimelineDayRow, WeekGanttTimeline), die Timeline- Einstellungen (Tag-/Freizeit-/Eintrags-Höhe, Eintrags-Schriftgröße) und der timeline_*-Zustand im ViewModel entfallen wieder. versionCode 56 == versionName 56.

Status 2026-09-02 (55)

Secretary Calendar 55 (versionCode 55): Timeline umgebaut zu einer sanft scrollenden Tagesliste (auf Wunsch statt Wochen-Paging). Die Hauptansicht zeigt jetzt eine LazyColumn mit einer fortlaufenden Tagesliste (Tag-Zeile in vorgegebener Höhe, heute hervorgehoben, initial auf heute gescrollt), eine Woche passt ohne Wischen auf den Bildschirm. Jede Tageszeile hat links die Tageskennung (Wochentag + Tag) und rechts eine horizontale 24-h-Timeline: eine dünne, mittige Freizeit-Linie über die ganze Breite; reservierte Zeiten erscheinen als dicke, farbige Balken an der echten Uhrzeit (Start–Dauer, Endzeit-Clipping auf 0–24 h) mit dem Termin-Titel im Balken (Schriftgröße konfigurierbar). Ganztags-Termine als Balken über die volle Zeile. Navigation: Antippen eines Balkens öffnet den Termin, Antippen einer Tageszeile öffnet die Tagesansicht, „Heute" springt zur heutigen Zeile; hoch-Wischen toggelt zwischen Timeline und der bestehenden Terminliste. Neu konfigurierbar in den Einstellungen („Timeline"): Tag-Höhe, Freizeit-Höhe, Eintrags-Höhe und Eintrags-Schriftgröße (SharedPreferences in CalendarRepository, Werte timeline_day_height, timeline_free_height, timeline_entry_height, timeline_entry_font_size). Die alten Raster-Einstellungen entfallen. CalendarViewModel liefert dazu timelineDays (Termine für ±180 Tage um timelineToday) mit refreshTimeline() (expandiert Wiederholungen, refresht nach Laden/Speichern/Löschen) und goToToday(). Zusätzlich .fossil-settings/ignore-glob um */build/ und app/build/ ergänzt (Build-Artefakte). versionCode 55 == versionName 55.

Status 2026-09-02 (54)

Secretary Calendar 54 (versionCode 54): Wochen-Gantt/Timeline-Ansicht – die Monats-/Listenansicht hat ein neues Raster-Layout: statt des Monats-Rasters zeigt die Hauptansicht (Timeline-Modus) jetzt eine vertikale Wochen-Gantt – horizontale Achse = 7 Wochentage (Start montags, Tageskopf mit Wochentag + Datum, Wochenende/Farbe hervorgehoben), vertikale Achse = Stunden von 06:00–22:00 mit Stundenlineal links und Stundenlinien. Termine werden als farbige Gantt-Balken an ihrer echten Startzeit/Dauer positioniert (Endzeit-Clipping auf das sichtbare Fenster); Ganztags-Termine als Balken über die volle Spaltenhöhe. Navigation: Pfeiltasten/Quer-Wischen blättern die Woche, „Heute" springt zur aktuellen Woche; Antippen eines Balkens öffnet den Termin, Antippen eines leeren Slots öffnet die Tagesansicht. Hoch-Wischen toggelt zwischen Timeline und der bestehenden Terminliste (die Monats-/Listenansicht bleibt als Listen-Modus erhalten). Umsetzung: neues WeekGanttTimeline + DayGanttColumn in CalendarListScreen.kt; im CalendarViewModel neue Zustände selectedWeekStart/weekEvents/ isWeekLoading und Methoden navigateWeek, goToWeekOf, goToTodayWeek, refreshWeek (expandiert Wiederholungen über recurrenceCache.occurrences); setEvents refresht die Woche nach jedem Laden/Speichern/Löschen. Das Monats-Raster (CalendarMonthGrid/DayCell) wurde entfernt; die Raster-Einstellungen (Farben/Wortumbruch/Schriftgröße) entfallen. versionCode 54 == versionName 54.

Status 2026-09-02 (53)

Secretary Calendar 53 (versionCode 53): Termin teilen (ICS) – der Termin-Editor hat jetzt für bestehende Termine ein Teilen-Icon (klassische TopAppBar und moderner Header), das den Termin als .ics über das Android-Share-Sheet versendet. Umsetzung: neue CalendarViewModel.shareEventAsIcs(event) serialisiert den Termin über VEventParser.serialize in eine Datei unter cacheDir/exports/ und liefert eine FileProvider-Content-URI; die UI startet einen ACTION_SEND-Intent mit MIME text/calendar und FLAG_GRANT_READ_URI_PERMISSION. Der ICS-Import (ACTION_VIEW/SEND) bleibt unverändert erhalten. versionCode 53 == versionName 53.

Status 2026-09-01 (52)

Secretary Calendar 52 (versionCode 52): Einheitlicher Moderner Header – alle secretary-Apps teilen jetzt denselben modernen Haupt-Header (ModernAppHeader in secretary-common-android): Hero mit Monogramm + Status-Chip und darunter die identische Aktionsleiste (Sync, Kalender, Einstellungen). Die Monats-/Listenansicht behält ihren gewohnten Sync/Kalender/Einstellungen-Block, jetzt aus der gemeinsamen Komponente. versionCode 52 == versionName 52.

Status 2026-09-01 (51)

Secretary Calendar 51 (versionCode 51): Komplett modernisierte Oberfläche (Moderner Stil). Monats-/Listenansicht, Tagesansicht, Termin-Editor, Sammlungen und Einstellungen haben jetzt einen vollständigen „Moderner Stil"- Zweig nach dem gemeinsamen Editorial-Ledger-Design (secretary-common-android): Serifen-Display-Typo, Pill-artige Karten, Hero-Header mit Monogramm, Status-Chips (ONLINE/OFFLINE/SYNC), Kalender-Raster im Karten-Container, Pill-Termin-FAB, moderne Leerszustände; der klassische Stil bleibt unverändert erhalten. Umschaltung über den globalen Schalter „Moderner Stil"; das Theme liefert im modernen Modus die secretaryModernTypography. versionCode 51 == versionName 51.

Status 2026-08-31 (47)

Secretary Calendar 47 (versionCode 47): Tagesansicht: neuer Termin startet auf dem gewählten Tag + Endzeit folgt automatisch. Der FAB der Tagesansicht legt den neuen Termin jetzt mit Startdatum auf dem ausgewählten Tag an (createNewEvent(date)), statt auf dem heutigen Datum – Zeit default 09:00, Dauer gemäß Einstellung. Im Editor wird das Enddatum/die Endzeit automatisch auf den korrekten Abstand zum Start gesetzt, sobald das Startdatum oder die Startzeit geändert, eingegeben oder vorbefüllt wird: Der ursprüngliche Zeitabstand (baseIntervalMinutes) bzw. die Tagesdifferenz (baseAllDayDays) wird einmal aus dem vorbefüllten Termin erfasst und auf jede Start-Änderung angewendet (LaunchedEffect), ohne eine explizite End-Änderung zu überschreiben. Version 47, versionCode 47 (VersionName und versionCode immer identisch).

Status 2026-08-31 (46)

Secretary Calendar 46 (versionCode 46): Tagesansicht-FAB + ICS-Import aus E-Mails. Die Tagesansicht (DayScreen) hat jetzt wie die Monats-/Listenansicht einen Floating-Action-Button zum Anlegen eines neuen Termins (onNewEvent → createNewEvent → Editor). Außerdem funktioniert das Hinzufügen eines Termins aus einer E-Mail-Anlage: die App deklariert schon ACTION_VIEW/text/calendar; MainActivity liest jetzt die eingegangene .ics-Anlage (onNewIntent/onCreate, contentResolver.openInputStream) und übergibt sie an das neue CalendarViewModel.importFromIcs(), das den VEVENT per VEventParser.parseMultiple parst, dem resolved Kalender zuordnet, neue UID vergibt und den ersten Termin im Editor öffnet – Speichern persistiert ihn über den normalen saveEvent-PUT-Pfad. Zusätzlich der determinat Recurrence-Fortschrittsbalken, der sauber bis 100% füllt (recurrenceProgress, Float) statt endlos zu schleifen. Version 46, versionCode 46 (VersionName und versionCode immer identisch).

Status 2026-08-30 (45)

Secretary Calendar 45 (versionCode 45): Termin-Editor: Kalender-Auswahl, gültige Zeitspannen, Editor-Settings; Recurrence-Berechnung beschleunigt + mit Fortschrittsanzeige. Der Edit-Dialog hat jetzt ein Kalender-Dropdown – neue Termine landen nicht mehr „im Leeren" (vorher wurde ohne Kalender-Auswahl eine PUT auf /calendars/<uid>.ics gesendet, die der Server als verwaiste Datei am Storage-Root ablegte; solche Termine waren in keiner Kollektions-Liste sichtbar). Zusätzlich greift eine Fallback-Logik im CalendarViewModel (resolveCalId): fehlende Kalender-ID → aktueller Kalender → erster Kalender, für alle Save/Delete/Recurrence-Pfade. Ende-vor- Anfang-Spannen werden abgefangen: wenn das Enddatum vor dem Anfangsdatum liegt, wird die Dauer automatisch auf die Standardlänge gesetzt; das Startdatum- Picker rückt das Enddatum mit. Neue Einstellung „Standardlänge (Minuten)" (default 60) steuert die Länge neuer Termine (createNewEvent, Edit-Dialog, Auto-Korrektur). Die Recurrence-Wiederberechnung läuft jetzt auf Dispatchers.Default statt auf dem Main-Thread (kein UI-Freeze mehr beim Monatswechsel/Refresh) und zeigt in der Monatsansicht eine „Wiederholungen werden berechnet…"-Progressbar (isComputingRecurrences). Der RecurrenceCache verlängert Serien nun inkrementell ab der letzten berechneten Occurrence (lastOccurrenceInstant) statt jede Serien-Wand von DTSTART neu zu laufen. Version 45, versionCode 45 (VersionName und versionCode immer identisch).

Status 2026-08-30 (44)

Secretary Calendar 44 (versionCode 44): Termin kopieren – neue Kopier-Funktion für Termine. Jede Termin-Zeile in der Tagesansicht (DayScreen) und der Terminliste (CalendarListScreen) hat jetzt ein Kopier-Icon, das den Edit-Dialog (EventEditScreen) vorbefüllt als neuen Termin öffnet: neue UID, leere href/etag, alle übrigen Felder (Titel, Ort, Beschreibung, Zeiten, Wiederholung, Status, …) übernommen. Speichern legt ein Duplikat an ohne das Original anzufassen. Umsetzung: neue copyEvent(event) im CalendarViewModel (erzeugt die Kopie und selektiert sie), Callbacks onCopyEvent in Tages- und Monats-Listen-Ansicht, Verdrahtung im NavGraph. Version 44, versionCode 44 (VersionName und versionCode immer identisch).

Status 2026-08-30 (43)

Secretary Calendar 43 (versionCode 43): System-Kalender zeigt Termine weiterhin 2–3fach – Wurzelursache: sich überlappende Sync-Läufe. Jeder ensureSyncing()-Aufruf startet einen direkten Background-Thread-Mirror und einen Framework-requestSync; mehrere Trigger feuern kurz hintereinander (Kaltstart, Login, backgroundSync, refreshFromServer, Berechtigungs-Grant, Settings-Button). Zwei Runs fragten gleichzeitig denselben „existing"-Zustand ab, bevor einer committet hatte, und insertierten dadurch dasselbe Event – die v39-Orphan-Bereinigung entfernt nur Zeilen mit leerem SYNC_DATA3, nicht Duplikate mit identischem UID. Fix in CalendarSystemSyncAdapter: (1) gemeinsames ReentrantLock serialisiert alle System-Syncs (direkter Thread + onPerformSync), überlappende Runs werden so zu sequenziellen idempotenten Upserts; (2) reconcileEvents sammelt jetzt Zeilen pro UID und löscht bei mehreren Treffern alle bis auf die niedrigste _id – damit werden auch bereits angelegte Duplikate beim nächsten Sync bereinigt. Version 43, versionCode 43 (VersionName und versionCode immer identisch).

Secretary Calendar 41 (versionCode 41): Sync-Fortschritt: kein Endlos-Spinner mehr nach Abschluss/Fehler – Fix im gemeinsamen SyncProgressUi (secretary-common-android): sobald ein Sync-Job beendet ist (DONE/FAILED), wird der Spinner/Progressbar nicht mehr gezeichnet – es erscheint nur noch der einmalige Toast („Sync abgeschlossen"/ „fehlgeschlagen"). Vorher blieb der Status gesetzt und der Inline-SyncProgressUi zeigte bei einem beendeten oder fehlgeschlagenen Sync (z. B. wenn der vorherige Server /sync/* mit 404 beantwortete) dauerhaft einen Spinner bei 0/0. Außerdem läuft der Server-Init-Sync jetzt gegen den aktualisierten secretary-server 0.10.0 mit den /sync/*-Endpunkten. Version 41, versionCode 41 (VersionName und versionCode immer identisch).

Status 2026-08-29 (40)

Secretary Calendar 40 (versionCode 40): Server-gesteuerter Sync mit Fortschritt – über den gemeinsamen SyncJobClient (secretary-common-android, POST /sync/start + GET /sync/status/{id}) startet CalendarViewModel.runServerSync() beim manuellen Refresh (forceRefresh) für jeden Kalender einen Server-Sync-Job und pollt den aggregierten Fortschritt in [syncStatus]. CalendarListScreen zeigt über das gemeinsame SyncProgressUi während des Syncs einen Spinner + Determinat-Progressbar (processed / total) und einmalig einen Toast („Sync abgeschlossen"/„fehlgeschlagen"). Version 40, versionCode 40 (VersionName und versionCode immer identisch).

Status 2026-08-29 (39)

Secretary Calendar 39 (versionCode 39): System-Kalender zeigt Termine doppelt – Regression-Bugfix. Der System-Provider-Sync legte pro UID/NAME zwei Kalender- bzw. Event-Zeilen an. Fix in CalendarSystemSyncAdapter: (1) reconcileEvents sammelt Zeilen mit leerem SYNC_DATA3 in orphanIds und löscht sie via asSyncAdapter+ContentUris.withAppendedId; (2) neue dedupeCalendars(resolver, account) löscht Duplikate (gleicher NAME, höhere _id behalten die niedrigste); calendarIdsByHref behält außerdem nur die erste Id pro NAME. Version 39, versionCode 39 (VersionName und versionCode immer identisch).

Status 2026-08-29 (38)

Secretary Calendar 38 (versionCode 38): Einstellungen-Screen an Kontakte-App angeglichen – Kalender und Kontakte schreiben beide in System-Collections (System-Kalender- bzw. System-Kontakte-Provider). Der Kalender- SettingsScreen hat jetzt die gleiche Struktur und den gleichen Sync-Block wie der Kontakte-Screen: Überschrift „Systemkalender" mit Beschreibung, Button „Jetzt in System-Kalender synchronisieren" (analog „…Systemkontakte…"), Hinweis über vm.systemSyncEnabled() (kein Konto → Meldung), danach Abmelden, dann Konto. Die kalenderspezifischen Raster-Einstellungen bleiben als Extra-Block erhalten. Neue ViewModel-Methode systemSyncEnabled() (analog Kontakte). Version 38, versionCode 38 (VersionName und versionCode immer identisch).

Status 2026-08-29 (37)

Secretary Calendar 37 (versionCode 37): System-Kalender jetzt voll befüllt + manueller Sync-Button – nach dem Berechtigungs-Fix (34) und caller_is_syncadapter (35) schlug der updateCalendar-Pfad (Kalender existierten bereits) mit For input string: "? AND …" fehl, weil im Sync-Adapter-Modus die Zeilen-_id aus dem URI-Pfad (ContentUris.withAppendedId) statt aus der Selection gelesen wird. Fix: Update/Delete von Kalendern und Events verwenden jetzt withAppendedId mit caller_is_syncadapter-Paramentern. Außerdem gibt es jetzt unter Einstellungen einen Button „Jetzt in System-Kalender synchronisieren" (vm.mirrorToSystem()), analog zum Kontakte-App-Button „Jetzt in Systemkontakte synchronisieren". Version 37, versionCode 37 (VersionName und versionCode immer identisch).

Status 2026-08-29 (36)

Secretary Calendar 36 (versionCode 36): Events landen nicht im System-Provider – nach dem caller_is_syncadapter-Fix (35) wurden die Kalender angelegt, aber jede Event-Insert schlug mit Only the provider may write to account_name fehl: eventValues schrieb account_name/account_type selbst mit. Beim Events-Insert leitet der CalendarProvider2 das Konto aus der CALENDAR_ID (Kalenderzeile) ab; die Spalten dürfen dort nicht explizit geschrieben werden. Fix: beide Spalten aus eventValues entfernt. Version 36, versionCode 36 (VersionName und versionCode immer identisch).

Status 2026-08-29 (35)

Secretary Calendar 35 (versionCode 35): Systemkalender bleibt leer (nach Berechtigungs-Fix) – nachdem die fehlende Runtime-Berechtigung (34) erteilt war, schlug der Sync trotzdem mit Only sync adapters may write to account_name fehl: CalendarSystemSyncAdapter schrieb auf die nackte Calendars/Events-URI, ohne den caller_is_syncadapter=true + account_name + account_type-Query-Parameter, den der CalendarProvider2 verlangt, um account-gebundene Zeilen zu schreiben (DAVx5/Exchange-Muster). Fix: asSyncAdapter(uri, accountType, accountName) und Verwendung für alle Insert/Update/Delete von Kalendern und Events. Version 35, versionCode 35 (VersionName und versionCode immer identisch).

Status 2026-08-29 (34)

Secretary Calendar 34 (versionCode 34): Systemmonat in LineageOS-Kalender leer – die eingebaute Android-Kalender-App zeigte keine secretary-Termine. Ursache: die App deklariert zwar READ_CALENDAR/WRITE_CALENDAR im Manifest, fragte die Runtime-Berechtigung aber nie an (targetSdk 36, API 26+). Dadurch blieb granted=false und der System-Provider-Sync (CalendarSystemScheduler.ensureSyncing → CalendarSystemSyncAdapter) schlug immer mit SecurityException: requires android.permission.READ_CALENDAR or WRITE_CALENDAR fehl — die Fehler wurden nur geloggt, daher wirkte alles normal, es kam aber nie etwas in com.android.calendar an. Fix: MainActivity fragt die Calendar-Berechtigung zur Laufzeit (Activity-Result- API, RequestMultiplePermissions) an; nach Gewährung wird vm.mirrorToSystem() aufgerufen, um den Cache sofort zu spiegeln. Version 34, versionCode 34 (VersionName und versionCode immer identisch).

Status 2026-08-29 (33)

Secretary Calendar 33 (versionCode 33): Diagnose leerer Monatsansicht – auch nach dem mehrwöchigen-Wochen-Fix im common-Recurrence meldete der User eine komplett leere Monatsansicht (keine Termine). Der End-to-End-Pfad (VEventParser + common Recurrence, Test RecurrenceDisplayTest) liefert die September-Occurrences korrekt, daher ist die Ursache in Datenladen/Anzeige (oder einer einzelnen Event-Ausnahme). Maßnahmen: recomputeEventsForMonth ist jetzt je Event mit try/catch abgesichert (ein kaputtes Event kann den Monat nicht mehr leeren) und loadEvents/recomputeEventsForMonth loggen Zähler (Tag CalendarVM) zur Diagnose. Version 33, versionCode 33 (VersionName und versionCode immer identisch).

Status 2026-08-29 (31)

Secretary Calendar 31 (versionCode 32): Fix für mehrwöchige Wochen- Wiederholungen – ein Bug in Recurrence.nextWeeklyByDay (secretary-common-android) ließ bei FREQ=WEEKLY;INTERVAL>1 mit BYDAY (für z. B. „alle 2 Wochen am Dienstag" erzeugt der Dialog BYDAY=TU) aufeinanderfolgende Occurrences weg (das 4. Vorkommen war übersprungen, Monatsansicht zeigte z. B. „Selbsthilfegruppe" im August, aber nicht mehr im September). Die Wochen-Berechnung zählte die absolute Wochenanzahl als Intervall-Woche und sprang dadurch um einen ganzen Zyklus zu weit. Fix: k zählt jetzt die Intervall-Wochen ab der Basiswoche. Regressionstest weeklyIntervalTwoByDayDoesNotSkipWeeks. Version 31, versionCode 32.

Status 2026-08-28 (30)

Secretary Calendar 30 (versionCode 31): Systemkalender-Sync – die App spiegelt ihren Offline-Cache (Kalender + Events) jetzt über einen SyncAdapter (system/CalendarSystemSyncAdapter.kt, Service CalendarSystemSyncService) für com.android.calendar in den System- Kalender-Provider, damit die Termine in der eingebauten Kalender-App und anderen Consumer-Apps sichtbar sind (analog zum Contacts-System-Provider-Sync; ersetzt DAVx5). CalendarStore.snapshot() liest synchron Kalender + Events je Kalender; der Adapter gleicht Kalender (per Calendars.NAME=href) und Events (per SYNC_DATA3=UID) ab, fügt ein/aktualisiert und löscht überschüssige Zeilen. Integration: CalendarViewModel ruft CalendarSystemScheduler.ensureSyncing() nach Login, beim Kaltstart mit Cache und nach jedem backgroundSync/refreshFromServer. Version 30, versionCode 31.