EarLLM One — Build & Maintain
Overview
Build, maintain, and extend the EarLLM One Android project — a Kotlin/Compose app that connects Bluetooth earbuds to an LLM via voice pipeline.
When to Use This Skill
- When the user mentions "earllm" or related topics
- When the user mentions "earbudllm" or related topics
- When the user mentions "earbud app" or related topics
- When the user mentions "voice pipeline kotlin" or related topics
- When the user mentions "bluetooth audio android" or related topics
- When the user mentions "sco microphone" or related topics
Do Not Use This Skill When
- The task is unrelated to earllm build
- A simpler, more specific tool can handle the request
- The user needs general-purpose assistance without domain expertise
How It Works
EarLLM One is a multi-module Android app (Kotlin + Jetpack Compose) that captures voice from Bluetooth earbuds, transcribes it, sends it to an LLM, and speaks the response back.
Project Location
C:\Users\renat\earbudllm
Module Dependency Graph
app ──→ voice ──→ audio ──→ core-logging
│ │
├──→ bluetooth ──→ core-logging
└──→ llm ──→ core-logging
Modules And Key Files
| Module |
Purpose |
Key Files |
| core-logging |
Structured logging, performance tracking |
EarLogger.kt, PerformanceTracker.kt |
| bluetooth |
BT discovery, pairing, A2DP/HFP profiles |
BluetoothController.kt, BluetoothState.kt, BluetoothPermissions.kt |
| audio |
Audio routing (SCO/BLE), capture, headset buttons |
AudioRouteController.kt, VoiceCaptureController.kt, HeadsetButtonController.kt |
| voice |
STT (SpeechRecognizer + Vosk stub), TTS, pipeline |
SpeechToTextController.kt, TextToSpeechController.kt, VoicePipeline.kt |
| llm |
LLM interface, stub, OpenAI-compatible client |
LlmClient.kt, StubLlmClient.kt, RealLlmClient.kt, SecureTokenStore.kt |
| app |
UI, ViewModel, Service, Settings, all screens |
MainViewModel.kt, EarLlmForegroundService.kt, 6 Compose screens |
Build Configuration
- SDK: minSdk 26, targetSdk 34, compileSdk 34
- Build tools: AGP 8.2.2, Kotlin 1.9.22, Gradle 8.5
- Compose BOM: 2024.02.00
- Key deps: OkHttp, AndroidX Security (EncryptedSharedPreferences), DataStore, Media
Target Hardware
| Device |
Model |
Key Details |
| Phone |
Samsung Galaxy S24 Ultra |
Android 14, One UI 6.1, Snapdragon 8 Gen 3 |
| Earbuds |
Xiaomi Redmi Buds 6 Pro |
BT 5.3, A2DP/HFP/AVRCP, ANC, LDAC |
Critical Technical Facts
These are verified facts from official documentation and device testing. Treat them as ground truth when making decisions:
Bluetooth SCO is limited to 8kHz mono input on most devices. Some support 16kHz mSBC. BLE Audio (Android 12+, TYPE_BLE_HEADSET = 26) supports up to 32kHz stereo. Always prefer BLE Audio when available.
startBluetoothSco() is deprecated since Android 12 (API 31). Use AudioManager.setCommunicationDevice(AudioDeviceInfo) and clearCommunicationDevice() instead. The project already implements both paths in AudioRouteController.kt.
Samsung One UI 7/8 has a known HFP corruption bug where A2DP playback corrupts the SCO link. The app handles this with silence detection and automatic fallback to the phone's built-in mic.
Redmi Buds 6 Pro tap controls must be set to "Default" (Play/Pause) in the Xiaomi Earbuds companion app. If set to ANC or custom functions, events are handled internally by the earbuds and never reach Android.
Android 14+ requires FOREGROUND_SERVICE_MICROPHONE permission and foregroundServiceType="microphone" in the service declaration. RECORD_AUDIO must be granted before startForeground().
VOICE_COMMUNICATION audio source enables AEC (Acoustic Echo Cancellation), which is critical to prevent TTS audio output from feeding back into the STT microphone input. Never change this source without understanding the echo implications.
Never play TTS (A2DP) while simultaneously recording via SCO. The correct sequence is: stop playback → switch to HFP → record → switch to A2DP → play response.
Data Flow
Headset button tap
→ MediaSession (HeadsetButtonController)
→ TapAction.RECORD_TOGGLE
→ VoicePipeline.toggleRecording()
→ VoiceCaptureController captures PCM (16kHz mono)
→ stopRecording() returns ByteArray
→ SpeechToTextController.transcribe(pcmData)
→ LlmClient.chat(messages)
→ TextToSpeechController.speak(response)
→ Audio output via A2DP to earbuds
Adding A New Feature
- Identify which module(s) are affected
- Read existing code in those modules first
- Follow the StateFlow pattern — expose state via
MutableStateFlow / StateFlow
- Update
MainViewModel.kt if the feature needs UI integration
- Add unit tests in the module's
src/test/ directory
- Update docs if the feature changes behavior
Modifying Audio Capture
VoiceCaptureController.kt handles PCM recording at 16kHz mono
- WAV headers use hex byte values (not char literals) to avoid shell quoting issues
- VU meter: RMS calculation → dB conversion → normalized 0-1 range
- Buffer size:
getMinBufferSize().coerceAtLeast(4096)
Changing Bluetooth Behavior
BluetoothController.kt manages discovery, pairing, profile proxies
- Earbuds detection uses name heuristics: "buds", "earbuds", "tws", "pods", "ear"
- Always handle both Bluetooth Classic and BLE Audio paths
Modifying The Llm Integration
LlmClient.kt defines the interface — keep it generic
StubLlmClient.kt for offline testing (500ms simulated delay)
RealLlmClient.kt uses OkHttp to call OpenAI-compatible APIs
- API keys stored in
SecureTokenStore.kt (EncryptedSharedPreferences)
Generating A Build Artifact
After code changes, regenerate the ZIP:
## From Project Root
powershell -Command "Remove-Item 'EarLLM_One_v1.0.zip' -Force -ErrorAction SilentlyContinue; Compress-Archive -Path (Get-ChildItem -Exclude '*.zip','_zip_verify','.git') -DestinationPath 'EarLLM_One_v1.0.zip' -Force"
Running Tests
./gradlew test --stacktrace # Unit tests
./gradlew connectedAndroidTest # Instrumented tests (device required)
Phase 2 Roadmap
- Real-time streaming voice conversation with LLM through earbuds
- Smart assistant: categorize speech into meetings, shopping lists, memos, emails
- Vosk offline STT integration (currently stubbed)
- Wake-word detection to avoid keeping SCO open continuously
- Streaming TTS (Android built-in TTS does NOT support streaming)
Stt Engine Reference
| Engine |
Size |
WER |
Streaming |
Best For |
| Vosk small-en |
40 MB |
~10% |
Yes |
Real-time mobile |
| Vosk lgraph |
128 MB |
~8% |
Yes |
Better accuracy |
| Whisper tiny |
40 MB |
~10-12% |
No (batch) |
Post-utterance polish |
| Android SpeechRecognizer |
0 MB |
varies |
Yes |
Online, no extra deps |
Best Practices
- Provide clear, specific context about your project and requirements
- Review all suggestions before applying them to production code
- Combine with other complementary skills for comprehensive analysis
Common Pitfalls
- Using this skill for tasks outside its domain expertise
- Applying recommendations without understanding your specific context
- Not providing enough project context for accurate analysis
AGI Framework Integration
Adapted for @techwavedev/agi-agent-kit
Original source: antigravity-awesome-skills
Memory-First Protocol
Retrieve prior design decisions (color palettes, typography, spacing scales) to maintain visual consistency across sessions. Cache generated design tokens.
# Check for prior frontend/design context before starting
python3 execution/memory_manager.py auto --query "design system decisions and component patterns for Earllm Build"
Storing Results
After completing work, store frontend/design decisions for future sessions:
python3 execution/memory_manager.py store \
--content "Design system: adopted 8px grid, Inter font family, HSL color tokens with dark mode support" \
--type decision --project <project> \
--tags earllm-build frontend
Multi-Agent Collaboration
Share design decisions with backend agents (API contract changes) and QA agents (visual regression baselines).
python3 execution/cross_agent_context.py store \
--agent "<your-agent>" \
--action "Implemented UI components — new design system with accessibility compliance (WCAG 2.1 AA)" \
--project <project>
Design Memory Persistence
Store design system tokens and component decisions in Qdrant so any agent on any platform (Claude, Gemini, Cursor) can retrieve and apply consistent styling.
1---2name: earllm-build3description: "Build, maintain, and extend the EarLLM One Android project — a Kotlin/Compose app that connects Bluetooth earbuds to an LLM via voice pipeline."4risk: safe5source: community6date_added: '2026-03-06'7author: renat8tags:9- android10- kotlin11- bluetooth12- llm13- voice14tools:15- claude-code16- antigravity17- cursor18- gemini-cli19- codex-cli20---2122# EarLLM One — Build & Maintain2324## Overview2526Build, maintain, and extend the EarLLM One Android project — a Kotlin/Compose app that connects Bluetooth earbuds to an LLM via voice pipeline.2728## When to Use This Skill2930- When the user mentions "earllm" or related topics31- When the user mentions "earbudllm" or related topics32- When the user mentions "earbud app" or related topics33- When the user mentions "voice pipeline kotlin" or related topics34- When the user mentions "bluetooth audio android" or related topics35- When the user mentions "sco microphone" or related topics3637## Do Not Use This Skill When3839- The task is unrelated to earllm build40- A simpler, more specific tool can handle the request41- The user needs general-purpose assistance without domain expertise4243## How It Works4445EarLLM One is a multi-module Android app (Kotlin + Jetpack Compose) that captures voice from Bluetooth earbuds, transcribes it, sends it to an LLM, and speaks the response back.4647## Project Location4849`C:\Users\renat\earbudllm`5051## Module Dependency Graph5253```54app ──→ voice ──→ audio ──→ core-logging55 │ │56 ├──→ bluetooth ──→ core-logging57 └──→ llm ──→ core-logging58```5960## Modules And Key Files6162| Module | Purpose | Key Files |63|--------|---------|-----------|64| **core-logging** | Structured logging, performance tracking | `EarLogger.kt`, `PerformanceTracker.kt` |65| **bluetooth** | BT discovery, pairing, A2DP/HFP profiles | `BluetoothController.kt`, `BluetoothState.kt`, `BluetoothPermissions.kt` |66| **audio** | Audio routing (SCO/BLE), capture, headset buttons | `AudioRouteController.kt`, `VoiceCaptureController.kt`, `HeadsetButtonController.kt` |67| **voice** | STT (SpeechRecognizer + Vosk stub), TTS, pipeline | `SpeechToTextController.kt`, `TextToSpeechController.kt`, `VoicePipeline.kt` |68| **llm** | LLM interface, stub, OpenAI-compatible client | `LlmClient.kt`, `StubLlmClient.kt`, `RealLlmClient.kt`, `SecureTokenStore.kt` |69| **app** | UI, ViewModel, Service, Settings, all screens | `MainViewModel.kt`, `EarLlmForegroundService.kt`, 6 Compose screens |7071## Build Configuration7273- **SDK**: minSdk 26, targetSdk 34, compileSdk 3474- **Build tools**: AGP 8.2.2, Kotlin 1.9.22, Gradle 8.575- **Compose BOM**: 2024.02.0076- **Key deps**: OkHttp, AndroidX Security (EncryptedSharedPreferences), DataStore, Media7778## Target Hardware7980| Device | Model | Key Details |81|--------|-------|-------------|82| Phone | Samsung Galaxy S24 Ultra | Android 14, One UI 6.1, Snapdragon 8 Gen 3 |83| Earbuds | Xiaomi Redmi Buds 6 Pro | BT 5.3, A2DP/HFP/AVRCP, ANC, LDAC |8485## Critical Technical Facts8687These are verified facts from official documentation and device testing. Treat them as ground truth when making decisions:88891. **Bluetooth SCO is limited to 8kHz mono input** on most devices. Some support 16kHz mSBC. BLE Audio (Android 12+, `TYPE_BLE_HEADSET = 26`) supports up to 32kHz stereo. Always prefer BLE Audio when available.90912. **`startBluetoothSco()` is deprecated since Android 12 (API 31).** Use `AudioManager.setCommunicationDevice(AudioDeviceInfo)` and `clearCommunicationDevice()` instead. The project already implements both paths in `AudioRouteController.kt`.92933. **Samsung One UI 7/8 has a known HFP corruption bug** where A2DP playback corrupts the SCO link. The app handles this with silence detection and automatic fallback to the phone's built-in mic.94954. **Redmi Buds 6 Pro tap controls must be set to "Default" (Play/Pause)** in the Xiaomi Earbuds companion app. If set to ANC or custom functions, events are handled internally by the earbuds and never reach Android.96975. **Android 14+ requires `FOREGROUND_SERVICE_MICROPHONE` permission** and `foregroundServiceType="microphone"` in the service declaration. `RECORD_AUDIO` must be granted before `startForeground()`.98996. **`VOICE_COMMUNICATION` audio source enables AEC** (Acoustic Echo Cancellation), which is critical to prevent TTS audio output from feeding back into the STT microphone input. Never change this source without understanding the echo implications.1001017. **Never play TTS (A2DP) while simultaneously recording via SCO.** The correct sequence is: stop playback → switch to HFP → record → switch to A2DP → play response.102103## Data Flow104105```106Headset button tap107 → MediaSession (HeadsetButtonController)108 → TapAction.RECORD_TOGGLE109 → VoicePipeline.toggleRecording()110 → VoiceCaptureController captures PCM (16kHz mono)111 → stopRecording() returns ByteArray112 → SpeechToTextController.transcribe(pcmData)113 → LlmClient.chat(messages)114 → TextToSpeechController.speak(response)115 → Audio output via A2DP to earbuds116```117118## Adding A New Feature1191201. Identify which module(s) are affected1212. Read existing code in those modules first1223. Follow the StateFlow pattern — expose state via `MutableStateFlow` / `StateFlow`1234. Update `MainViewModel.kt` if the feature needs UI integration1245. Add unit tests in the module's `src/test/` directory1256. Update docs if the feature changes behavior126127## Modifying Audio Capture128129- `VoiceCaptureController.kt` handles PCM recording at 16kHz mono130- WAV headers use hex byte values (not char literals) to avoid shell quoting issues131- VU meter: RMS calculation → dB conversion → normalized 0-1 range132- Buffer size: `getMinBufferSize().coerceAtLeast(4096)`133134## Changing Bluetooth Behavior135136- `BluetoothController.kt` manages discovery, pairing, profile proxies137- Earbuds detection uses name heuristics: "buds", "earbuds", "tws", "pods", "ear"138- Always handle both Bluetooth Classic and BLE Audio paths139140## Modifying The Llm Integration141142- `LlmClient.kt` defines the interface — keep it generic143- `StubLlmClient.kt` for offline testing (500ms simulated delay)144- `RealLlmClient.kt` uses OkHttp to call OpenAI-compatible APIs145- API keys stored in `SecureTokenStore.kt` (EncryptedSharedPreferences)146147## Generating A Build Artifact148149After code changes, regenerate the ZIP:150```powershell151152## From Project Root153154powershell -Command "Remove-Item 'EarLLM_One_v1.0.zip' -Force -ErrorAction SilentlyContinue; Compress-Archive -Path (Get-ChildItem -Exclude '*.zip','_zip_verify','.git') -DestinationPath 'EarLLM_One_v1.0.zip' -Force"155```156157## Running Tests158159```bash160./gradlew test --stacktrace # Unit tests161./gradlew connectedAndroidTest # Instrumented tests (device required)162```163164## Phase 2 Roadmap165166- Real-time streaming voice conversation with LLM through earbuds167- Smart assistant: categorize speech into meetings, shopping lists, memos, emails168- Vosk offline STT integration (currently stubbed)169- Wake-word detection to avoid keeping SCO open continuously170- Streaming TTS (Android built-in TTS does NOT support streaming)171172## Stt Engine Reference173174| Engine | Size | WER | Streaming | Best For |175|--------|------|-----|-----------|----------|176| Vosk small-en | 40 MB | ~10% | Yes | Real-time mobile |177| Vosk lgraph | 128 MB | ~8% | Yes | Better accuracy |178| Whisper tiny | 40 MB | ~10-12% | No (batch) | Post-utterance polish |179| Android SpeechRecognizer | 0 MB | varies | Yes | Online, no extra deps |180181## Best Practices182183- Provide clear, specific context about your project and requirements184- Review all suggestions before applying them to production code185- Combine with other complementary skills for comprehensive analysis186187## Common Pitfalls188189- Using this skill for tasks outside its domain expertise190- Applying recommendations without understanding your specific context191- Not providing enough project context for accurate analysis192193---194195<!-- AGI-INTEGRATION-START -->196197## AGI Framework Integration198199> **Adapted for [@techwavedev/agi-agent-kit](https://www.npmjs.com/package/@techwavedev/agi-agent-kit)**200> Original source: [antigravity-awesome-skills](https://github.com/sickn33/antigravity-awesome-skills)201202### Memory-First Protocol203204Retrieve prior design decisions (color palettes, typography, spacing scales) to maintain visual consistency across sessions. Cache generated design tokens.205206```bash207# Check for prior frontend/design context before starting208python3 execution/memory_manager.py auto --query "design system decisions and component patterns for Earllm Build"209```210211### Storing Results212213After completing work, store frontend/design decisions for future sessions:214215```bash216python3 execution/memory_manager.py store \217 --content "Design system: adopted 8px grid, Inter font family, HSL color tokens with dark mode support" \218 --type decision --project <project> \219 --tags earllm-build frontend220```221222### Multi-Agent Collaboration223224Share design decisions with backend agents (API contract changes) and QA agents (visual regression baselines).225226```bash227python3 execution/cross_agent_context.py store \228 --agent "<your-agent>" \229 --action "Implemented UI components — new design system with accessibility compliance (WCAG 2.1 AA)" \230 --project <project>231```232233### Design Memory Persistence234235Store design system tokens and component decisions in Qdrant so any agent on any platform (Claude, Gemini, Cursor) can retrieve and apply consistent styling.236237<!-- AGI-INTEGRATION-END -->