waydroid-adb
Skill0 starsChanged 55 days agoMITView on GitHub
Control Waydroid Android emulator via ADB for Expo/React Native development. This skill should be used when needing to take screenshots of Waydroid, verify app rendering, install APKs, or interact with the Android emulator. Triggers on tasks involving Android emulator verification, mobile app visual testing, or Waydroid troubleshooting.
We split this skill into the rules it gives an agent
14 rules. About 0 are common, 7 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈7
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 34
After dev build is installed, start the full dev environment:
- Very specific: line 50
After npm install of packages with native code
- Distinctive: line 56
When you observe any console error, network error, debugger warning, or unexpected log output during emulator sessions, you MUST either fix …
- Very specific: line 155
Buttons: Use content-desc (set via accessibilityLabel prop)
- Very specific: line 156
Text: Use text attribute
- Very specific: line 157
Views with testID: Check resource-id (may have package prefix)
- Distinctive: line 170
IMPORTANT: When filling forms (login, signup, etc.):
- Distinctive: line 220
Run startup script (if not already done)
- Very specific: line 234
Check Metro is running: ss -tlnp | grep 8081
- Very specific: line 235
Check adb reverse: adb reverse --list
- Very specific: line 249
All scripts are in ~/.claude/skills/waydroid-adb/scripts/ and can be whitelisted.
- Distinctive: line 316
Use logcat to verify offline handling:
- Distinctive: line 367
If ADB won't connect even after unfreeze:
- Distinctive: line 375
If still failing, re-run the startup script which handles network, iptables, and ADB:
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Waydroid ADB Control
Tools and workflows for controlling Waydroid Android emulator via ADB for Expo/React Native development.
Session Startup
Always run the startup script first:
<project-root>/scripts/waydroid-dev-start.shThis handles network config, ADB connection, and port forwarding (Metro 8081, Supabase 54321, PowerSync 8080). Takes ~15 seconds.
Development Build vs Expo Go
We use a development build (custom native app) instead of Expo Go because:
- MMKV, PowerSync, and other native modules require custom native code
- Dev builds support all native modules after one-time compile
- Hot reload still works normally via Metro
First-Time Build
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
npx expo prebuild --platform android # Generate android/ folder
npx expo run:android # Build + install (~5-10 min)Subsequent Development
After dev build is installed, start the full dev environment:
npm run dev
# Starts Supabase + PowerSync + Metro
# App already installed - hot reload worksOr start services separately:
npm run dev:backend # Supabase + PowerSync only
npm start # Metro only (if backend already running)Rebuild Required When
- Adding new native dependencies
- Changing app.json/app.config.js native settings
- After
npm installof packages with native code
Error Handling During Emulator Verification
This applies to ALL emulator use: frontend verification, E2E QA, integration testing, debugging.
When you observe any console error, network error, debugger warning, or unexpected log output during emulator sessions, you MUST either fix it or explicitly escalate. There is no third option.
You MUST NEVER:
- Classify errors as "dev-only noise" and proceed — this is a dismissal, not a resolution
- Label errors as "pre-existing" or "unrelated" without evidence and without escalating
- Note an error in a completion summary and move on without a decision
You MUST ALWAYS — for every error observed — do one of:
- Fix it — if the error is caused by your changes or is clearly fixable
- Escalate it — using this format:
ESCALATION: [error description + what URL/module/line if available] Evidence it is pre-existing: [e.g. "present on main branch before my changes" — or "unknown"] Impact: [does it affect the task's functionality?] Recommended action: [fix now / defer with reason / ignore with reason] Decision needed from user: YES
Silence is not acceptable. Classify and escalate — never just note and move on.
Common Operations
Take Screenshot
adb exec-out screencap -p > /tmp/screen.pngThen use Read tool to view the image.
Check Status
waydroid status # Container state
adb devices # ADB connection (should show "device")Launch Dev Build App
# Launch by package name (dev build)
adb shell am start -n com.anonymous.projectalfalfa/.MainActivityForce Restart App
adb shell am force-stop com.anonymous.projectalfalfa
adb shell am start -n com.anonymous.projectalfalfa/.MainActivityInstall APK
adb install /path/to/app.apkView Logs (for debugging)
adb logcat -d | grep -iE "react|expo|error" | tail -30Toggle Dark Mode
adb shell cmd uimode night yes # Enable dark mode
adb shell cmd uimode night no # Disable dark modeUI Interaction (USE HELPER SCRIPTS)
ALWAYS use helper scripts for UI interaction - they can be whitelisted for auto-approval.
Tap a Button by Label
~/.claude/skills/waydroid-adb/scripts/tap-button.sh "Add Todo"
~/.claude/skills/waydroid-adb/scripts/tap-button.sh "Clear All"
~/.claude/skills/waydroid-adb/scripts/tap-button.sh "Cycle Theme"Get UI Text Values
~/.claude/skills/waydroid-adb/scripts/get-ui-text.sh "Todos:" # Returns "Todos: 5"
~/.claude/skills/waydroid-adb/scripts/get-ui-text.sh "DB:" # Returns "DB: Ready"
~/.claude/skills/waydroid-adb/scripts/get-ui-text.sh "Connected" # Returns "Connected"Dump UI Hierarchy
~/.claude/skills/waydroid-adb/scripts/ui-dump.sh # Full dump
~/.claude/skills/waydroid-adb/scripts/ui-dump.sh "Add Todo" # Filter for patternLaunch/Restart App
~/.claude/skills/waydroid-adb/scripts/app-launch.sh # Launch app
~/.claude/skills/waydroid-adb/scripts/app-launch.sh --restart # Force restartTake Screenshot
~/.claude/skills/waydroid-adb/scripts/screenshot.sh # Save to /tmp/waydroid_screen.png
~/.claude/skills/waydroid-adb/scripts/screenshot.sh /tmp/test.pngReact Native Specifics
- Buttons: Use
content-desc(set viaaccessibilityLabelprop) - Text: Use
textattribute - Views with testID: Check
resource-id(may have package prefix)
Manual ADB Commands (fallback)
adb shell input text "hello" # Type text (no spaces)
adb shell input keyevent KEYCODE_BACK # Back button (keyevent 4)
adb shell input keyevent KEYCODE_ENTER # Enter key (keyevent 66)
adb shell input keyevent 61 # TAB - move to next field
adb shell input swipe 500 1000 500 500 # Swipe up (x1 y1 x2 y2)Form Input Best Practices
IMPORTANT: When filling forms (login, signup, etc.):
- Use TAB to navigate fields - More reliable than tapping each field
- Use TAB + SPACE to submit - From last field, TAB to button then SPACE to press
- Avoid special characters -
!,@,#don't work reliably via ADB text input - Clear fields first - Text appends rather than replaces; clear before entering
Login Flow (Recommended)
# 1. Get coordinates from ui-dump (one-time)
~/.claude/skills/waydroid-adb/scripts/ui-dump.sh | grep -E 'Email|Password|Sign In'
# 2. Tap email field (use coordinates from ui-dump, typically ~708,793)
adb shell input tap 708 793
sleep 1
# 3. Enter email
adb shell input text 'staff-a@test.com'
# 4. TAB to password field
adb shell input keyevent 61
# 5. Enter password (no special chars - test password is TestPass123)
adb shell input text 'TestPass123'
# 6. TAB to Sign In button, then SPACE to press it
adb shell input keyevent 61
adb shell input keyevent 62Alternative: Use tap-button.sh (if button has accessibility label):
adb shell input keyevent 4 # Dismiss keyboard first
~/.claude/skills/waydroid-adb/scripts/tap-button.sh "Sign In"Test Credentials
| User | Password | Business | Role | |
| staff-a | staff-a@test.com | TestPass123 | Farm A | staff |
| staff-b | staff-b@test.com | TestPass123 | Farm B | staff |
| customer-a | customer-a@test.com | TestPass123 | Farm A | customer |
Note: Passwords intentionally have no special characters for ADB compatibility.
Workflows
Verify App Rendering (Dev Build)
- Run startup script (if not already done)
- Start Metro:
npm start - Launch app:
adb shell am start -n com.anonymous.projectalfalfa/.MainActivity - Take screenshot:
adb exec-out screencap -p > /tmp/screen.png - View with Read tool
Hot Reload Verification
- Take screenshot
- Make code change and save
- Wait 3-4 seconds
- Take another screenshot
- Compare to verify change reflected
Debug Loading Issues
- Check Metro is running:
ss -tlnp | grep 8081 - Check adb reverse:
adb reverse --list - Check logs:
adb logcat -d | grep -iE "react|expo|error" | tail -30 - Force restart app and retry
Test Persistence (State Survives Restart)
- Take screenshot showing initial state
- Change state in app (e.g., theme preference)
- Take screenshot showing changed state
- Force quit:
adb shell am force-stop com.anonymous.projectalfalfa - Relaunch app
- Take screenshot - state should be preserved
Scripts
All scripts are in ~/.claude/skills/waydroid-adb/scripts/ and can be whitelisted.
| Script | Purpose | Example |
screenshot.sh | Take screenshot | screenshot.sh /tmp/test.png |
status.sh | Full status check | status.sh |
tap-button.sh | Tap button by label | tap-button.sh "Add Todo" |
get-ui-text.sh | Get text from UI | get-ui-text.sh "Todos:" |
ui-dump.sh | Dump/filter UI hierarchy | ui-dump.sh "pattern" |
app-launch.sh | Launch/restart app | app-launch.sh --restart |
Offline Testing
IMPORTANT: Waydroid airplane mode does NOT work for simulating offline conditions.
Waydroid runs as a container using the host's network stack. Setting airplane_mode_on=1 via ADB changes the Android setting but doesn't actually disconnect network access:
# This does NOT work to simulate offline:
adb shell settings put global airplane_mode_on 1
# Container still has network access via hostSimulating Offline Mode
Method 1: Stop Backend Services (Recommended)
For testing offline behavior (e.g., session persistence when auth server unreachable):
# Stop Supabase services
docker stop supabase-db supabase-kong supabase-auth
# Test app behavior while backend is down
adb shell am force-stop com.anonymous.projectalfalfa
adb shell am start -n com.anonymous.projectalfalfa/.MainActivity
# Verify offline handling (screenshots, logcat)
~/.claude/skills/waydroid-adb/scripts/screenshot.sh /tmp/offline-test.png
# Restart services when done
docker start supabase-db supabase-kong supabase-authMethod 2: Block Host Network (More Invasive)
For complete network isolation, block traffic at the host level:
# Block all traffic to backend ports
sudo iptables -A OUTPUT -p tcp --dport 54321 -j DROP # Supabase
sudo iptables -A OUTPUT -p tcp --dport 8080 -j DROP # PowerSync
# Test offline behavior...
# Remove blocks when done
sudo iptables -D OUTPUT -p tcp --dport 54321 -j DROP
sudo iptables -D OUTPUT -p tcp --dport 8080 -j DROPVerifying Offline State
The app's PowerSync indicator shows connectivity:
- "Connected" - Network available, auth valid
- "Offline" - Network unavailable or auth failed
- Error banner - Connection issues (check message for details)
Use logcat to verify offline handling:
adb logcat -d | grep -E '(MMKV|Session|backup|offline)' | tail -20Troubleshooting
Supabase Auth Network Errors After Auth Testing
Symptoms: TypeError: Network request failed / AuthRetryableFetchError errors in logcat and as dev overlays on cold start.
Root cause: The Supabase JS client is initialized as a module-level singleton in SupabaseConnector.ts with autoRefreshToken: true. When Expo Router bundles the route graph, it includes (auth)/login.tsx → @/lib/auth/signIn.ts → supabaseConnector. If MMKV has a cached auth session from previous test logins, the Supabase client auto-refreshes it on startup — but localhost:54321 is unreachable from Waydroid.
Fix: Clear all app data to remove the cached session:
adb shell pm clear com.anonymous.projectalfalfa
adb shell am start -n com.anonymous.projectalfalfa/.MainActivityRecurrence: This reoccurs any time auth testing writes a session to MMKV and then demo mode is tested afterward. Standard practice: clear app data when switching from auth testing to demo mode testing.
Container FROZEN State (ADB Commands Hang)
Symptoms:
- ADB commands hang indefinitely (screenshot, shell, etc.)
adb devicesshows192.168.240.112:5555 offline- Commands time out or never return
Diagnosis:
waydroid status
# Look for: Container: FROZENCause: Waydroid has a power-saving feature that freezes the container after idle timeout. When frozen, all network and ADB communication fails.
Fix:
sudo waydroid container unfreezeThen retry your ADB command. The startup script (waydroid-dev-start.sh) handles this automatically, but if you've been idle for a while mid-session, you may need to unfreeze manually.
Prevention: The startup script now detects and unfreezes automatically. If you encounter this mid-session, just run the unfreeze command above.
ADB Connection Issues
If ADB won't connect even after unfreeze:
# Full reconnect sequence
adb kill-server
adb start-server
adb connect 192.168.240.112:5555If still failing, re-run the startup script which handles network, iptables, and ADB:
<project-root>/scripts/waydroid-dev-start.shReference
For detailed setup, troubleshooting, and known issues, see:
- Project docs:
<project-root>/docs/waydroid-setup.md - Skill reference:
references/setup-guide.md
In this repository43 other files it gives its agents.
- skills/technical/supabase-postgres-best-practices/AGENTS.mdAGENTS.md
- skills/technical/supabase-postgres-best-practices/CLAUDE.mdCLAUDE.md
- direct-handoffSkill
- handoffSkill
- nt-commsSkill
- threadsSkill
- meal-planningSkill
- resume-workflowSkill
- bash-scriptsSkill
- code-reviewSkill
- davinci-resolveSkill
- disk-cleanupSkill
- fastmcp-pythonSkill
- go-cliSkill
- markdown-to-pdfSkill
- pr-createSkill
- qa-planSkill
- rcaSkill
- react-native-best-practicesSkill
- ruby-railsSkill
- skill-creatorSkill
- spec-checkSkill
- supabase-migrationsSkill
- supabase-postgres-best-practicesSkill
- supabase-sqlSkill
- testingSkill
- ui-state-managementSkill
- calcinatioSkill
- citationsSkill
- exploreSkill
- manual-qaSkill
- nvim-copilotSkill
- orchestrateSkill
- orthogonal-emanationSkill
- product-intent-signal-extractionSkill
- project-management-signal-extractionSkill
- request-researchSkill
- researchSkill
- skill-discoverySkill
- slack-thread-triageSkill
- specSkill
- tmux-sidecarSkill
- youtube-transcribeSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

