Dev Investigation Notes
Connecting Claude Code to an Android tablet: where do you start with the voice assistant app?
This is a translated article. Read the original in Korean ↗
This is a design discussion about moving a PC voice assistant—a Windows Python program that listens for the wake word hey_jarvis, passes a sentence to claude -p, and reads the response aloud—to an Android tablet. This article records design decisions about what to build first, not an app that was actually built or tested on a tablet. The goal was to have a tablet continuously listening while docked and charging, but the questions of where to run Claude Code and what the app should handle had become tangled together.
Conclusion
The app handles only the ears and mouth (wake word, STT, TTS, and screen), while the brain that generates answers sits behind a BrainClient interface. Build the app first using MockBrain (a stand-in that echoes what it hears). Whether the brain runs in Termux on the tablet or on a PC server, the app just sends one line of text and receives one back, so the work on the ears and mouth carries over.
Next step in one sentence: Upgrade the development PC's JDK to 17 or later (currently 16), set up the Android SDK and adb, then build an app that runs with MockBrain.
What to verify yourself (things this article cannot guarantee because they are not in the record)
These are the things to check yourself before building the MockBrain app or connecting the real Claude. The session does not provide values for the items below; only the Termux values in the final table were separately checked against the official Termux wiki (RUN_COMMAND Intent).
- Wake word detection: The session proposed running an openWakeWord
.tflitemodel with TensorFlow Lite and continuously processing 16kHz microphone audio. Whether it actually runs on Android and which library to use for integration remain unverified. - Continuous use of
SpeechRecognizer: The session said that “enabling on-device recognition also makes offline use possible,” but this was never tested. You need to check whether the session must restart each time recognition ends and whether the device has a Korean on-device model. - Permissions: The proposed requirements were
RECORD_AUDIO,FOREGROUND_SERVICE,FOREGROUND_SERVICE_MICROPHONE,POST_NOTIFICATIONS, a foreground service declaration with themicrophonetype on Android 14 or later, and an exemption from battery optimization. - Build versions: All that was confirmed was that the development PC had JDK 16 and needed 17 or later, and that adb, the SDK, Android Studio, and Gradle were missing. No AGP, Gradle, or minSdk values were decided.
- Tablet heat and speed: The warning was that
claude -plaunches a new Node process on every call and could be noticeably slower than on a PC. There are no measurements.
Connecting Claude through Termux later
The setup sequence proposed in the session was Install Termux → Install Node.js → npm i -g @anthropic-ai/claude-code → Log in to claude. Login uses browser OAuth once at the start; next, check that claude -p runs on its own in Termux and measure its response time. The session only described the app↔Termux bridge (RUN_COMMAND) as “quite tricky to set up” and provided no values.
These values were checked against the official Termux wiki (RUN_COMMAND Intent). They were not tested in this session or on a physical device.
| Item | Value |
|---|---|
| Termux configuration | allow-external-apps=true in ~/.termux/termux.properties |
| App manifest | <uses-permission android:name="com.termux.permission.RUN_COMMAND" /> |
| Intent | action com.termux.RUN_COMMAND, service com.termux.app.RunCommandService, package com.termux |
| Required extra | com.termux.RUN_COMMAND_PATH — absolute path to the file to execute |
| Optional extras | com.termux.RUN_COMMAND_ARGUMENTS (argument array), com.termux.RUN_COMMAND_WORKDIR (working directory), com.termux.RUN_COMMAND_BACKGROUND (whether to run in the background), com.termux.RUN_COMMAND_PENDING_INTENT (for receiving results) |
| Retrieving results | stdout, stderr, and exit code from the PendingIntent result bundle (Termux v0.109 or later). See the wiki for exact key names and example code |
| Permissions | The user grants the RUN_COMMAND permission in app settings. targetSdk 30 or later requires a package query declaration in the manifest |
Situation
The initial plan was for the tablet to handle only the wake word, STT, and TTS, sending text to a PC and receiving an answer. That changed to “run Claude Code on the tablet itself without going through a PC.” Since Claude Code is a Node.js CLI and cannot be put into an APK, the proposal was to run it in Termux, a Linux terminal environment for Android, and have the app issue commands through the RUN_COMMAND intent.
Symptom
The issue was confusion about the architecture, not a runtime error. Someone asked, “So using Termux doesn't mean we're developing an app?” and then asked whether the app would pass input to “Claude Code running in Termux.”
Cause
Building an app that listens and speaks and connecting an answer engine were being treated as one task. Termux does not replace app development; it handles just one thing the app cannot do on its own: running Claude Code. Nor does Claude Code stay running inside it. Normally, it is simply installed; each time the app passes it a sentence, claude -p starts fresh and exits when finished. The app's microphone is what stays listening.
Fix
This was a decision about the scope of the first development phase, not an implementation. Separate the brain through an interface, use MockBrain now, and plug in TermuxBrain or HttpBrain later.
Scope of the first app: an always-on microphone Foreground Service, wake word detection, SpeechRecognizer STT, BrainClient.ask(text), TextToSpeech, and a screen showing status (대기중/듣는중/처리중 (Waiting/Listening/Processing)) and recognition/response logs.
Checklist—not yet carried out
- [ ] Prepare the development PC: install a JDK so that
java -versionreports 17 or later, install the Android SDK and SDK Platform Tools, then checkadb version. Set up Gradle and Android Studio. - [ ] Create a Kotlin Android project separate from the Python program.
- [ ] Wire up
BrainClientandMockBrain, and verify the input and stand-in response in the on-screen log. - [ ]
SpeechRecognizerresult →ask→TextToSpeech, with status logs. Whether recognition needs to restart each time it ends is unverified, so this article provides no code for it. - [ ] Foreground Service + the permissions above + wake word detection. This could be a blocker: Not even a candidate library has been chosen for integrating the wake word model on Android. Finish the
MockBrainapp from the earlier steps first, then investigate this item separately (see “What to verify yourself”). - [ ] Build the APK, install it with adb, and verify the sequence: wake word → recognition → stand-in response → voice playback.
The following is a Kotlin design sketch based on the proposal, not original implementation code. The suspend modifier and String return type are examples supplied by this article.
interface BrainClient {
suspend fun ask(text: String): String
}
class MockBrain : BrainClient {
override suspend fun ask(text: String): String =
"네, 들었어요. '$text'라고 하셨네요"
}
// Replace only the implementation on this line when connecting the real engine.
val brain: BrainClient = MockBrain()
“네, 들었어요. '$text'라고 하셨네요” (“Yes, I heard you. You said '$text'.”)
Reporter's Notes — deciding to build the ears and mouth first
The design discussion has been reconstructed as a working meeting among four people. There was no reproduction on a physical device.
claude -p? I want the tablet to run it on its own. If we install Termux, Claude Code runs there, so don't we no longer need an app? claude -p starts fresh and exits once the answer is ready. Only the app's microphone stays awake all the time. RUN_COMMAND is? Let's connect that first. interface and have MockBrain echo what it hears for now. Later, swap in TermuxBrain or HttpBrain, and the app stays the same. MockBrain app, then wake word research. We can decide where Claude lives after that. As long as we leave a slot for the brain, postponing that decision won't block work on the app.