Secretary Calendar (Android)
CalDAV calendar client for secretary-server with offline cache, continuous day-list view, event editing, and ICS import/export.
What it does
- Every event of every calendar in one continuous day list - no week/month grid to switch between
- Full event editing: title, date and time, location, description, attendees, reminders
- Multiple calendars (collections), selectable and remembered across restarts
- Sync into the Android system calendar so events show up in the platform calendar apps
- Import and export single events as
.icsfiles - Works offline: the calendar is cached on the device, edits queue up and sync on reconnect
- Search across title, description and location
Changelog
Every released version: Apps/calendar/changelog
Screenshots

Directory Structure
secretary-calendar-android/
├── build.gradle.kts # Root build
├── settings.gradle.kts
├── gradle.properties
├── CONTEXT.md
├── gradle/wrapper/
└── app/
├── build.gradle.kts
├── proguard-rules.pro
└── src/main/
├── AndroidManifest.xml
├── res/values/
│ ├── strings.xml
│ └── themes.xml
├── java/org/beowulf/secretary/calendar/
│ ├── SecretaryCalendarApp.kt
│ ├── MainActivity.kt
│ ├── data/
│ │ ├── Models.kt # Calendar, CalendarEvent, Attendee, Reminder
│ │ ├── VEvent.kt # RFC 5545 parse/serialize
│ │ ├── CalendarCaldavClient.kt # CalDAV HTTP client
│ │ ├── CalendarRepository.kt # Account + prefs
│ │ └── CalendarStore.kt # JSON cache + pending queue + snapshot()
│ ├── system/
│ │ ├── CalendarSystemSyncAdapter.kt # System-Provider-Sync (com.android.calendar)
│ │ ├── CalendarSystemSyncService.kt # Sync-Service (IBinder)
│ │ └── CalendarSystemScheduler.kt # periodisch + manueller Sync
│ ├── vm/
│ │ └── CalendarViewModel.kt
│ └── ui/
│ ├── Theme.kt
│ ├── LoginScreen.kt
│ ├── CalendarListScreen.kt # Continuous day list (+ month nav)
│ ├── CollectionsScreen.kt # Calendar picker (overview + calendars)
│ ├── EventEditScreen.kt
│ └── navigation/NavGraph.kt
└── src/test/java/org/beowulf/secretary/calendar/data/
└── VEventTest.kt
Commands
Prerequisites: JDK 21, Android SDK (platform 37, build-tools 34) and
Gradle 9.x. The repository deliberately ships no Gradle wrapper --
either use Android Studio (it brings its own Gradle) or install Gradle 9
yourself, which is why the examples below call gradle directly.
export ANDROID_HOME="$HOME/Android/Sdk" # bzw. sdk.dir=... in local.properties
gradle :calendar:assembleDebug # debug APK
gradle :calendar:testDebugUnitTest # unit tests
gradle :calendar:lintDebug # lint
Concepts
- Calendars: VEVENT collections discovered via CalDAV PROPFIND.
- Day list view: Continuous daily list around the current month; the horizontal swipe changes the focused month, tapping the monogram scrolls to today.
- Overview mode: Shows events from all calendars with colored bars per calendar.
- Offline: Events cached as JSON in
app/filesDir/calendar/; changes go into a write-behind queue (PendingAction) and replayed on next connection. - Account: Credentials from
secretary-account(ContentProvider, signature permission), with local SharedPreferences cache.
Permissions
INTERNET— CalDAV accessorg.beowulf.secretaryaccount.permission.READ_ACCOUNT— central accountREAD_CALENDAR/WRITE_CALENDAR— Schreiben in den System-Kalender-ProviderREAD_SYNC_SETTINGS,WRITE_SYNC_SETTINGS,GET_ACCOUNTS,AUTHENTICATE_ACCOUNTS,MANAGE_ACCOUNTS— Sync über das geteilte secretary-Konto
Systemkalender (Sync in den System-Provider)
- SyncAdapter:
system/CalendarSystemSyncAdapter.kt+CalendarSystemSyncServiceregistriert fürcom.android.calendar(res/xml/sync_calendars.xml,contentAuthority="com.android.calendar", Kontoorg.beowulf.secretary,isAlwaysSyncable).onPerformSyncschreibt Kalender (Calendars: NAME=href, Display-Name, Farbe, ctag inCAL_SYNC1) und Events (Events:SYNC_DATA3=UID,SYNC_DATA1=ETag,_SYNC_ID=href, Titel, Beschreibung, Ort, DTSTART/DTEND/ALL_DAY/RRULE, Status, Availability) direkt in den System-Kalender-Provider (com.android.calendar), Abgleich per UID; überschüssige Kalender/Events werden gelöscht. - Quelle: der lokale
CalendarStore-Cache – einseitig (Cache → System-Provider) via synchronesnapshot()(Kalender + Events je Kalender). Die App bleibt die Quelle für CalDAV-I/O über ihren eigenen Client. - Trigger: nach Login, Kaltstart mit Cache, nach jedem
backgroundSync/refreshFromServer, periodisch (alle 6 h). - Dadurch sind die secretary-Termine für die eingebaute Android-Kalender-App und andere Consumer sichtbar. (Eine separate Authority würde vom System-Kalender-Provider nicht konsultiert; deshalb schreibt der SyncAdapter direkt in den System-Provider – das DAVx5-Muster.)
Server Reference
secretary-server(CalDAV, Rust):~/Projects/secretary-server/(/calendars/, PROPFIND, REPORT calendar-query, PUT/DELETE, MKCALENDAR)
Attachments:
- calendar-settings.png [download] added by beowulf on 2026-10-06 16:03:27. [details]
- calendar-event.png [download] added by beowulf on 2026-10-06 16:03:27. [details]
- calendar-collections.png [download] added by beowulf on 2026-10-06 16:03:27. [details]
- calendar-agenda.png [download] added by beowulf on 2026-10-06 16:03:27. [details]