agentleFS
matthullmatthull/my-skills/skills/technical/waydroid-adb/SKILL.md

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
  1. Distinctive:

    After dev build is installed, start the full dev environment:

    line 34
  2. Very specific:

    After npm install of packages with native code

    line 50
  3. Distinctive:

    When you observe any console error, network error, debugger warning, or unexpected log output during emulator sessions, you MUST either fix …

    line 56
  4. Very specific:

    Buttons: Use content-desc (set via accessibilityLabel prop)

    line 155
  5. Very specific:

    Text: Use text attribute

    line 156
  6. Very specific:

    Views with testID: Check resource-id (may have package prefix)

    line 157
  7. Distinctive:

    IMPORTANT: When filling forms (login, signup, etc.):

    line 170
  8. Distinctive:

    Run startup script (if not already done)

    line 220
  9. Very specific:

    Check Metro is running: ss -tlnp | grep 8081

    line 234
  10. Very specific:

    Check adb reverse: adb reverse --list

    line 235
  11. Very specific:

    All scripts are in ~/.claude/skills/waydroid-adb/scripts/ and can be whitelisted.

    line 249
  12. Distinctive:

    Use logcat to verify offline handling:

    line 316
  13. Distinctive:

    If ADB won't connect even after unfreeze:

    line 367
  14. Distinctive:

    If still failing, re-run the startup script which handles network, iptables, and ADB:

    line 375

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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.sh

This 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 works

Or 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 install of 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:

  1. Fix it — if the error is caused by your changes or is clearly fixable
  2. 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.png

Then 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/.MainActivity

Force Restart App

adb shell am force-stop com.anonymous.projectalfalfa
adb shell am start -n com.anonymous.projectalfalfa/.MainActivity

Install APK

adb install /path/to/app.apk

View Logs (for debugging)

adb logcat -d | grep -iE "react|expo|error" | tail -30

Toggle Dark Mode

adb shell cmd uimode night yes   # Enable dark mode
adb shell cmd uimode night no    # Disable dark mode

UI 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 pattern

Launch/Restart App

~/.claude/skills/waydroid-adb/scripts/app-launch.sh              # Launch app
~/.claude/skills/waydroid-adb/scripts/app-launch.sh --restart    # Force restart

Take Screenshot

~/.claude/skills/waydroid-adb/scripts/screenshot.sh              # Save to /tmp/waydroid_screen.png
~/.claude/skills/waydroid-adb/scripts/screenshot.sh /tmp/test.png

React Native Specifics

  • Buttons: Use content-desc (set via accessibilityLabel prop)
  • Text: Use text attribute
  • 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.):

  1. Use TAB to navigate fields - More reliable than tapping each field
  2. Use TAB + SPACE to submit - From last field, TAB to button then SPACE to press
  3. Avoid special characters - !, @, # don't work reliably via ADB text input
  4. 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 62

Alternative: 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

UserEmailPasswordBusinessRole
staff-astaff-a@test.comTestPass123Farm Astaff
staff-bstaff-b@test.comTestPass123Farm Bstaff
customer-acustomer-a@test.comTestPass123Farm Acustomer

Note: Passwords intentionally have no special characters for ADB compatibility.

Workflows

Verify App Rendering (Dev Build)

  1. Run startup script (if not already done)
  2. Start Metro: npm start
  3. Launch app: adb shell am start -n com.anonymous.projectalfalfa/.MainActivity
  4. Take screenshot: adb exec-out screencap -p > /tmp/screen.png
  5. View with Read tool

Hot Reload Verification

  1. Take screenshot
  2. Make code change and save
  3. Wait 3-4 seconds
  4. Take another screenshot
  5. Compare to verify change reflected

Debug Loading Issues

  1. Check Metro is running: ss -tlnp | grep 8081
  2. Check adb reverse: adb reverse --list
  3. Check logs: adb logcat -d | grep -iE "react|expo|error" | tail -30
  4. Force restart app and retry

Test Persistence (State Survives Restart)

  1. Take screenshot showing initial state
  2. Change state in app (e.g., theme preference)
  3. Take screenshot showing changed state
  4. Force quit: adb shell am force-stop com.anonymous.projectalfalfa
  5. Relaunch app
  6. Take screenshot - state should be preserved

Scripts

All scripts are in ~/.claude/skills/waydroid-adb/scripts/ and can be whitelisted.

ScriptPurposeExample
screenshot.shTake screenshotscreenshot.sh /tmp/test.png
status.shFull status checkstatus.sh
tap-button.shTap button by labeltap-button.sh "Add Todo"
get-ui-text.shGet text from UIget-ui-text.sh "Todos:"
ui-dump.shDump/filter UI hierarchyui-dump.sh "pattern"
app-launch.shLaunch/restart appapp-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 host

Simulating 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-auth

Method 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 DROP

Verifying 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 -20

Troubleshooting

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/.MainActivity

Recurrence: 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 devices shows 192.168.240.112:5555 offline
  • Commands time out or never return

Diagnosis:

waydroid status
# Look for: Container: FROZEN

Cause: 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 unfreeze

Then 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:5555

If still failing, re-run the startup script which handles network, iptables, and ADB:

<project-root>/scripts/waydroid-dev-start.sh

Reference

For detailed setup, troubleshooting, and known issues, see:

  • Project docs: <project-root>/docs/waydroid-setup.md
  • Skill reference: references/setup-guide.md

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.