[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-fc426fd1-54bd-4801-b98d-897211d4d59f":3,"$fgUdDISOdyyHER-VKdzh5-86j_Lb67VNjqUd0sqkidEc":43},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":34},"fc426fd1-54bd-4801-b98d-897211d4d59f","earllm-build","构建、维护和扩展EarLLM One Android项目——一个连接蓝牙耳机到LLM的语音管道的Kotlin\u002FCompose应用程序。","cat_coding_frontend","mod_coding","sickn33,coding","---\nname: earllm-build\ndescription: \"Build, maintain, and extend the EarLLM One Android project — a Kotlin\u002FCompose app that connects Bluetooth earbuds to an LLM via voice pipeline.\"\nrisk: safe\nsource: community\ndate_added: '2026-03-06'\nauthor: renat\ntags:\n- android\n- kotlin\n- bluetooth\n- llm\n- voice\ntools:\n- claude-code\n- antigravity\n- cursor\n- gemini-cli\n- codex-cli\n---\n\n# EarLLM One — Build & Maintain\n\n## Overview\n\nBuild, maintain, and extend the EarLLM One Android project — a Kotlin\u002FCompose app that connects Bluetooth earbuds to an LLM via voice pipeline.\n\n## When to Use This Skill\n\n- When the user mentions \"earllm\" or related topics\n- When the user mentions \"earbudllm\" or related topics\n- When the user mentions \"earbud app\" or related topics\n- When the user mentions \"voice pipeline kotlin\" or related topics\n- When the user mentions \"bluetooth audio android\" or related topics\n- When the user mentions \"sco microphone\" or related topics\n\n## Do Not Use This Skill When\n\n- The task is unrelated to earllm build\n- A simpler, more specific tool can handle the request\n- The user needs general-purpose assistance without domain expertise\n\n## How It Works\n\nEarLLM 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.\n\n## Project Location\n\n`C:\\Users\\renat\\earbudllm`\n\n## Module Dependency Graph\n\n```\napp ──→ voice ──→ audio ──→ core-logging\n  │       │\n  ├──→ bluetooth ──→ core-logging\n  └──→ llm ──→ core-logging\n```\n\n## Modules And Key Files\n\n| Module | Purpose | Key Files |\n|--------|---------|-----------|\n| **core-logging** | Structured logging, performance tracking | `EarLogger.kt`, `PerformanceTracker.kt` |\n| **bluetooth** | BT discovery, pairing, A2DP\u002FHFP profiles | `BluetoothController.kt`, `BluetoothState.kt`, `BluetoothPermissions.kt` |\n| **audio** | Audio routing (SCO\u002FBLE), capture, headset buttons | `AudioRouteController.kt`, `VoiceCaptureController.kt`, `HeadsetButtonController.kt` |\n| **voice** | STT (SpeechRecognizer + Vosk stub), TTS, pipeline | `SpeechToTextController.kt`, `TextToSpeechController.kt`, `VoicePipeline.kt` |\n| **llm** | LLM interface, stub, OpenAI-compatible client | `LlmClient.kt`, `StubLlmClient.kt`, `RealLlmClient.kt`, `SecureTokenStore.kt` |\n| **app** | UI, ViewModel, Service, Settings, all screens | `MainViewModel.kt`, `EarLlmForegroundService.kt`, 6 Compose screens |\n\n## Build Configuration\n\n- **SDK**: minSdk 26, targetSdk 34, compileSdk 34\n- **Build tools**: AGP 8.2.2, Kotlin 1.9.22, Gradle 8.5\n- **Compose BOM**: 2024.02.00\n- **Key deps**: OkHttp, AndroidX Security (EncryptedSharedPreferences), DataStore, Media\n\n## Target Hardware\n\n| Device | Model | Key Details |\n|--------|-------|-------------|\n| Phone | Samsung Galaxy S24 Ultra | Android 14, One UI 6.1, Snapdragon 8 Gen 3 |\n| Earbuds | Xiaomi Redmi Buds 6 Pro | BT 5.3, A2DP\u002FHFP\u002FAVRCP, ANC, LDAC |\n\n## Critical Technical Facts\n\nThese are verified facts from official documentation and device testing. Treat them as ground truth when making decisions:\n\n1. **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.\n\n2. **`startBluetoothSco()` is deprecated since Android 12 (API 31).** Use `AudioManager.setCommunicationDevice(AudioDeviceInfo)` and `clearCommunicationDevice()` instead. The project already implements both paths in `AudioRouteController.kt`.\n\n3. **Samsung One UI 7\u002F8 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.\n\n4. **Redmi Buds 6 Pro tap controls must be set to \"Default\" (Play\u002FPause)** in the Xiaomi Earbuds companion app. If set to ANC or custom functions, events are handled internally by the earbuds and never reach Android.\n\n5. **Android 14+ requires `FOREGROUND_SERVICE_MICROPHONE` permission** and `foregroundServiceType=\"microphone\"` in the service declaration. `RECORD_AUDIO` must be granted before `startForeground()`.\n\n6. **`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.\n\n7. **Never play TTS (A2DP) while simultaneously recording via SCO.** The correct sequence is: stop playback → switch to HFP → record → switch to A2DP → play response.\n\n## Data Flow\n\n```\nHeadset button tap\n  → MediaSession (HeadsetButtonController)\n  → TapAction.RECORD_TOGGLE\n  → VoicePipeline.toggleRecording()\n  → VoiceCaptureController captures PCM (16kHz mono)\n  → stopRecording() returns ByteArray\n  → SpeechToTextController.transcribe(pcmData)\n  → LlmClient.chat(messages)\n  → TextToSpeechController.speak(response)\n  → Audio output via A2DP to earbuds\n```\n\n## Adding A New Feature\n\n1. Identify which module(s) are affected\n2. Read existing code in those modules first\n3. Follow the StateFlow pattern — expose state via `MutableStateFlow` \u002F `StateFlow`\n4. Update `MainViewModel.kt` if the feature needs UI integration\n5. Add unit tests in the module's `src\u002Ftest\u002F` directory\n6. Update docs if the feature changes behavior\n\n## Modifying Audio Capture\n\n- `VoiceCaptureController.kt` handles PCM recording at 16kHz mono\n- WAV headers use hex byte values (not char literals) to avoid shell quoting issues\n- VU meter: RMS calculation → dB conversion → normalized 0-1 range\n- Buffer size: `getMinBufferSize().coerceAtLeast(4096)`\n\n## Changing Bluetooth Behavior\n\n- `BluetoothController.kt` manages discovery, pairing, profile proxies\n- Earbuds detection uses name heuristics: \"buds\", \"earbuds\", \"tws\", \"pods\", \"ear\"\n- Always handle both Bluetooth Classic and BLE Audio paths\n\n## Modifying The Llm Integration\n\n- `LlmClient.kt` defines the interface — keep it generic\n- `StubLlmClient.kt` for offline testing (500ms simulated delay)\n- `RealLlmClient.kt` uses OkHttp to call OpenAI-compatible APIs\n- API keys stored in `SecureTokenStore.kt` (EncryptedSharedPreferences)\n\n## Generating A Build Artifact\n\nAfter code changes, regenerate the ZIP:\n```powershell\n\n## From Project Root\n\npowershell -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\"\n```\n\n## Running Tests\n\n```bash\n.\u002Fgradlew test --stacktrace          # Unit tests\n.\u002Fgradlew connectedAndroidTest       # Instrumented tests (device required)\n```\n\n## Phase 2 Roadmap\n\n- Real-time streaming voice conversation with LLM through earbuds\n- Smart assistant: categorize speech into meetings, shopping lists, memos, emails\n- Vosk offline STT integration (currently stubbed)\n- Wake-word detection to avoid keeping SCO open continuously\n- Streaming TTS (Android built-in TTS does NOT support streaming)\n\n## Stt Engine Reference\n\n| Engine | Size | WER | Streaming | Best For |\n|--------|------|-----|-----------|----------|\n| Vosk small-en | 40 MB | ~10% | Yes | Real-time mobile |\n| Vosk lgraph | 128 MB | ~8% | Yes | Better accuracy |\n| Whisper tiny | 40 MB | ~10-12% | No (batch) | Post-utterance polish |\n| Android SpeechRecognizer | 0 MB | varies | Yes | Online, no extra deps |\n\n## Best Practices\n\n- Provide clear, specific context about your project and requirements\n- Review all suggestions before applying them to production code\n- Combine with other complementary skills for comprehensive analysis\n\n## Common Pitfalls\n\n- Using this skill for tasks outside its domain expertise\n- Applying recommendations without understanding your specific context\n- Not providing enough project context for accurate analysis\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,126,346,"2026-05-16 13:16:18",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"编程开发","coding","mdi-code-braces","代码生成、调试、审查，提升开发效率",2,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"前端开发","frontend","mdi-language-html5","HTML\u002FCSS\u002FJavaScript\u002F框架相关",1,96,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"32b85d70-f56b-4863-84e4-bb9e2a9da440","1.0.0","earllm-build.zip",3928,"uploads\u002Fskills\u002Ffc426fd1-54bd-4801-b98d-897211d4d59f\u002Fearllm-build.zip","98627d75a4988fd29b47894980e4a7b2352f898b059720b239102a996228f30b","[{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":8242}]",{"code":44,"message":45,"data":46},200,"success",{"items":47,"stats":48,"page":51},[],{"averageRating":49,"totalRatings":49,"ratingCounts":50},0,[49,49,49,49,49],{"limit":52,"offset":49,"hasMore":53,"nextOffset":52,"ratedOnly":16},15,false]