---
name: Android Device
description: Use to drive an Android phone — a real handset plugged in over USB, or an emulator. Boot an AVD, install and launch an app, tap/swipe/scroll/type, read the view hierarchy, take screenshots. Use for Android development and UI testing, and equally for operating an app on the operator's own phone.
version: 2
situations: run my Android app, test on Android, control my phone, use my Android phone, my real device, the phone plugged in, order something in an app, open an app on my phone, android emulator, boot an AVD, install the apk, tap the button, does this screen look right, reproduce on device, UI test, take a screenshot of the app, launch the app, deep link, adb, Android build
requires-secrets:
requires-packages: android-platform-tools
---

# Android Device

You can drive Android: **a real phone plugged into this machine over USB, or an emulator.** Boot an AVD,
install/launch an app, interact with it, and see what is on screen. The operator sees the same phone live
in the session's right-hand panel, so you are both looking at one device — if they are watching, narrate
what you're doing.

Everything runs through `adb`, part of the Android SDK's platform-tools, which this skill installs. Booting
an AVD additionally needs the SDK's `emulator` component and at least one virtual device; if a tool reports
the capability is unavailable, it says exactly which piece is missing — relay that, don't guess.

## Real phones are not a special case — check before you answer

**Never tell the operator you can only drive an emulator, or that you can't reach their phone. Run
`android_list` and look.** A handset with USB debugging on appears there like any other device, marked
`[physical]`, and every tool except `android_boot` / `android_shutdown` works on it — it's already on and
it isn't ours to power off.

The one real difference is permission, and it is per-device:

- **Reading is always allowed** — `android_screenshot`, `android_describe_ui`, `android_screen_size`,
  `android_foreground_app` work on a real phone with no setup.
- **Acting is off until the operator turns it on** — tapping, typing, scrolling, installing, launching. The
  tool answers with exactly what to do: turn on "let agents drive" for that device in the Android panel.
  Relay that instead of concluding it's impossible.

A real phone is signed into the operator's real accounts. That is the point of using it — and the reason
to be careful: it is their money, their messages, their 2FA. Ask before anything that spends, sends, posts
or deletes, and stop at the last screen before it happens rather than after.

## How to call these tools

**If you are a CLI brain (Claude Code / Codex), these are NOT invocable skills or built-in tools — run
each one through the `00` command from your shell:**

```bash
00 media android_list --json '{}'
00 media android_describe_ui --json '{}'
00 media android_tap --json '{"x":540,"y":1200}'
00 media android_scroll --json '{"direction":"down"}'
00 media android_launch --json '{"packageName":"com.example.app"}'
```

`00` already knows which agent and conversation you are, so you never pass ids and never write curl. If a
tool name isn't recognised, run `00 media tools` — do NOT go hunting through your capability settings.
(A pi-brained agent calls them directly as tools instead; the names and parameters are identical.)

## Coordinates — read this before your first tap

**Every coordinate is in DEVICE PIXELS, origin top-left, x→right, y→down** — the same raw pixels the
hierarchy reports. There is no points-vs-pixels scale factor here.

**Take coordinates from `android_describe_ui`, never from a screenshot.** Each node carries
`{x, y, width, height}` already in device pixels; tap its centre (`x + width/2`, `y + height/2`). A
screenshot is the wrong source twice over: you'd be estimating a position by eye, and your client may have
**downscaled the image before you saw it** — if the picture arrives with a note like "original 1080x2400,
displayed at 900x2000, multiply by 1.2", every number you read off it is wrong until scaled, and doing that
arithmetic in your head is how taps land 200px off. Don't. Read the hierarchy.

- `android_screen_size` gives the bounds every coordinate must fall inside (e.g. `1080×2400 px`). Anything
  outside is refused rather than silently swallowed.
- `android_screenshot --scale 2` halves the IMAGE only; it changes nothing about the coordinates.
- After `android_orientation`, width and height swap. Re-read the hierarchy; don't reuse old coordinates.

### The edges belong to the system, not to the app

Android's gesture navigation owns strips at the screen edges, drawn on top of whatever the app shows:

- **Bottom strip** (~the last 48px, and the whole nav bar above it) — a touch starting there is
  home/recents. A "button" you can see at `y = 2380` on a 2400-tall screen is under it: tapping there
  sends you to the launcher, and nothing about the app explains why.
- **Left/right edges** — a drag starting there is the back gesture.

So: aim at the CENTRE of the element's frame, and if the element you want genuinely sits in the bottom
strip, `android_scroll` the content up first and tap it where it lands. The tools flag a touch in these
strips in their result — when you see that warning, change the coordinate rather than repeating the tap.

### Scrolling

Use **`android_scroll { direction, amount? }`**, not a hand-rolled swipe: it drags down the middle of the
screen, clear of those strips. `direction` is the way the CONTENT moves (`down` reveals what's further
down the page); `amount` is a fraction of the screen, 0.1–0.9, default 0.5. Every frame moves, so re-read
`android_describe_ui` before your next tap. Keep `android_swipe` for what it's for: a deliberate gesture
(dismiss, pull-to-refresh, an edge swipe) between two points you chose.

## Hardware buttons you can press

`android_press { button }` takes exactly these:

| Button | What it does |
|---|---|
| `home` | Go to the home screen |
| `back` | Go back — Android's universal "leave this screen" |
| `app-switcher` | Open recents / multitasking |
| `lock` / `power` | Power button (locks a live screen, wakes a locked one) |
| `menu` | The legacy menu key — some apps still bind it |
| `volume-up`, `volume-down` | Volume |

**`back` is the button iOS doesn't have.** On Android it is almost always the right way to leave a screen —
reach for it before hunting for an on-screen back arrow.

## Driving a screen — the loop that actually works

1. `android_describe_ui` → find the node by its `label` (text or content-description) or `id`, compute its
   centre.
2. Act: `android_tap` / `android_scroll` / `android_swipe` / `android_type`.
3. `android_describe_ui` **again** → confirm the screen changed the way you expected. A tap that hit
   nothing is completely silent; re-reading is the only way you find out.
4. `android_screenshot` when the operator should SEE it, or when the question is visual (layout, spacing,
   colour) rather than structural.

While you're driving, the operator's panel shows "🤖 <you> is controlling" and their taps are paused, so
they aren't fighting you for the phone. It releases a few seconds after your last action — so do a related
burst of actions, then stop; don't hold the device idle.

## Typing is more limited than on iOS

`android_type` goes through `adb shell input text`, which carries **printable ASCII only** — emoji and
accented characters are rejected rather than half-typed. `android_paste` is the same mechanism (Android
exposes no clipboard we can reach over adb), so it is not an escape hatch for non-ASCII: to get such a
string into a field, have the app receive it another way (a deep link, a test fixture, an intent extra).

Autocorrect on the on-screen keyboard does **not** apply — `input text` commits characters directly — so
unlike iOS you don't need to re-read a field just because you typed a non-word. Re-read it because the tap
that focused it might have missed.

## Unlike iOS, the home screen IS readable

`android_describe_ui` works everywhere, including the launcher — there's no "no frontmost application"
state to work around. What can fail is `uiautomator` timing out on a screen that never settles (an
animation, a video, a spinner): if a dump comes back empty or errors, wait a moment and read again rather
than assuming the screen is blank.

## Tools

- `android_list` — every reachable device, emulator or `[physical]` phone, and which are running.
  `android_boot { device }` / `android_shutdown { device }` — **emulators only**.
- `android_describe_ui { device }` — the view hierarchy, frames in pixels. Your primary sense.
- `android_screen_size { device }` — the screen's bounds in pixels.
- `android_screenshot { device, scale? }` — an image saved into `files/`; use it to SHOW the operator.
- `android_tap { device, x, y }`, `android_double_tap`.
- `android_scroll { device, direction, amount? }` — safe scrolling. `android_swipe { x, y, toX, toY, duration? }`
  — a deliberate drag between two points.
- `android_type { device, text }` — printable ASCII into the focused field. `android_paste` is the same.
- `android_key { device, code }` — one key: `Enter`, `Escape`, `Backspace`, `Tab`, `ArrowDown`, … or a bare
  Android keycode name (`SEARCH`, `MEDIA_PLAY_PAUSE`).
- `android_press { device, button }` — `home`, `back`, `app-switcher`, `lock`, `volume-up`, …
- `android_install { device, apkPath }`, `android_launch { device, packageName }`, `android_terminate`,
  `android_foreground_app`, `android_open_url { device, url }` (deep links).
- `android_orientation { device, orientation }` — rotate; screen dimensions swap.

## Working rules

- **Look before you refuse.** "Can you use my phone?" is answered by `android_list`, not from memory.
- **Boot once.** A cold AVD boot takes tens of seconds to minutes. Check `android_list` first and reuse a
  running device. A physical phone never needs booting.
- **Build, then install.** You build the app with your normal tools (`./gradlew assembleDebug`), then
  `android_install` the APK and `android_launch` it. This skill does not build.
- **Package name, not activity.** `android_launch` takes the package (`com.example.app`) and starts its
  launcher activity for you. Use `android_foreground_app` to check it landed.
- **Verify, don't assume.** After any action that should change the screen, re-read the hierarchy. A tap
  that landed on nothing is silent — the only way you know is by looking again.
- **`back` before hunting.** Most "how do I get out of this screen" answers are `android_press { back }`.
- **Don't type secrets.** Never enter credentials, API keys, or card numbers — on an emulator or, far more
  seriously, on the operator's own phone — unless they explicitly asked you to in this conversation.
- **Stop before the irreversible one.** On a real phone, placing an order, sending a message, publishing or
  deleting spends something real. Get to the confirmation screen, report exactly what it says (item, total,
  address, payment method), and wait for a yes.
- If the app crashes or won't launch, say so plainly with the error — don't retry blindly in a loop.
