Android UI Behavior Analyzer
A suite of two Claude Code custom agents that analyze Android app UI code to extract all user interactions, navigation flows, data dependencies, and side effects — then derive functional requirements checklists from the analysis.
| Agent | Target | Input |
|---|---|---|
android-activity-ui-behavior-analyzer |
Traditional View system (Activity + XML layouts) | Android project path |
compose-ui-behavior-analyzer |
Jetpack Compose (@Composable screens) |
Android project path + compose-ui-tree-analyzer output |
Quick Start
# Traditional View system (Activity + XML)
@android-activity-ui-behavior-analyzer /path/to/android/project [/path/to/output]
# Jetpack Compose
@android-compose-ui-behavior-analyzer /path/to/android/project /path/to/compose-ui-analysis /path/to/output
What They Do
From an Android project's source code, both agents answer:
- What can the user do on each screen?
- What happens when they tap each button / menu item / list item?
- Does it navigate to another page? Show a dialog? Call the camera? Delete a file?
- What APIs / data sources power each screen's initial content?
- What are the app's functional requirements, derived from UI behavior?
Architecture
Both agents follow the same 3-phase pipeline:
Phase 1: Python static analyzer → structured JSON (automated, ~56-58% fill rate)
Phase 2: Claude deep analysis → enhanced JSON (MANDATORY, target ≥90%)
Phase 3: Claude report writing → functional_report.md + requirements_checklist.md
Activity Analyzer Pipeline
Phase 1: Python Script (automated) Phase 2: Claude (mandatory)
┌────────────────────────────────┐ ┌──────────────────────────────┐
│ android_ui_api_analyzer.py │ │ Deep Semantic Analysis │
│ │ │ │
│ AndroidManifest.xml ──┐ │ │ Read JSON ──┐ │
│ res/layout/*.xml ─────┤ │ │ Categorize │ A: toast │
│ res/menu/*.xml ───────┤ │ JSON │ empty items │ B: toggle │
│ *.kt / *.java ────────┤ │ ───► │ │ C: parent │
│ │ │ │ Read source │ D: CAB │
│ → Activities from Manifest │ │ Trace calls │ E: misc │
│ → ~58% nav filled │ │ Update JSON │ │
│ → Page data loaders │ │ → ≥90% nav │ │
└────────────────────────────────┘ └──────┬───────────────────────┘
│
▼
Phase 3: Claude (report writing)
┌──────────────────────────┐
│ functional_report.md │
│ requirements_checklist │
└──────────────────────────┘
Compose Analyzer Pipeline
Phase 1: Python Script (automated) Phase 2: Claude (mandatory)
┌────────────────────────────────┐ ┌──────────────────────────────┐
│ compose_ui_behavior_analyzer │ │ Deep Semantic Analysis │
│ │ │ │
│ discovery.json ───────┐ │ │ Read JSON ──┐ │
│ pages/*.json ─────────┤ │ │ Categorize │ A: nav route │
│ source_analyses/*.json┤ │ JSON │ empty items │ B: ViewModel │
│ viewmodels/*.json ────┤ │ ───► │ │ C: dialog │
│ *.kt source ──────────┤ │ │ Read source │ D: toggle │
│ │ │ │ Trace calls │ E: external │
│ → Recursive composable expand │ │ Update JSON │ F: player │
│ → ~56% nav filled │ │ → ≥90% nav │ │
│ → 353+ indexed @Composable │ │ │ │
└────────────────────────────────┘ └──────┬───────────────────────┘
│
▼
Phase 3: Claude (report writing)
┌──────────────────────────┐
│ functional_report.md │
│ requirements_checklist │
└──────────────────────────┘
4-Column Navigation Classification
Every UI interaction is classified across 4 dimensions (shared by both agents):
| Column | What it captures | Example Values |
|---|---|---|
| Navigation | Page-level jumps | → SettingsActivity, → ScreenRoute.XXX, → [Back] |
| Screen Change | In-page state changes | Dialog: ChangeSortingDialog, UI: toggle, Refresh: adapter |
| External | System/external calls | Camera: system, Share: ACTION_SEND, Browser: URL |
| Side Effect | Persistent effects | FS: delete, DB: write, Playback: play/pause |
A single interaction can fill multiple columns. Example: a delete button may have
Navigation: → [Back] + Screen Change: Dialog + Side Effect: FS: delete.
Output Files
Both agents produce the same 3-file output structure:
<output_dir>/
├── <analysis>.json # Structured JSON evidence (Phase 1 + Phase 2)
├── functional_report.md # Per-screen: description + interaction table + data loading table
└── requirements_checklist.md # Functional requirements grouped by feature area
| File | Activity Analyzer | Compose Analyzer |
|---|---|---|
| JSON | ui_api_analysis.json |
compose_ui_behavior.json |
| Report | functional_report.md |
functional_report.md |
| Checklist | requirements_checklist.md |
requirements_checklist.md |
JSON Structure
Activity analyzer — per Activity:
ui_components[]— actionable components with 4-column targets + API dependenciespage_data_loading_entries[]— lifecycle/init methods that load page datafunctional_evidence[]— manifest, layout, fragment evidencefragment_analyses[]— nested fragment component analysis
Compose analyzer — per Screen:
interactive_components[]— @Composable components with 4-column targets + ViewModel methodsdata_loading_entries[]— LaunchedEffect, collectAsState, remember bindingsviewmodel_bindings[]— ViewModel state and method references
functional_report.md
Per-screen sections with:
- Screen Functional Description (2-4 paragraphs)
- UI Component Interaction Table (11-12 columns)
- Page-Level Data Loading Table
requirements_checklist.md
Functional requirements grouped by area:
FR-001: Gallery Browsing
Screen: MainActivity
Interactions: tap folder → MediaActivity, pull to refresh, search
Data Source: MediaFetcher → MediaStore query
Side Effects: none
Agent Comparison
| Activity Analyzer | Compose Analyzer | |
|---|---|---|
| Target UI | Traditional View system (XML layouts) | Jetpack Compose (@Composable) |
| Entry point | AndroidManifest.xml → Activities |
discovery.json → Screens |
| Layout source | res/layout/*.xml, res/menu/*.xml |
@Composable functions in .kt |
| Event binding | setOnClickListener, onOptionsItemSelected, XML onClick |
onClick, onLongClick, onChange, Modifier.clickable |
| Navigation | startActivity(Intent(...)), finish() |
navController.navigate(ScreenRoute.XXX), popBackStack() |
| Data loading | onCreate, onResume, custom loadData methods |
LaunchedEffect, collectAsState, remember |
| State management | SharedPreferences, Room DAO, direct field access | ViewModel + StateFlow, DataStore, MMKV/AppConfig |
| Phase 1 script | android_ui_api_analyzer.py |
compose_ui_behavior_analyzer.py |
| Phase 2 categories | A-E (toast, toggle, parent/ext, CAB, misc) | A-F (nav route, ViewModel, dialog, toggle, external, player) |
| Prerequisites | Android project only | Android project + compose-ui-tree-analyzer output |
| Python script | scripts/android_ui_api_analyzer.py |
scripts/compose_ui_behavior_analyzer.py |
Phase 2: How Claude Fills the Gaps
Both Phase 1 scripts use regex and static analysis — they find who is clicked but often can't trace what happens when the handler calls an indirect method. Phase 2 categorizes empty-navigation components and handles each systematically.
Activity Analyzer Categories
| Category | Pattern | Claude Action |
|---|---|---|
| A (~60) | setOnLongClickListener { toast(...) } |
Set Screen Change: UI: toast("text") |
| B (~31) | Settings toggle → config.xxx = !config.xxx |
Set UI: toggle + DB: write (config) |
| C (~13) | Menu → parent/extension method | Trace across inheritance + extension files |
| D (~27) | CAB action → adapter method | Read adapter, trace actionItemPressed branches |
| E (~35) | Editor/panorama buttons → mode switch | Read method body, classify action |
Compose Analyzer Categories
| Category | Pattern | Claude Action |
|---|---|---|
| A (~40%) | Item(onClick = { navController.navigate(...) }) |
Extract route from lambda |
| B (~25%) | onClick calls ViewModel method | Read ViewModel, determine effect |
| C (~15%) | showXxxDialog = true |
Set Screen Change: Dialog: XxxDialog |
| D (~10%) | ItemSwitcher / ItemCheck toggle |
Set UI: toggle + Config: write |
| E (~5%) | External intent (share, browser, email) | Read handler, set External |
| F | Player controls (play, pause, seek, etc.) | Read PlayerViewModel, set effects |
Files
Agents/android-ui-behavior-analyzer/
├── README.md # This file
├── android-activity-ui-behavior-analyzer.md # Agent: traditional View system
├── compose-ui-behavior-analyzer.md # Agent: Jetpack Compose
└── scripts/
├── android_ui_api_analyzer.py # Python analyzer for Activity + XML
└── compose_ui_behavior_analyzer.py # Python analyzer for Compose
Requirements
- Python 3.10+ (standard library only, no pip dependencies)
- Claude Code for Phase 2 deep semantic analysis and Phase 3 report writing
Activity Analyzer
- Android project source with
app/src/main/AndroidManifest.xml
Compose Analyzer
- Android project source with
app/src/main/ - compose-ui-tree-analyzer output (contains
discovery.json,pages/*.json,source_analyses/*.json,viewmodels/*.json)
Iron Rules (Shared)
- JSON first — read the analyzer JSON before opening source files
- Source-backed claims only — every claim traces to analyzer output or source code
- Declaration-level output only — do not narrate runtime values or full dataflow
- Prefer nearest verified declaration — if tracing stops at
Repository.getAlbums(), output that - Keep uncertainty visible — use
N/Aor cautious phrasing when evidence is weak - Deep semantic analysis is MANDATORY — Phase 2 must execute for every screen. Target >= 90% fill rate
- Full code surface — analyze all relevant source files (parent classes, extensions, adapters, ViewModels, dialogs)
- No fabrication — only output what exists in source code
- 4-column navigation — always classify targets into Navigation / Screen Change / External / Side Effect
- No confirmation needed — full read/write access, proceed without asking
- Categorize before tracing — group empty components by pattern before reading source files
Comparison with android-ui-tree-analyzer
android-ui-tree-analyzer |
android-ui-behavior-analyzer |
|
|---|---|---|
| Goal | UI layout hierarchy (view tree) | UI behavior + functional requirements |
| Output | JSON with nested view_tree per Activity/Screen | Interaction tables + requirements checklist |
| Focus | Component types, attributes, nesting | Click events, navigation, data flow, side effects |
| Use case | HarmonyOS ArkTS UI conversion | Feature/requirement documentation |
| Navigation | Single navigates_to field |
4 columns: Navigation / Screen Change / External / Side Effect |
| API tracing | None | DAO, Retrofit, ContentResolver, ViewModel, MediaStore, system intents |
| Data loading | None | Lifecycle init + LaunchedEffect / collectAsState analysis |
Known Limitations
Shared
- Phase 1 scripts cover ~56-58% of navigation targets — Phase 2 (Claude) is required to reach 90%+
- Reflection / dynamic registration — event handlers registered via reflection are not detected
- Deep indirect calls (>3 levels) — call chain tracing stops at depth 2-3
- Third-party library callbacks — internal behavior of libraries (Glide, ExoPlayer, etc.) not traced
- Runtime-conditional behavior — all code paths reported; cannot determine which runs at runtime
Activity Analyzer Only
- Compose UI — Jetpack Compose
@Composablefunctions are not analyzed (usecompose-ui-behavior-analyzerinstead)
Compose Analyzer Only
- Requires compose-ui-tree-analyzer output — must run
compose-ui-tree-analyzerfirst to generate the input data - Custom composable expansion depth — recursive expansion stops at 2 levels deep