Move all business logic and state (~35 variables, ~15 functions) from the 1540-line ChatScreen @Composable into ChatScreenViewModel. ChatScreen is now a thin rendering layer (~430 lines) that reads VM state and wires UI events to VM methods. Android-framework-only concerns (ActivityResultLaunchers, DrawerState, LazyListState, keyboard insets) stay in the Composable. Changes: - New ChatScreenViewModel.kt: messages, streaming, conversation mgmt, sidebar actions, file upload, voice recording, TTS, live call, unlock - ChatScreen.kt: pure UI, delegates all logic to vm.* - Haptics.kt: add createHaptics(Context) factory for non-Composable use - SessionViewModel: store nullable for test construction - MainActivity: instantiates ChatScreenViewModel Tests: 12 new unit tests for ChatScreenViewModel (state transitions, newChat reset, mode toggles, edit/delete, password dialog lifecycle). All 90 tests pass. |
||
|---|---|---|
| app | ||
| gradle | ||
| .gitignore | ||
| build.gradle.kts | ||
| CLAUDE.md | ||
| DESIGN.md | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LATER.md | ||
| OFFLINE_CACHE_ADD.md | ||
| PASSKEY_PLAN.md | ||
| README.md | ||
| settings.gradle.kts | ||
Kaizen — Android
Native Android-App für Kaizen (Jetpack Compose + Kotlin). Kein WebView, kein Tauri.
Phase 1 — echtes Backend (aktiv)
Der Feel-Prototyp redet jetzt mit dem echten Kaizen-Backend — echte, gestreamte
Antworten statt fakeReply. Die App ist deployment-agnostisch: ein Binary, das auf
die offizielle Instanz ODER jeden Self-Host zeigt, über einen einheitlichen
Authorization: Bearer-Contract. SaaS vs. Self-Host ist nur „welche Base-URL".
Drin:
- In-App-Login (
auth/LoginScreen.kt): Server-URL (Default = offizielle Instanz, editierbar = die „offiziell/Self-Host"-Adresse) + E-Mail + Passwort →POST {baseUrl}/api/v1/auth/token→kzn_-Token. Kein Token-Copy-Paste. - Sichere Token-Ablage (
net/SecureStore.kt):EncryptedSharedPreferences(AES-256-GCM, Schlüssel im Android-KeyStore/StrongBox) — der Token liegt nie im Klartext, auch nicht auf gerooteten Geräten. - Modell vom Server (
net/SessionViewModel.kt): nach dem Login holt die App perGET /api/v1/medas vom Nutzer unter/settings/modelsgewählte Default-Modell — kein hartgecodetes Modell. - Echtes Streaming (
net/KaizenApi.kt+net/StreamConsumer.kt):POST /api/v1/chat, liest dentext/plain-Chunked-Stream inkrementell und strippt die Sentinels (Port vonlib/chat/stream-consumer.tsaus dem Backend, JVM-unit-getestet). - Design + Haptik + Orb unverändert — nur die Datenquelle hat sich geändert.
Bewusst noch nicht drin (siehe LATER.md): Conversation-History vom Server,
Passkey-Login nativ, Bild/Video, Biometric-Lock, WebSocket-Streaming.
Bauen & laufen
Wichtig: Auf dem echten S25 Ultra laufen lassen, nicht im Emulator — der Emulator kann keine echte Haptik, und genau die wollen wir fühlen.
- Projekt in Android Studio öffnen, Gradle syncen (neue Deps: OkHttp, kotlinx-serialization, security-crypto + INTERNET-Permission).
- S25 Ultra per Wireless Debugging oder USB verbinden.
- Run ▶ auf das Gerät → Login-Screen erscheint. Server-URL prüfen, einloggen.
DEFAULT_BASE_URL (net/ServerConfig.kt) ist die vorausgefüllte Instanz-Adresse —
bei Domainwechsel hier anpassen.
Der Test
Eine Woche nutzen. Geht der Finger anfangen hierher statt zu Gemini → wir bauen voll aus (History, Passkey, Medien = Phase 2). Wenn nicht → wir wissen billig, woran's liegt.