Audio recording:
- Mic button wired to MediaRecorder (AAC/M4A, 44.1kHz/128kbps)
- Tap to start, tap again to stop — auto-uploads as audio attachment
- RECORD_AUDIO permission with runtime request
- Recording state: red stop-square on mic button
TTS read-aloud:
- Volume button on assistant messages → POST /api/v1/audio-gen
- Gemini-TTS generates speech server-side, MediaPlayer streams it
- Tap again to stop playback, accent-colored icon when playing
- Graceful error handling (Toast on failure)
Supporting changes:
- KaizenApi.generateSpeech() for /api/v1/audio-gen endpoint
- KaizenIcons.Volume2 added (Lucide)
- DE/EN strings for recording, permission, TTS states
adjustResize didn't work with enableEdgeToEdge (input hidden
behind keyboard). imePadding inflated the dock (input too high).
New approach: read WindowInsets.ime bottom value directly and
apply it as negative Y offset on the dock. The dock slides up
by exactly the keyboard height — no inflation, no layout changes.
windowSoftInputMode=adjustNothing (edge-to-edge default).
When keyboard closed: offset = nav bar height (gesture bar).
When keyboard open: offset = keyboard height (includes nav bar).
imePadding() on Compose Box/Column with overlapping children
(BottomCenter-aligned dock over fillMaxSize LazyColumn) caused
the input to float to the top of the screen. The Compose inset
system doesn't work well with this overlay layout pattern.
Fix: remove all imePadding() usage, set windowSoftInputMode=
adjustResize in AndroidManifest. Android resizes the window
itself when the keyboard opens — the entire layout naturally
reflows, dock stays at the bottom, chat area shrinks. This is
the standard approach for chat apps with edge-to-edge.
Replaces the Phase-0 fakeReply mock with the real v1 API. Deployment-agnostic:
one binary, one Authorization: Bearer contract, server-URL field switches between
the official instance and any self-host.
- auth/LoginScreen: server-URL + email + password -> POST /api/v1/auth/token
- net/SecureStore: kzn_ token in EncryptedSharedPreferences (KeyStore AES-256-GCM)
- net/SessionViewModel: snapshot-state login/logout routing, restores on launch
- net/KaizenApi: POST /api/v1/chat streaming + GET /api/v1/me for the default model
- net/StreamConsumer: Kotlin port of lib/chat/stream-consumer.ts (JVM-unit-tested)
- deps: okhttp, kotlinx-serialization, security-crypto; INTERNET permission
Code-complete; not yet built/run on device (no Android SDK on the dev Mac).