Skip to content

About

adb-only Android app UX crawler — maps a competitor app's screens/flows to a graph + clean English report. Deterministic, blocklist-gated, EN/FA.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

reverse-competitors

Reverse-map a competitor Android app's screens and navigation into a flow graph plus a clean, English UX report — by driving a physical device over adb only, with zero device-side footprint (no instrumentation APK, no agent on the device).

Built for competitive UX research. Works on normal native apps, WebView PWAs, and locked-down POS/payment terminals. Language-agnostic: it maps English and non-Latin (e.g. Persian/Arabic) UIs, and its output is always in English.

It ships as a Claude Code skill (SKILL.md) plus standalone Python scripts you can run directly.


Why adb-only

An instrumentation APK on a payment terminal can trip attestation or show up in a vendor audit. Everything here goes through adb shell — uiautomator dump for the UI tree, screencap for pixels, input tap for actions. Slower than a helper APK, but leaves nothing on the device.

What you get

A portable report/ folder (English throughout):

report/
├── screens.md      # inventory: #, name, purpose, entry path (taps from home), element count, screenshot
├── flows.md        # per-section ordered tap paths, home → screen
├── walls.md        # blocked / gated / needs-human / non-ASCII-input items — the manual-pass worklist
├── comparison.md   # competitor taps-to-reach filled; your app's column left for a human
├── graph.mmd       # Mermaid flowchart — paste into any Mermaid viewer
└── screenshots/    # every screen copied + renamed NN-<name>.png (English)

Plus the machine-readable run/<timestamp>/graph.json (screens, edges, flags).

Safety model (non-negotiable)

  1. Never autonomously taps money-moving or state-changing controls. A regex blocklist (BLOCKED_PATTERN, English + Persian out of the box) gates every tap; blocked controls are recorded as walls, never pressed. Adding terms is encouraged; removing one is a deliberate, recorded decision.
  2. No device-side installs without a human's say-so.
  3. Captured screenshots / APKs never leave the machine — no online analyzers, no cloud device farms, and no LLM API. Screens carry account/merchant/transaction data. The repo's .gitignore excludes run/, report/, and all *.png/*.apk so you can't publish them by accident.
  4. Deterministic by default. Phases 0–2 use no model at all. An optional model-driven policy (Phase 3) exists in the design but is gated — it would send screenshots to an API, so it must not run without explicit sign-off.
  5. Deterministic Python always owns the graph, frontier, dedup, replay, budget, and blocklist. A model, if ever added, only chooses which element to touch.

Install

As a Claude Code skill:

git clone https://github.com/IFDS-OSS/reverse-competitors ~/.claude/skills/reverse-competitors

Then invoke /reverse-competitors in Claude Code, or just run the scripts directly.

Quickstart

# 0. one device attached
adb devices

# target
export POS_SERIAL=<serial>      # auto-detects if exactly one device
export POS_PKG=<package>        # required

# 1. probe the device + find the package
python scripts/preflight.py --find <app-substring>

# 2. (interactive) sanity-check the bridge by hand; characterize BACK
python scripts/drive.py --app "$POS_PKG"

# 3. deterministic crawl (no model)
python scripts/crawl.py --budget 300 --max-depth 6 --time-limit 45

# 4. build the report from the newest run
python scripts/report.py

Log in / clear any OTP gate by hand first — the crawl runs from an authenticated screen.

How it works (the hard parts, baked in)

  • WebView PWA apps — if the SPA won't render, an old device WebView may be failing to parse a modern JS bundle (logcat shows SyntaxError); updating the WebView is a device-side install → ask first. Once it renders, the WebView a11y bridge exposes the DOM to uiautomator.
  • Coordinate frontier — div-soup SPAs mark nothing clickable, so the frontier is built from text / content-desc nodes and tapped by coordinate. Labels are English-first (ASCII content-desc/resource-id preferred over on-screen text).
  • Stable fingerprints — the screen fingerprint collapses repeated sibling subtrees, so a list of 3 vs 30 rows is one screen, not many. Dedup falls back to resource-id then label-set Jaccard (WebViews often lack resource-ids).
  • Backtracking — hardware BACK is characterized first and usually avoided (on some POS ROMs it exits the app and hangs adb); the crawler backtracks by restart + replaying the shortest path from home.
  • Resilience — adb stalls are recoverable, a flaky action is skipped not fatal, and graph.json is always written on exit.

Scripts

file role
bridge.py device layer: adb transport, UI parse, fingerprint, blocklist
preflight.py Phase 0 — probe device, latency, package
drive.py Phase 1 — interactive manual driver + session recorder
stepwalk.py supervised one-action-at-a-time stepper + coordinate frontier
crawl.py Phase 2 — deterministic BFS crawler
report.py Phase 4 — render report/ from graph.json

See SKILL.md for the full method, phase gates, and acceptance criteria.

License

MIT — see LICENSE.

About

adb-only Android app UX crawler — maps a competitor app's screens/flows to a graph + clean English report. Deterministic, blocklist-gated, EN/FA.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages