Instructions for Claude agents working autonomously with the Android emulator. Follow these sections in order when starting from scratch, or jump to the relevant section for ongoing work.
adb devicesIf you see emulator-5554 device, the emulator is ready — skip to section 2.
If the list is empty or shows offline, you need to start the emulator.
If no AVD has been created yet:
make setupThis downloads the Android SDK, system image, and creates the kolibri-test AVD.
make emulatorThis launches the emulator in the background. Wait for it to finish booting:
adb wait-for-device
adb shell getprop sys.boot_completedPoll sys.boot_completed until it returns 1. The boot can take 30-60 seconds.
If the emulator segfaults or crashes, it's likely a GPU issue. Start with software rendering instead:
"${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}/emulator/emulator" -avd kolibri-test -gpu guest -no-snapshot &This project uses a Makefile as the primary build interface. Run make help to see all available targets.
make installThis builds the debug APK via Gradle and installs it on the connected emulator. The first build takes several minutes; subsequent builds are faster.
Watch for:
- BUILD SUCCESSFUL: Proceed to install
- Compilation errors: Fix before continuing
- Python errors: Check Chaquopy output for syntax issues
Or just build without installing:
make kolibri.apk.unsignedadb shell pm list packages | grep kolibriShould output: package:org.learningequality.Kolibri
adb shell am start -n org.learningequality.Kolibri/org.learningequality.Kolibri.WebViewActivityadb shell am force-stop org.learningequality.Kolibriadb shell pm clear org.learningequality.KolibriThis is needed after Python code changes since Chaquopy caches bytecode.
make uninstall && make install| Target | Description |
|---|---|
make setup |
Complete SDK + emulator setup (first time) |
make emulator |
Start the emulator |
make kolibri.apk.unsigned |
Build debug APK to dist/ |
make install |
Build and install debug APK |
make uninstall |
Uninstall app from device |
make logcat |
View filtered Kolibri logs |
make clean |
Clean build artifacts |
make test |
Run unit tests |
make lint |
Run Android linter |
Build + Install + Launch:
make install && adb shell am start -n org.learningequality.Kolibri/org.learningequality.Kolibri.WebViewActivityClear logs and monitor:
adb logcat -c && make logcatThis is the core workflow for autonomous UI interaction. Use /project:screenshot to run the full loop with instructions, or follow these steps:
mkdir -p /tmp/claude
adb exec-out screencap -p > /tmp/claude/screenshot.pngRead the screenshot image at /tmp/claude/screenshot.png to see the screen visually.
Kolibri is a WebView app. WebView content and native Android UI require different tools:
| What you see | Tool | Why |
|---|---|---|
| Kolibri UI (buttons, forms, nav, text) | python3 scripts/cdp_helper.py dump |
WebView DOM is invisible to uiautomator |
| Native Android dialogs (permissions, system prompts) | adb shell uiautomator dump /sdcard/window_dump.xml && adb shell cat /sdcard/window_dump.xml |
System dialogs are invisible to CDP |
Rule of thumb: If a system dialog with rounded corners is overlaying the app, use uiautomator. For everything else, use CDP.
WebView elements — click by text via CDP (no coordinate math needed):
python3 scripts/cdp_helper.py click "CONTINUE"
python3 scripts/cdp_helper.py click "EXPLORE"Native elements — tap by coordinates from uiautomator bounds:
# bounds="[137,1177][943,1331]" → center at (540, 1254)
adb shell input tap 540 1254Other interactions:
adb shell input text "<text>" # Type text (encode spaces as %s)
adb shell input swipe 540 1500 540 500 300 # Scroll down
adb shell input swipe 540 500 540 1500 300 # Scroll up
adb shell input keyevent 4 # Press BACK
adb shell input keyevent 66 # Press ENTER
adb shell input keyevent 3 # Press HOMETake another screenshot after every interaction. Confirm the UI changed as expected before proceeding.
The CDP helper (scripts/cdp_helper.py) uses Chrome DevTools Protocol over ADB to access the WebView DOM. Requires websockets (uv pip install websockets).
python3 scripts/cdp_helper.py dump # List visible DOM elements as JSON
python3 scripts/cdp_helper.py click "Button" # Click element by exact text match
python3 scripts/cdp_helper.py js "expr" # Evaluate arbitrary JavaScript| Code | Key | Code | Key |
|---|---|---|---|
| 3 | HOME | 4 | BACK |
| 19 | DPAD_UP | 20 | DPAD_DOWN |
| 21 | DPAD_LEFT | 22 | DPAD_RIGHT |
| 61 | TAB | 66 | ENTER |
| 67 | DEL | 111 | ESCAPE |
Maestro flows live in .maestro/. Use them for repeatable UI test sequences.
- Discover UI elements using the CDP helper:
python3 scripts/cdp_helper.py dump. Note thetextcontent — Maestro matches WebView elements by text whenandroidWebViewHierarchy: devtoolsis set. - Write the flow as a YAML file in
.maestro/:appId: org.learningequality.Kolibri androidWebViewHierarchy: devtools --- - launchApp - tapOn: "CONTINUE"
- Run the flow:
~/.maestro/bin/maestro test .maestro/your-flow.yaml
- Iterate: If the flow fails, screenshot to see the actual state, adjust selectors or add waits, re-run.
make maestro-installlaunchApp/clearState/clearKeychaintapOn: "text"/tapOn: { id: "resource-id" }inputText: "value"assertVisible: "text"/assertNotVisible: "text"extendedWaitUntil: { visible: "text", timeout: 30000 }scroll/swipeback/hideKeyboard
make logcatadb logcat -s python.stdout:V python.stderr:Vadb logcat -s KolibriWebView:V KolibriServer:V TaskWorkerImpl:V BaseTaskWorker:Vadb logcat -s AndroidRuntime:Eadb logcat -d -t 50adb logcat -c- Check crash logs:
adb logcat -s AndroidRuntime:E - Look for Python import errors:
adb logcat -s python.stderr:V
Chaquopy caches Python bytecode. Clear app data:
adb shell pm clear org.learningequality.KolibriOr uninstall and reinstall:
make uninstall && make installSigning key mismatch. Uninstall first:
make uninstall && make install- Open Chrome DevTools:
chrome://inspect - Find the Kolibri WebView and inspect
- Check Application > Service Workers
Check task logs:
adb logcat -s TaskWorkerImpl:V BaseTaskWorker:V WM-WorkerWrapper:Vmake setup # Creates SDK + AVD
make emulator- Make code changes
make install- Launch app and test
make logcatin another terminal- Repeat
For Python-only changes, builds are fast since Java doesn't need recompilation.
| Fact | Value |
|---|---|
| Package name | org.learningequality.Kolibri |
| Main activity | org.learningequality.Kolibri.WebViewActivity |
| AVD name | kolibri-test |
| JDK | Compilation pinned to 21 via app/build.gradle toolchain; daemon JVM pinned via gradle/gradle-daemon-jvm.properties. Set JAVA_HOME to a JDK 21 if Gradle can't auto-discover one. |
| GPU workaround | Use -gpu guest if default GPU segfaults |
| Python caching | Clear app data after Python changes (Chaquopy caches bytecode) |
| Build system | Gradle via Makefile wrappers — use make targets |
| CDP helper | python3 scripts/cdp_helper.py — requires websockets package |