Secretary Notes (Android)
Android-Client für Notizen auf dem secretary-server
(CalDAV/CardDAV). Jede Notiz ist ein VJOURNAL-Komponent (RFC 5545
§3.6.3) in einer .ics-Datei unter einer Kalender-Collection.
What it does
- Notes with title, text, tags and a date, grouped in collections you can switch between
- Stale-while-revalidate offline: the cache is shown immediately and refreshed in the background
- Search across title, text and tags
- Every note is a VJOURNAL component (RFC 5545 3.6.3) - plain
.icsfiles on your own server, readable without Secretary - Conflict-aware writes: updates use
If-Match, a concurrent change is reported instead of silently overwritten - Full CalDAV sync through the shared Secretary client
Changelog
Every released version: Apps/notes/changelog
Screenshots

Verzeichnisstruktur
secretary-notes-android/
├── build.gradle.kts # Root-Build
├── settings.gradle.kts
├── gradle.properties
├── CONTEXT.md
├── doc/ # (GUI-/Protokoll-Notizen: Wiki Apps/notes/gui-protocol)
├── gradle/wrapper/
└── app/
├── build.gradle.kts
├── proguard-rules.pro
└── src/main/
├── AndroidManifest.xml
├── res/values/
│ ├── strings.xml
│ └── themes.xml
└── java/org/beowulf/secretarynotes/
├── SecretaryNotesApp.kt # Application (Service-Locator)
├── MainActivity.kt # Single-Activity
├── data/
│ ├── Models.kt # NotesCollection, Note
│ ├── VJournal.kt # RFC-5545-Parser/-Writer
│ ├── CaldavClient.kt # CalDAV-Client (PROPFIND/PUT/…)
│ ├── AccountResolver.kt # Zentrales Konto (ContentProvider)
│ └── NotesRepository.kt # Config-Persistenz
├── vm/
│ └── NotesViewModel.kt # App-State
└── ui/
├── Theme.kt # Material 3
├── LoginScreen.kt # Server + Basic-Auth
├── NotesListScreen.kt # Collections + Notizliste
├── NoteEditScreen.kt # Editor (Titel/Datum/Tags/Status)
├── SettingsScreen.kt # Verbindung/Abmelden
└── navigation/NavGraph.kt # Routen: login, notes, note, settings
Befehle
Voraussetzungen: JDK 21, Android-SDK (Platform 37, Build-Tools 34)
und Gradle 9.x. Im Repository liegt bewusst kein Gradle-Wrapper --
entweder Android Studio nutzen (bringt sein Gradle mit) oder Gradle 9 selbst
installieren; die Beispiele unten rufen deshalb gradle direkt.
export ANDROID_HOME="$HOME/Android/Sdk" # bzw. sdk.dir=... in local.properties
gradle :notes:assembleDebug # Debug-APK
gradle :notes:testDebugUnitTest # Unit-Tests
gradle :notes:lintDebug # Lint
Builds laufen im Hintergrund via setsid, Logging geht ins Syslog.
Alle acht Module werden gemeinsam gebaut; ein Release-Build signiert die
APKs (gradle assembleRelease, siehe Docs).
Datenmodell (VJOURNAL)
| App-Feld | ICS-Eigenschaft | Hinweis |
|---|---|---|
| uid | UID |
Ressourcenname <uid>.ics |
| summary | SUMMARY |
Titel |
| description | DESCRIPTION |
Text, \n-escaped |
| dateRaw | DTSTART;VALUE=DATE |
Notizdatum YYYYMMDD |
| categories | CATEGORIES |
Tags, kommagetrennt |
| status | STATUS |
DRAFT / CONFIRMED / CANCELLED |
| createdRaw | CREATED |
UTC-Stempel |
| lastModifiedRaw | LAST-MODIFIED |
UTC-Stempel |
| href / etag | – (DAV) | Ressourcen-URL + ETag (If-Match beim PUT) |
Protokoll
PROPFIND /notes/(Depth 1) → Collections +supported-calendar-component-setPROPFIND /notes/<id>/(Depth 1) → Mitglieder (getetag)GET /notes/<id>/<uid>.ics→ VJOURNAL-TextPUTmitIf-Match(412 → Konflikt) zum Anlegen/ÄndernDELETEzum LöschenMKCALENDARmit<comp name="VJOURNAL"/>zum Anlegen der Notiz-Sammlung
Der secretary-server (≥ 0.16.0) benötigt keine Server-Änderungen für
VJOURNAL – VJOURNAL wird erkannt (Storage-Sniff) und in
supported-calendar-component-set gemeldet; seit 0.16.0 liegt Notizen
unter dem top-level Namespace-Root /notes/ (zuvor /calendars/notes-*).
Offline (Stale-While-Revalidate)
OfflineStore (app/src/main/java/org/beowulf/secretarynotes/data/OfflineStore.kt)
hält Collections + Notizen als JSON-Cache (filesDir/secretary_notes_cache.json,
atomare Schreibvorgänge) und eine Write-Behind-PendingAction-Warteschlange.
- Lese-Seite: Beim Start wird sofort der Cache angezeigt; NavGraph stößt
danach einen Online-Refresh an. Server-Fehler (
IOException) schalten auf Cache um (offlineMode=true),DavExceptionwird als echter Fehler gemeldet. - Schreib-Seite offline: PUT/DELETE/MKCALENDAR werden in die Queue
(
PutNote/DeleteNote/CreateCollection) eingereiht und lokal angewendet. Dedupe pro Ressource (Put ersetzt Put/Delete derselbenhref, Delete ersetzt Puts). Beim nächsten erfolgreichen Sync spieltflushPending()die Queue in Reihenfolge ab;DavException(z.B. 412-Konflikt) verwirft den Eintrag mit Fehlermeldung,IOExceptionbricht ab (bleibt offline). - UI: Offline-Banner in der Notizliste („Offline – N Änderung(en) warten auf Synchronisierung") mit Sync-Button.
Konventionen
- Kotlin + Jetpack Compose + Material 3
- Single-Activity, AndroidViewModel, manuelle DI (Application als Service-Locator)
- Zentrales Konto: Die Zugangsdaten kommen primär aus
secretary-account-androidüber den ContentProvidercontent://org.beowulf.secretaryaccount/account(AccountResolver, Signature-PermissionREAD_ACCOUNT).NotesRepository.config()migriert das Konto als Cache in die lokalen Prefs; ohne Provider gilt der Legacy-Fallback (eigene Anmeldung).MainActivity.onResumeruftNotesViewModel.refreshAccount()auf, damit Änderungen in der Account-App ohne Neustart übernommen werden. - Passwort in SharedPreferences (wie podcache-Token); EncryptedSharedPreferences wäre der nächste Schritt
usesCleartextTraffic=true: der secretary-server läuft im LAN ggf. per HTTP
Server-Referenz
secretary-server(CalDAV/CardDAV, Rust):~/Projects/secretary-server/- Tremere:
https://tremere:8443(Servicesecretary-server, Daten~/.local/share/secretary-server/calendars/)
Attachments:
- notes-list.png [download] added by beowulf on 2026-10-06 16:03:28. [details]
- notes-detail.png [download] added by beowulf on 2026-10-06 16:03:28. [details]