Keep Android BLE transcription running when the app is closed#7483
Keep Android BLE transcription running when the app is closed#7483axAilotl wants to merge 4 commits into
Conversation
Greptile SummaryThis PR keeps the Android BLE foreground service alive after the app task is removed, so pendant audio continues transcribing without the user needing to reopen the app. A new native Kotlin WebSocket streamer (
Confidence Score: 3/5The BLE background path works but The core BLE-keeps-alive mechanism is well-structured and the Flutter-side state management is careful. The unintended AndroidManifest.xml (phone-mic service Important Files Changed
Sequence DiagramsequenceDiagram
participant Flutter
participant OmiBleFgService
participant BgAudioStreamer
participant BackendWS as /v4/listen WS
Flutter->>OmiBleFgService: startService(device, requiresBond)
Flutter->>SharedPrefs: "save nativeBleStreamConfig + nativeBleStreamingEnabled=true"
Flutter->>OmiBleFgService: BLE audio frames (via CharacteristicValueListener)
Flutter->>SharedPrefs: "nativeBleForegroundReady=true"
note over BgAudioStreamer: Flutter alive — streamer idle
note over Flutter: User closes app (task removed)
Flutter->>SharedPrefs: "nativeBleForegroundReady=false"
OmiBleFgService->>OmiBleFgService: onTaskRemoved (service survives, START_STICKY)
OmiBleFgService->>BgAudioStreamer: handleCharacteristic (BLE frames)
BgAudioStreamer->>BackendWS: WebSocket connect + stream audio
BackendWS-->>BgAudioStreamer: transcript messages (cached in memory)
note over Flutter: User reopens app
Flutter->>SharedPrefs: "nativeBleForegroundReady=false (reset)"
Flutter->>BgAudioStreamer: drain() via MethodChannel
BgAudioStreamer-->>Flutter: cached transcript messages
Flutter->>Flutter: _processNewSegmentReceived (hydrate UI)
Flutter->>SharedPrefs: "nativeBleForegroundReady=true"
BgAudioStreamer->>BgAudioStreamer: stop("foreground_ready")
Reviews (1): Last reviewed commit: "fix(android): hydrate cold-start BLE tra..." | Re-trigger Greptile |
Summary
This came from feedback on a weekly Omi call: an Android user noted that other AI wearables can keep recording and transcribing even after the companion app is closed. This PR makes the stock Android Omi app behave that way for BLE pendant capture, so users do not have to reopen or check the app just to confirm transcription is still running. The pendant on/off state remains the user control point for recording and privacy.
Related issue
What changed
/v4/listenwebsocket while Flutter is not runningDevice support note
The native background path does not hardcode an individual device ID. It uses the active device ID and audio service/characteristic saved by Flutter. Packet handling is currently enabled for Omi/OpenGlass-style audio and Friend Pendant audio. Other BLE device families keep the existing foreground app behavior until their packet parsing and start/stop control flows are mirrored natively.
Testing
flutter build apk --debug --flavor devpassed.bash test.shran; the current app suite reports 465 passing and 7 unrelated baseline failures:AudioWavePainter shouldRepaint returns false when level differs by 0.01 or lessAudioWavePainter shouldRepaint returns false when level differs by clearly less than 0.01env_test.dart,env_staging_test.dart, andenv_empty_staging_test.dartfail to compile because test EnvFields stubs do not implementposthogApiKeyRingProtocol.parseAudioPayload parses a frame that exactly fills the buffer (boundary)RingProtocol.parseAudioPayload parses tightly-packed frames with no trailing padding (440B exactly)Additional tester request
This touches Android foreground service, BLE, Flutter lifecycle behavior, and has only been tested on DK2 so far. Please test on additional Android models, OS versions, and supported Omi/OpenGlass/Friend Pendant devices by starting pendant capture, closing the app task, speaking for a while, reopening the app, and confirming the pendant stays connected and the transcript catches up without losing audio.