A lens into Android apps — analysis · debugging · screen flows · scenario verification
Inspect any app with just adb, no SDK required, and let Claude Code agents drive the device to verify it.
English · 한국어

▶ Watch the full-quality video (MP4, 28s)
DroidLens aims to replace Meta's Flipper (archived in 2025). Without adding anything to your app, you get screen mirroring, the view hierarchy, screen navigation paths, and the libraries an app uses — and the built-in MCP server lets Claude Code agents use the same features.
3D view hierarchy with class labels![]() |
Screen flow from auto-exploration![]() |
Library detection from the APK![]() |
App info![]() |
macOS · Linux — one line in Terminal (installs or updates to the latest version; on macOS it opens without any Gatekeeper warning):
curl -fsSL https://raw.githubusercontent.com/ESeungJun/droidlens/master/scripts/install.sh | bashOr download just one installer for your OS from Releases. The other files (.zip, .mjs, .apk) are used by in-app updates or optional tools — you don't need them.
| OS | File |
|---|---|
| macOS (Apple silicon, M1+) | DroidLens-x.y.z-mac-arm64.dmg |
| macOS (Intel) | DroidLens-x.y.z-mac-x64.dmg |
| Windows (x64) | DroidLens-x.y.z-win-x64.exe |
| Windows (ARM) | DroidLens-x.y.z-win-arm64.exe |
| Linux | DroidLens-x.y.z-linux-x86_64.AppImage |
First launch on macOS (downloaded .dmg): the app is signed and notarized by Apple, so just move it to Applications and open it. (0.8.3 and older weren't notarized and need Open Anyway in System Settings → Privacy & Security or xattr -dr com.apple.quarantine /Applications/DroidLens.app.)
First launch on Windows: if SmartScreen warns you, click "More info → Run anyway".
Updates: when a new version is out, an ⬆ Update button appears in the top bar. Click it to download, replace, and restart (you can also check from the Settings & Integrations tab; the macOS log is at ~/.droidlens/update.log).
If Android Studio is installed, adb is found automatically. Otherwise, install just the platform-tools:
macOS brew install --cask android-platform-tools
Windows winget install Google.PlatformTools
Linux sudo apt install adb
Then enable Developer options → USB debugging on your device and connect it, or start an emulator. Virtual devices (AVDs) on this computer appear in the device list at the top; pick one and DroidLens starts it and selects it once it has booted (there is a stop button too). If you have none, the device setup helper walks you through installing one. You can also set the adb path manually in the app's Settings & Integrations tab.
| Tab | What it does |
|---|---|
| Screen | High-frame-rate mirroring (scrcpy stream + WebCodecs, up to 60fps) or snapshot mode; click = tap · drag = swipe · hold = long press; hardware keys, text input, screenshots, recording (up to 180s) |
| View hierarchy | Two-way selection between screenshot and tree, attributes and selectors, node count by depth, Compose detection, 3D exploded view |
| Screen flow | A per-app screen map — screens and transitions from auto-exploration, manual use, and scenario runs keep adding up in one map (thumbnails + action labels). Auto-exploration (never taps payment, delete, logout, or send buttons), pick buttons, long presses, or swipes on a screenshot and explore only the flows they open, tapped-button coverage per screen, path from the first screen, auto-navigate to a screen along the recorded path, map export/import (merge) |
| Logs | Live logcat (app PID filter, level, search), recent crashes |
| Network ¹ | OkHttp requests and responses (headers, pretty-printed JSON bodies, image preview, timing), failure filter, copy as cURL |
| App data ¹ | View and edit SharedPreferences, SQLite/Room DB browser with SQL execution, custom events |
| Libraries | Detects 100+ libraries and frameworks from the APK's DEX and native libs, estimates obfuscation |
| App info | Version, SDK, permissions, activities; launch / restart / kill; deep links |
| Scenarios | Verification reports (per-step screenshots and verdicts), write and run YAML scripts, run the full regression suite |
| Settings & Integrations | adb path, one-click Claude Code MCP registration, install bundled agents |
| 💬 Agent chat | Chat panel opened from the top bar. Ask the verifier or analyst agent while watching the mirror (uses this computer's Claude Code login — no API key needed) |
¹ Requires the DroidLens SDK in your app's debug build.
Information that only lives inside the app — network traffic, storage — is visible only in your own app's debug build with the SDK added. Release builds get a no-op implementation of the same API (sdk-noop).
// settings.gradle.kts
dependencyResolutionManagement {
repositories { maven { url = uri("https://jitpack.io") } }
}
// app/build.gradle.kts
dependencies {
debugImplementation("com.github.ESeungJun.droidlens:sdk:v0.5.0")
releaseImplementation("com.github.ESeungJun.droidlens:sdk-noop:v0.5.0")
}
// Network inspector (if you use OkHttp)
val client = OkHttpClient.Builder()
.addInterceptor(DroidLensInterceptor())
.build()
// Custom events (optional)
DroidLens.event("purchase_clicked", JSONObject().put("itemId", 42))- No initialization code needed (starts automatically via a ContentProvider)
- Communicates only over an in-app Unix socket (
localabstract:droidlens_sdk_<package>), so no INTERNET permission is required; it checks the peer UID and rejects any connection other than adb and the app itself (e.g. other apps on the same device) - Request and response bodies are capped at 256KB each; the latest 500 entries are kept in memory only
- Sensitive values are masked on the device, before they are stored — in headers, URL queries,
Locationredirects, JSON bodies (including nested objects/arrays and JSON inside strings), GraphQL arguments, form, multipart and XML bodies, error messages and custom events. Keys match regardless of case and separators, and common words match anywhere in the name (newPassword,fcmToken,X-Refresh-Token); short ones likejwt,otp,pinmatch exactly. Add your own withDroidLens.redactHeaders("X-Session")/DroidLens.redactKeys("phone"), or callDroidLens.clearDefaultRedaction()to use only your list. Compressed bodies the app requested itself (Content-Encoding) are not recorded - DB write SQL runs only when explicitly allowed
Just press two buttons in the app's Settings & Integrations tab.
- Register MCP — registers the app's built-in MCP server (44 tools) with
claude mcp add --scope user. No separate Node install needed. - Install agents — copies the bundled agents to
~/.claude/agents/.
| Agent | What it does |
|---|---|
droidlens-verifier |
Verifies natural-language scenarios step by step on a real device and leaves an HTML report with screenshots |
droidlens-analyst |
Analyzes the tech stack, UI structure (View/Compose), and screen map. With REA installed, it traces down to the code level |
Restart Claude Code, then ask:
Use droidlens-verifier to verify the search → product detail → add to wishlist scenario in the shopping app
Use droidlens-analyst to analyze this app's tech stack and screen structure
To use only the MCP server without the app, download droidlens-mcp.mjs (Node 20+) from Releases and register it with claude mcp add --scope user droidlens -- node /path/to/droidlens-mcp.mjs.
Save verified flows as YAML and replay them anytime. When an agent passes a scenario with droidlens-verifier, its actions and checks are recorded automatically and saved to ~/.droidlens/scripts/. You can also write or edit scripts by hand.
name: Product search
app: com.example.shop
setup: { restart: true } # clearData and prefs (SDK) can also set up the initial state
steps:
- tap: { text: Search }
- type: { text: sneakers, into: { className: EditText } }
- enter # one-word shorthands: back / home / enter / launch / screenshot
- wait: { id: result_list, timeout: 8000 }
- swipe: { direction: up, until: { textContains: Air Max } }
- tap: { textContains: Air Max }
- assert: { activity: ProductDetailActivity }
- assert: { network: { url: /api/products, method: GET, status: 200 } } # SDK
- assert: { event: product_viewed } # SDK
- assert: { noCrash: true }| Step | Description |
|---|---|
tap / longPress |
Waits for the element matching a selector (id · text · textContains · desc · descContains · className · index), then presses it. With optional: true, skipped if absent |
type |
Text input (into targets an input field, clear replaces). Supports non-ASCII text (e.g. Korean) when the agent is running |
swipe |
up/down/left/right; with until, scrolls until the element appears |
wait |
Until an element appears (with gone: true, until it disappears) |
assert |
visible · notVisible · text · activity · package · network · pref · db · event · noCrash |
| Other | key, tapAt, sleep, launch, deeplink, screenshot |
How to run:
- Desktop: Scenarios → Scripts tab to edit, run, and run the full regression suite
- Claude Code:
script_run,suite_run,script_write - CI/terminal:
node droidlens-mcp.mjs run <file|folder> [--serial <device>](exit code 1 on failure)
See examples/scripts for examples.
Everything is saved under ~/.droidlens/ (override with DROIDLENS_HOME).
| Folder | Contents |
|---|---|
screenshots/, recordings/ |
Screenshot PNGs, recorded mp4s |
graphs/<package>/ |
App screen map graph.json, graph.mmd (mermaid), per-screen screenshots. 'Export' in the Screen flow tab saves a single *.droidlens-map.json with screenshots |
scenarios/<id>/ |
Scenario verification report.html, scenario.json, recorded script.yaml, per-step screenshots |
scripts/ |
Regression test scripts (YAML) |
apks/<package>/ |
APKs pulled for analysis |
When a device is connected, DroidLens automatically installs and starts a small agent app (io.github.eseungjun.droidlens.agent, 13KB). Check its status and toggle it with the ⚡ Agent indicator in the top bar.
| Without agent (uiautomator) | With agent | |
|---|---|---|
| View hierarchy dump | 1.5–3s | 0.03–0.3s |
| Current activity | dumpsys, 1–7s | Instant (accessibility event tracking) |
| Text input | ASCII only | Unicode (Korean, etc.) |
| Screen idle detection | Screenshot diff | Accessibility idle + structure diff |
The agent runs with the same Instrumentation privileges as UI testing tools (Appium, uiautomator). It opens no network ports, uses only an on-device Unix socket, and accepts only connections that come through adb (peer UID check). To disable it, set DROIDLENS_AGENT=0.
- Network, DB, and SharedPreferences work only in debug builds with the SDK (not available for third-party apps)
- The network inspector records only OkHttp-based requests (including Retrofit)
- WebView, Flutter, and game-engine screens expose few accessibility nodes, so analysis is limited
- Can't run alongside other UiAutomation tools such as Appium (turn off the agent from the top bar)
pnpm install
pnpm build # core + mcp
pnpm dev # desktop app (dev mode)
pnpm build:agent # agent APK (requires JDK 17 + Android SDK)
pnpm dist # installer for the current OS → apps/desktop/release/packages/core adb engine (device, input, capture, view hierarchy, screen graph & auto-exploration, library detection, scenarios)
packages/mcp MCP server (single-file bundle droidlens-mcp.mjs)
android/agent on-device agent APK (Instrumentation + HTTP)
android/sdk app SDK (network, prefs, DB, events), sdk-noop, sample
apps/desktop Electron + React
agents/ Claude Code agent definitions
| Branch | Purpose |
|---|---|
master |
Released code only. Every commit here is a tagged release (v*) |
develop |
Integration branch for the next release. Open feature PRs against this |
feature/<name> |
New features and fixes, branched from develop and merged back via PR |
release/<version> |
Release prep (version bump, notes) from develop; merged into master and develop, then tagged |
hotfix/<version> |
Urgent fixes from master; merged into master and develop, then tagged |
docs/<name> |
README and docs only (no app code), from master; merged into master and develop with no release or tag |
Every commit (and PR title, which becomes the merge commit title) uses Conventional Commits: <type>(<scope>)?: <description>.
| Type | Use for |
|---|---|
feat |
New feature |
fix |
Bug fix |
docs |
Documentation only |
refactor / perf / style |
Code change without behavior change / performance / formatting |
test |
Tests |
build / ci |
Packaging, dependencies / GitHub Actions |
chore |
Versions, merges, other maintenance |
pnpm install installs a commit-msg hook (.githooks/) that rejects commits without a prefix, and CI checks pushed commits and PR titles.
Pushing a v* tag makes GitHub Actions build installers for macOS (arm64/x64), Windows (x64/arm64), and Linux and upload them to Releases. See the roadmap in docs/ROADMAP.md.
MIT. For high-frame-rate mirroring, the scrcpy server (Apache-2.0) is downloaded and bundled (scripts/fetch-scrcpy.mjs, SHA256-verified).




