16 KiB
Android App — Kotlin (Jetpack Compose) Plan und Abhakliste
Kurz: Ziel ist eine native Android-App mit Kotlin + Jetpack Compose, die die Web-UI 1:1 abbildet (Farben, Typografie, Funktionalitäten). Diese Datei enthält Architekturentscheidungen, empfohlene Bibliotheken und eine detaillierte Abhakliste (schrittweise).
- Zusammenfassung der Entscheidungen
- Plattform: Native Android
- Sprache: Kotlin
- UI-Toolkit: Jetpack Compose (Compose Material3)
- Architektur: MVVM mit
ViewModel+ Kotlin Coroutines + Flow - DI: Hilt
- HTTP-Client: Ktor Client oder Retrofit + OkHttp (empfohlen: Retrofit für breite Community-Docs)
- Bild-Loading: Coil
- Lokale DB / Caching: Room + DataStore (Preferences)
- Background/Sync: WorkManager
- Auth: kurzlebiges JWT-Access-Token plus rotierendes, widerrufbares Refresh-Token pro Android-Gerätesitzung; Speicherung in
EncryptedSharedPreferences/Android Keystore; Unterstützung für Passkeys (Android Passkeys / WebAuthn Interop über FIDO2 APIs) - Auth-Sicherheitsentscheidung: kein statischer App-Key bzw. kein in der APK hinterlegtes Client-Secret. Native Apps können ein gemeinsames Secret nicht vertraulich halten. Optional später: Refresh-Sitzung an ein pro Installation im Android Keystore erzeugtes Schlüsselpaar binden.
- Rich-Text: WebView-basierte Anzeige; Editoren: ggf. hybride Lösung (Server-side HTML editor + WebView) oder
RichEditor-Libs - Crash-Reporting & Monitoring: Firebase Crashlytics oder Sentry
- Design & Farben
- Material Theme (Material3) mit Farben aus
tailwind.config.js(Primary + Accent). - Fonts: Inter & Montserrat via Google Fonts (Download/Bundle oder Play-Services-Download at runtime).
- Mapping: Tailwind-Token →
colors.xml/ ComposeColortokens.
- Empfohlene Abhängigkeiten (erste Implementierung)
- androidx.compose.* (ui, material3, navigation)
- androidx.lifecycle:lifecycle-viewmodel-ktx
- com.google.dagger:hilt-android
- retrofit2 + converter-moshi / kotlinx-serialization
- io.coil-kt:coil-compose
- androidx.room:room-runtime + room-ktx
- androidx.work:work-runtime-ktx
- androidx.datastore:datastore-preferences
- com.google.android.gms:play-services-auth (für passkeys falls nötig)
- io.sentry:sentry-android (optional)
-
Detaillierte Abhakliste (Schritte) [x] 1. Repo-Analyse: Liste der externen Endpunkte und Auth-Anforderungen exportieren [x] 2. Projekt-Scaffold: Android Studio Projekt mit Kotlin + Compose anlegen [x] 3. App-Architektur: Module / Packages anlegen (ui, data, domain, di, util) [x] 4. CI-Build: Gradle-Config und GitHub Actions Skeleton [x] 5. Theme:
Color.kt,Typography.kt,Theme.kterstellen und Tailwind-Farben mappen [x] 6. Fonts: Inter + Montserrat einbinden (res/font oder GoogleFonts) [ ] 7. Navigation: Compose Navigation-Graph mit Routen für alle Web-Seiten anlegen [x] 7a. Umgebungen: Android-Varianten fuer lokal, Test-Instanz und Produktion mit eigener API-Basis konfigurieren [x] 7b. Adaptive Navigation: Tablet mit persistentem Header/Hauptmenue, Smartphone mit bestehender screenbezogener Navigation [x] 7c. Branding: vorhandenes Web-Logo als optimierte Android-Ressource in der App-Navigation verwenden [x] 7d. Tablet-Navigation: öffentliche Haupt- und Subnavigation der Web-UI mit Portierungszielen abbilden [x] 7e. Navigation: dynamische Mannschaftslinks, Galerie-Sichtbarkeit und rollenabhängigesInternwie in der Web-UI anbinden [x] 8. Start-Screen:HomeScreenwebnah mit Hero, Navigation, Termine, Spielen, News und Aktionen umsetzen [x] 9. Komponenten: NavBar, Footer, Cards, ImageGrid und News-Dialog implementieren [x] 10. Öffentliche Screens aus der Web-Navigation portieren/Startseite/termine: öffentliche Terminliste mit Lade-, Leer- und Fehlerzustand/mannschaften/spielplaene: Saison-, Wettbewerbs- und Mannschaftsfilter mit Spielkarten/verein/galerie: Anzeige-Screen vorhanden/kontakt: Formular-Screen vorhanden/mitgliedschaft: Antrag, Validierung, PDF-Erzeugung und PDF-Öffnen/verein/ueber-uns: CMS-Inhalt aus der öffentlichen Konfiguration/vorstand: öffentliche Vorstandsangaben aus der Konfiguration/verein/geschichte: CMS-Inhalt aus der öffentlichen Konfiguration/verein/satzung: CMS-Inhalt und PDF-Aufruf aus der öffentlichen Konfiguration/vereinsmeisterschaften: Ergebnisliste mit Jahresfilter und Statistik/links: strukturierte CMS-Links mit Fallback-Verweisen/mannschaften: Übersicht aus saisonaler Mannschafts-CSV/mannschaften/[slug]: dynamische Mannschaftsdetails mit aktuellem Spielplan und UmschaltungMatches/Tabelle/spielsysteme: Spielsystemkarten mit Kategoriefilter aus CSV/training: Trainingsort und gruppierte Trainingszeiten aus der Konfiguration/training/trainer/training/anfaenger/tt-regeln: Regelübersicht mit DTTB- und PDF-Aufruf [ ] 10a. Weitere öffentliche bzw. bestehende Web-Routen prüfen und portieren/anlagen/impressum- Legacy-/Doppelrouten klären:
/galerie,/geschichte,/satzung,/ueber-uns,/spielplan,/verein/tt-regeln,/mannschaft/[slug],/mannschaften/herren,/mannschaften/damen,/mannschaften/jugend[ ] 10b. Newsletter-Screens portieren /newsletter/subscribe/newsletter/unsubscribe/newsletter/confirm,/newsletter/confirmed,/newsletter/unsubscribed[x] 10c. Auth-Screens portieren/login: Passwort-Login und Logout in der laufenden Sitzung/registrieren/passwort-vergessen[ ] 10d. Mitgliederbereich portieren/mitgliederbereich: Übersicht/mitgliederbereich/mitglieder/mitgliederbereich/news/mitgliederbereich/profil/mitgliederbereich/api[ ] 10e. CMS-Screens nach Rollenberechtigung portieren/cms,/cms/startseite,/cms/inhalte,/cms/vereinsmeisterschaften/cms/sportbetrieb,/cms/mitgliederverwaltung,/cms/kontaktanfragen/cms/newsletter,/cms/einstellungen,/cms/benutzer[ ] 11. API-Client: Retrofit/Ktor-Client implementieren, Auth-Interceptor (Token Refresh)- Retrofit/OkHttp/Moshi und Hilt-Verdrahtung
- Öffentliche Endpunkte für Startseite, Termine, Spielplan, Galerie und Kontakt
- Mitgliedschafts-PDF-Endpunkte mit Cookie-Jar und
FileProvider - Passwort-Login-Endpunkt und Token-Übergabe an den Interceptor
- Verschlüsselte Token-Persistenz sowie Status/Logout per Bearer-Token
- Bearer-Unterstützung aller später portierten geschützten Bereiche
POST /api/auth/refreshanbinden und Access-Token bei Ablauf automatisch erneuern- OkHttp-
Authenticatormit genau einem synchronisierten Refresh-Versuch pro fehlgeschlagenem Request ergänzen [x] 12. Auth: Login/Register/Logout + sichere Token-Speicherung (EncryptedSharedPreferences) - Login/Logout und verschlüsselte Token-Speicherung
- Registrierung und Passwort-Reset
- Backend: JWT-Access-Token von aktuell 7 Tagen auf kurze Laufzeit (Ziel: ca. 15 Minuten) reduzieren
- Backend: langlebige, zufällige Refresh-Token pro Gerätesitzung mit serverseitig gespeichertem Token-Hash einführen
- Backend: Refresh-Token bei jeder Erneuerung rotieren und Wiederverwendung eines verbrauchten Tokens als Sitzungsdiebstahl behandeln
- Backend: Logout, Kontodeaktivierung und Passwortänderung widerrufen betroffene Refresh-Sitzungen
- App: Access- und Refresh-Token verschlüsselt speichern, Sitzung beim App-Start durch Refresh wiederherstellen
- Optional nach MVP: pro Installation ein nicht exportierbares Keystore-Schlüsselpaar erzeugen und Refresh-Requests daran binden
[ ] 13. Passkeys: Integration prüfen (FIDO2 / Passkeys) und Fallback auf Passwort
[ ] 14. Image-Upload: Multipart-Upload + Coil für Anzeige + Bildkompression (u. a. Sharp-Äquivalent evtl. serverseitig)
[ ] 15. Rich-Text: Anzeige von HTML (Compose + WebView) und ggf. Editor via WebView-bridge
[ ] 16. Formulare: Validierung (clientseitig) und Fehlerdarstellung
[ ] 17. Offline & Caching: Room für persistente Daten, Response-Caching, Sync-Strategie
[ ] 18. Lokalisierung:
strings.xml(DE + EN) und i18n-Check [ ] 19. Accessibility: ContentDescription, Focus, Farben/Kontrast prüfen [ ] 20. Tests: Unit-Tests für ViewModels + UI-Tests mit Compose Testing [ ] 21. Performance: Bildoptimierung, LazyLists, Paging (falls große Daten) [ ] 22. Analytics: Firebase / Matomo Integration (je nach Datenschutz) [ ] 23. Crash-Reporting: Sentry / Crashlytics integrieren [ ] 24. Build/Release: App signing, Release-Notes, Play-Store-Metadaten vorbereiten [ ] 25. Dokumentation:README-android.mdmit Setup, Architektur und Release-Anleitung
-
Kurzzeit-MVP (Priorität für erste Version)
- A. Auth (Login/Logout)
- Passwort-Login und Logout in der aktuellen App-Sitzung
- Persistente Statuswiederherstellung/Logout für die Auth-Endpunkte
- Dauerhaftes Eingeloggtbleiben durch rotierendes Refresh-Token pro Android-Gerätesitzung
- B. Home, Termine, Spielplan, Galerie (anzeigen)
- C. Kontaktformular (absenden)
- D. Bildanzeige + Caching
- E. Theme & Fonts
- Nächste Aktionen (sofort)
- Dauerhaftes Android-Login umsetzen: Backend-Refresh-Sitzungen, Token-Rotation, serverseitigen Widerruf und App-Refresh-Flow ergänzen.
- Passkey-Anmeldung über Android Credential Manager anbinden.
- Die noch fehlenden öffentlichen Routen aus
10aund die Newsletter-Screens aus10bnativ portieren. - Saisonwahl für Mannschaftsübersicht/-details wie in der Web-UI ergänzen.
- Mitgliederbereich/CMS-Screens portieren; dabei rollenabhängige
Intern-Navigation und Bearer-Unterstützung der verwendeten Backend-Endpunkte ergänzen.
- Umsetzungsprotokoll
- 2026-05-27: Webnahe Startseite mit öffentlichen Live-Daten umgesetzt; Hilt/Moshi-App-Verdrahtung ergänzt.
- 2026-05-27:
TermineundSpielplanals native Screens umgesetzt; Spielplan unterstützt Saison, Wettbewerb, Mannschaft, Ergebnis und zweizeilige Gruppeninformation. - 2026-05-27:
Mitgliedschaftmit Antrag/PDF-Abruf sowie Passwort-Login/Logoutumgesetzt; offene Auth-Härtung separat ausgewiesen. - 2026-05-27: Tokens verschlüsselt persistiert; Session-Wiederherstellung sowie Logout per Bearer-Token in den Auth-Endpunkten ermöglicht.
- 2026-05-27: Registrierung und Passwort-Reset an die vorhandenen Auth-Endpunkte angebunden.
- 2026-05-27: Product-Flavors
local,instantTestundproductioneingerichtet; lokale Basis-URL ist per Gradle-Parameter überschreibbar. - 2026-05-27: Gradle-Heap/Worker für Flavor-Builds festgelegt, nachdem paralleles D8/KSP mit dem 512-MiB-Standardheap nicht ausreichend Speicher hatte.
- 2026-05-27: Lokales Testsetup gegen Emulator geprüft; bei IPv6-gebundenem Nuxt-Dev-Server wird die von Nuxt ausgegebene Network-URL per
LOCAL_API_BASE_URLverwendet. - 2026-05-27: Adaptive Navigation umgesetzt; Tablet-Layouts ab
600dpzeigen Header und Hauptmenue dauerhaft, Smartphone-Layouts behalten die vorhandene Navigation. - 2026-05-27: Platzhalterlogo in der Android-Navigation durch das vorhandene Harheimer-TC-Weblogo als skalierte lokale PNG-Ressource ersetzt.
- 2026-05-27: Web-Navigation und
pages/vollständig inventarisiert; Tablet-Haupt-/Subnavigation für die öffentlichen Bereiche strukturell angeglichen und alle fehlenden Screens einzeln in die Portierungsliste aufgenommen. - 2026-05-27: Tablet-Header auf Web-Verhalten angepasst (Bereichswechsel öffnet Startseite und Submenü) und die native ActionBar zugunsten des App-Headers entfernt.
- 2026-05-27: Navigation mit Live-Mannschaftslinks, öffentlicher Galerie-Sichtbarkeit und rollenabhängigem
Internergänzt; Mannschaftsübersicht/-detail sowie Training, Trainer und Anfänger nativ portiert. - 2026-05-27: Mannschaftsdetail um die Web-Untertabs
MatchesundTabelleerweitert; Tabellenzeilen werden aus/api/spielplan/tablegeladen und die eigene Mannschaft hervorgehoben. - 2026-05-27: Tabellenraster in den Mannschaftsdetails mit gemeinsamen Spaltenbreiten für Tablet und Smartphone ausgerichtet; die Zustandswiederherstellung dynamischer Mannschaftslinks korrigiert.
- 2026-05-27: Die verbleibenden öffentlichen Screens aus Punkt 10 portiert: Verein/CMS-Inhalte, Vorstand, Satzung/PDF, Links, Vereinsmeisterschaften mit Personenbild-Dialog, Spielsysteme und TT-Regeln.
- 2026-05-27: Architektur für dauerhaftes Android-Login festgelegt: kein eingebetteter App-Key, sondern kurzlebige Access-Tokens und rotierende, widerrufbare Refresh-Tokens pro Gerätesitzung; optionale spätere Gerätebindung per Keystore-Schlüsselpaar.
- Android-Testumgebungen
- Lokal im Emulator:
./gradlew :app:installLocalDebugverwendethttp://10.0.2.2:3100/und die App-IDde.harheimertc.local. - Lokal, wenn
10.0.2.2nicht erreichbar ist:./gradlew :app:installLocalDebug -PLOCAL_API_BASE_URL=http://<NUXT-NETWORK-HOST>:3100/; die passende URL steht in dernpm run dev-Ausgabe (hierhttp://torstens:3100/). - Test-Instanz:
./gradlew :app:installInstantTestDebugverwendethttps://harheimertc.tsschulz.de/und die App-IDde.harheimertc.test. - Produktion:
./gradlew :app:installProductionDebugverwendethttps://harheimertc.de/und die App-IDde.harheimertc. - Nur APKs erzeugen:
./gradlew :app:assembleLocalDebug :app:assembleInstantTestDebug :app:assembleProductionDebug.
- Dauerhaftes Android-Login: Architektur und Umsetzung
- Ausgangslage: Das Backend gibt derzeit ein sieben Tage gültiges JWT aus. Die App speichert es bereits verschlüsselt und sendet es als Bearer-Token. Die vorhandene serverseitige Sessiondatei wird beim Authentifizieren geschützter Requests derzeit nicht zur Widerrufsprüfung herangezogen.
- Ziel: Ein Benutzer bleibt auf einem bekannten Gerät angemeldet, ohne dass ein langfristig gültiges Bearer-JWT oder ein extrahierbares App-Secret verwendet wird.
- Token-Modell:
- Access-Token: JWT mit kurzer Laufzeit, Zielwert ca. 15 Minuten; wird für normale API-Requests als Bearer-Token verwendet.
- Refresh-Token: kryptografisch zufälliges, undurchsichtiges Token mit längerer Laufzeit, Zielwert z. B. 90 Tage mit Erneuerung bei aktiver Nutzung.
- Server speichert ausschließlich den Hash des Refresh-Tokens zusammen mit
sessionId,userId,createdAt,lastUsedAt,expiresAt,revokedAtund optional Gerätebezeichnung.
- Backend-Arbeitspaket:
- Login-Antwort um
accessToken,refreshToken,sessionIdund Ablaufmetadaten erweitern; bestehendestokennur befristet kompatibel halten. POST /api/auth/refreshimplementieren: gültiges Refresh-Token konsumieren, rotieren und ein neues Token-Paar zurückgeben.- Token-Wiederverwendung erkennen: Wird ein rotiertes Refresh-Token erneut präsentiert, die betroffene Token-Familie bzw. Gerätesitzung widerrufen.
POST /api/auth/logoutauf Widerruf der Gerätesitzung erweitern; optional Endpunkte zum Anzeigen und Widerrufen eigener Geräte-Sitzungen vorsehen.- Kontodeaktivierung und Passwortänderung müssen sämtliche Refresh-Sitzungen des Benutzers widerrufen.
- Rate-Limits und Audit-Events für Login, Refresh-Erfolg/-Fehlschlag, Wiederverwendung und Widerruf ergänzen.
- Login-Antwort um
- Android-Arbeitspaket:
AuthRepositoryauf Access-Token, Refresh-Token und Session-ID erweitern; Speicherung weiter über Keystore-geschützte Preferences.ApiService/DTOs um Refresh-Request und Token-Paar-Antwort ergänzen.- Einen OkHttp-
Authenticatoreinsetzen, der auf401einmalig ein Access-Token erneuert, parallele Refreshes synchronisiert und den ursprünglichen Request wiederholt. - Beim App-Start zunächst Access-Token prüfen und bei Ablauf transparent mit dem Refresh-Token erneuern; nur bei fehlgeschlagenem Refresh zum Login zurückkehren.
- Beim Logout lokale Tokens auch bei Netzwerkfehler entfernen; serverseitiger Widerruf erfolgt best effort bzw. bei nächster Konnektivität.
- Sicherheitsregeln:
- Kein gemeinsamer App-Key und kein statisches Client-Secret in Sourcecode,
BuildConfigoder APK. - Refresh-Tokens nie im Klartext serverseitig speichern oder protokollieren.
- Nur HTTPS für Test-/Produktionsumgebungen; Token-Werte nicht in Logging-Interceptors ausgeben.
- Optional nach MVP: App erzeugt pro Installation ein Keystore-Schlüsselpaar; Backend bindet Refresh-Sitzungen an den öffentlichen Schlüssel und prüft signierte Refresh-Anfragen.
- Kein gemeinsamer App-Key und kein statisches Client-Secret in Sourcecode,
Datei: ANDROID_KOTLIN_PLAN.md