Android UI View-Tree Analyzer
A suite of two Claude Code custom agents that statically and semantically analyze Android app projects to extract complete, hierarchical UI information as view trees. Designed for downstream HarmonyOS ArkTS UI generation.
Two specialized agents handle different Android UI paradigms:
| Agent | Target UI | Invocation |
|---|---|---|
android-activity-ui-tree-analyzer |
Traditional XML layouts (Activity + Layout XML) | @android-activity-ui-tree-analyzer <project> [output] |
android-compose-ui-tree-analyzer |
Jetpack Compose UI (Composable screens + NavHost) | @android-compose-ui-tree-analyzer <project> [output] |
Both agents combine Python scripts for deterministic parsing + Claude for semantic code understanding + optional runtime capture via adb to achieve ~95%+ UI information coverage.
Agent 1: android-activity-ui-tree-analyzer
Analyzes Android apps built with traditional XML layouts. Discovers every Activity with 100% recall, parses layout XML into proper nested trees, analyzes source code for click handlers/menus/dialogs/fragments, merges everything into per-Activity JSON files, and runs automated verification.
Quick Start
@android-activity-ui-tree-analyzer /path/to/android/project [/path/to/output]
- Project Path: root directory of the Android project (must contain
app/src/main/AndroidManifest.xml) - Output Directory: where all output files are written (default:
<project>/UI_Analysis)
Architecture
Android Project Source
|
+----------------+----------------+
v v v
AndroidManifest.xml res/layout/*.xml *.kt / *.java
| | |
Phase 1: Phase 2: Phase 3:
Activity XML -> Tree Source Code
Discovery Parser Analyzer
| | |
| | Phase 3b:
| | Claude Deep
| | Semantic <-- MANDATORY
| | Analysis
| | |
| Phase 3.5: |
| Runtime UI (optional)
| Dump
| | |
+--------+-------+--------+------+
v
Phase 4: Merge
v
Phase 5: Verify
v
Final per-Activity JSON
+ UI_Design.md
+ verification_report.md
Pipeline Phases
| Phase | Script | Output | Description |
|---|---|---|---|
| 1 | discover_activities.py |
discovery.json |
Parse AndroidManifest.xml, scan source dirs (incl. build flavors, includeBuild libraries), cross-reference manifest vs. source |
| 2 | parse_layout_xml.py |
layout_trees/*.json, resolved_resources.json |
Parse layout XML into hierarchical trees; resolve <include>/<merge>, resource references (@string/, @color/, @dimen/) |
| 3 | analyze_source.py |
source_analyses/*.json, component_maps.json |
Regex-based extraction: layout binding, click handlers, menus, dialogs, fragments, dynamic views; global adapter/fragment/dialog scanning |
| 3b | Claude deep analysis | Updated source_analyses/*.json |
MANDATORY. Trace inheritance chains, extension functions, adapter callbacks, menu handlers, dialog triggers to reach ~95%+ coverage |
| 3.5 | runtime_dump.py |
runtime_dumps/*.xml |
Optional. Capture runtime view hierarchy via adb shell uiautomator dump |
| 4 | merge_ui_tree.py |
pages/*.json, UI_Design.md |
Merge all sources; inline adapter item layouts, fragment layouts, dialog layouts, runtime data |
| 5 | verify_output.py |
verification_report.md |
Automated quality checks: Activity recall, view completeness, resource resolution, tree integrity |
Phase 3b — Deep Semantic Analysis Detail
Claude reads source code across the full inheritance chain for every Activity:
| Step | What | Why |
|---|---|---|
| Trace inheritance chain | Read Activity -> SimpleActivity -> BaseSimpleActivity |
Catch inherited click handlers and menu handlers |
| Scan extension functions | Search for fun SimpleActivity.launchXxx() |
Catch navigation methods defined in extension files |
| Trace adapter callbacks | Follow DirectoryAdapter(itemClick = { ... }) to final startActivity() |
Catch indirect item click navigation |
| Trace menu handlers | Follow R.id.settings -> launchSettings() -> startActivity() |
Catch menu-triggered navigation |
| Trace dialog triggers | Find which view click opens which dialog, and what dialog buttons do | Map dialog lifecycle to UI components |
Output Format
Each Activity produces a JSON file in pages/:
{
"page_name": "MainActivity",
"activity_class": "com.example.app.activities.MainActivity",
"source_file": "app/src/main/kotlin/.../MainActivity.kt",
"layout_file": "activity_main.xml",
"description": "...",
"window": { "theme": "...", "screen_orientation": "...", "exported": true },
"view_tree": {
"type": "CoordinatorLayout",
"id": "main_coordinator",
"third_party": false,
"attributes": { "android:layout_width": "match_parent" },
"children": [
{
"type": "RecyclerView",
"id": "items_grid",
"children": [],
"actions": [
{ "event": "onItemClick", "navigates_to": "MediaActivity" }
],
"adapter_items": [
{
"adapter": "DirectoryAdapter",
"layout_file": "directory_item_grid.xml",
"view_tree": { "type": "RelativeLayout", "children": [ "..." ] }
}
]
}
]
},
"menus": [ { "menu_file": "menu_main", "items": [ "..." ] } ],
"dialogs": [ { "dialog_class": "ChangeSortingDialog", "view_tree": { "..." : "..." } } ],
"fragments": [ { "fragment_class": "PhotoFragment", "view_tree": { "..." : "..." } } ],
"dynamic_elements": []
}
Key properties:
view_tree— single root node with nestedchildren[], preserving exact XML hierarchyadapter_items— on RecyclerView nodes, contains parsed item layout treesfragments[].view_tree/dialogs[].view_tree— inlined full hierarchiesactions[]— click handlers with navigation targets, attached to the correct tree noderuntime_bounds,_runtime_only— present when runtime capture is available
Verification Criteria
| Check | Pass Criteria |
|---|---|
| Activity Recall | 100% — every manifest activity has a JSON |
| View Completeness | ID coverage >= 95% per Activity |
| Resource Resolution | >= 95% references resolved |
| Tree Integrity | No structural issues |
| Layout Binding | Activities correctly mapped to layouts |
Iron Rules
- 100% Activity Recall — Every
<activity>in AndroidManifest.xml must have a corresponding JSON output - True Hierarchical Tree —
view_treepreserves exact parent-child nesting from layout XML - Complete Attribute Capture — Every XML attribute on every view must appear
- Resolved Resource Values —
@string/app_name->"Simple Gallery",@color/primary->"#F57C00" - Include Resolution —
<include>replaced with actual layout tree;<merge>children lifted - Third-Party Flagging — Non-Android/AndroidX views marked
"third_party": true - Deep Semantic Analysis is MANDATORY — Phase 3b must execute for EVERY Activity
- No Fabrication — Only output what exists in source code
Test Results (Simple-Gallery-master)
Activity Recall: 100% (20/20 manifest + 6 extras)
View Tree Nodes: 347 (static XML)
Adapter Item Nodes: 115 (6 adapters, 13 item layouts)
Fragment Layout Nodes: 30 (PhotoFragment + VideoFragment)
Dialog Layout Nodes: 536 (56 dialog classes)
Runtime Enrichments: 50 (3 Activities captured)
Click Handler Coverage: ~95% (with Phase 3b deep analysis)
-----------------------------------------------
Total UI Nodes: 1078 (3x improvement over static-only)
Agent 2: android-compose-ui-tree-analyzer
Analyzes Android apps built with Jetpack Compose. Discovers every Activity and Compose navigation screen with 100% recall, parses @Composable functions into nested component trees, analyzes source code for navigation/state/events, merges everything into per-screen JSON files, and runs automated verification.
Quick Start
@android-compose-ui-tree-analyzer /path/to/android/project [/path/to/output]
- Project Path: root directory of the Android project (must contain
app/src/main/AndroidManifest.xml) - Output Directory: where all output files are written (default:
<project>/Compose_UI_Analysis)
Prerequisites
pip3 install tree-sitter tree-sitter-kotlin
Architecture
Android Project Source (Compose)
|
+----------+----------+----------+-----------+
v v v v v
Manifest ScreenRoute ComposeNav *.kt res/values/
| | | | |
Phase 1: Discover all screens, routes, registrations,
shell components (dynamic detection)
|
Phase 1.5: ViewModel analysis + Theme token extraction
|
Phase 2: tree-sitter AST -> compose_trees/*.json
(Modifier parsing, content_params, custom expansion)
|
Phase 2b: Legacy XML layouts (xml-based Activities only)
|
Phase 3a: Automated source analysis (regex: ViewModel, nav, dialogs)
|
Phase 3b: Claude deep semantic analysis (MANDATORY)
|
Phase 3.5: Runtime UI capture (optional)
|
Phase 4: Merge -> pages/*.json + navigation_graph.json + UI_Design.md
|
Phase 5: Verify -> verification_report.md (11 checks)
Pipeline Phases
| Phase | Script | Output | Description |
|---|---|---|---|
| 1 | discover_all_screens.py |
discovery.json |
Activities from manifest, route constants from ScreenRoute.kt, composable registrations from ComposeNavigation.kt, shell components from setContent {} |
| 1.5 | analyze_viewmodels.py |
viewmodels/*.json |
StateFlow/LiveData properties, sealed UiState definitions, public method signatures |
| 1.5b | extract_theme_tokens.py |
theme_tokens.json |
SaltTheme color/textStyle/dimension token catalog |
| 2 | parse_compose_tree_v2.py |
compose_trees/*.json |
tree-sitter AST parsing: structured Modifier, content_params, conditional blocks, custom composable one-level expansion, R.string resolution |
| 2b | parse_layout_xml.py |
layout_trees/*.json |
Legacy XML layouts only (for Activities with ui_type: "xml") |
| 3a | analyze_compose_source.py |
source_analyses/*.json |
Automated: ViewModel bindings, state collection, navigation targets, dialog state, LaunchedEffect, sealed UiState variants |
| 3b | Claude deep analysis | Updated source_analyses/*.json |
MANDATORY. Complex navigation in nested lambdas, custom composable resolution, ViewModel method effects, cross-file navigation chains |
| 3.5 | Runtime capture | runtime_dumps/ |
Optional. Compose semantics tree dump, Layout Inspector, or UIAutomator |
| 4 | merge_compose_output.py |
pages/*.json, navigation_graph.json, UI_Design.md |
Merge all sources into final per-page JSON with navigation graph |
| 5 | verify_compose_output.py |
verification_report.md |
11 automated checks including semantic validation |
Phase 2 — tree-sitter AST Parsing Detail
Uses tree-sitter-kotlin for proper AST parsing (not regex+brace-depth). Handles:
- Chained call pattern —
Foo(params) { content }correctly parsed - Structured Modifier parsing —
.fillMaxSize().padding(16.dp)-> structured array - Content parameter lambdas —
titleContent = { ... },sheetContent = { ... }parsed intocontent_params - Transparent containers —
CompositionLocalProvider { ... }penetrated for content - Control flow preservation —
if/when/elseas ConditionalBlock/WhenBlock nodes - R.string resolution —
stringResource(R.string.xxx)resolved fromres/values/strings.xml - Custom composable expansion — one-level auto-expansion with
expanded_children[]
Phase 3b — Deep Semantic Analysis Detail
Claude fills remaining ~15% gaps that automated Phase 3a cannot capture:
| Step | What |
|---|---|
| Read screen source | Identify wrapper, ViewModel access, state collection, CompositionLocal usage |
| Trace navigation | Every navController.navigate() and startActivity() traced to trigger component |
| Resolve custom composables | Find @Composable definition, build sub-tree, record children |
| Analyze event handlers | Trace onClick lambdas to final effect (ViewModel method, dialog, state toggle) |
Output Format
Compose Screens (NavHost routes)
{
"page_name": "SettingsScreen",
"page_type": "compose_screen",
"route": "SETTINGS",
"route_pattern": "settings",
"route_arguments": [],
"parent_activity": "MainActivity",
"source_file": "app/src/main/.../SettingsScreen.kt",
"view_model": "SettingsViewModel",
"compose_tree": [
{
"type": "BasicScreenColumn",
"salt_semantic": "screen_wrapper_scrollable",
"library": "salt_ui",
"arkts_hint": "Navigation + Scroll + Column",
"children": [
{
"type": "Item",
"salt_semantic": "list_item",
"clickable": true,
"arkts_hint": "ListItem with onClick",
"params": { "text": "Salt Player Pro" },
"actions": [
{ "event": "onClick", "navigates_to": "SALT_PLAYER_PRO" }
]
}
]
}
],
"navigation_targets": ["SALT_PLAYER_PRO", "USER_INTERFACE"],
"dialogs": [],
"state_description": "Settings hub with grouped navigation items"
}
Activities
{
"page_name": "MainActivity",
"page_type": "activity",
"is_activity": true,
"activity_class": "com.salt.music.ui.MainActivity",
"ui_type": "compose",
"window": { "theme": "...", "exported": true, "launch_mode": "singleTask" },
"compose_entry": "MainActivityUI",
"compose_tree": [ "..." ]
}
Shell Components
{
"page_name": "PlayerScreen",
"page_type": "shell_component",
"shell_role": "full_player_overlay",
"source_file": "app/src/main/.../PlayerScreen.kt",
"compose_tree": [ "..." ]
}
Key properties:
compose_tree— list of root nodes with nestedchildren[], a true hierarchical treesalt_semantic— maps Salt UI components to semantic UI typesarkts_hint— suggests equivalent HarmonyOS ArkUI componentcustom: true— flags app-specific composablesConditionalBlock/WhenBlock— conditional UI rendering preservedcontent_params— named content lambdas (e.g.,sheetContent,titleContent)
Salt UI Component Mapping
| Salt UI | Semantic Type | ArkTS Hint |
|---|---|---|
BasicScreenColumn |
screen_wrapper_scrollable | Navigation + Scroll + Column |
BasicScreenBox |
screen_wrapper_box | Navigation + Stack |
RoundedColumn |
grouped_container | Column with borderRadius |
Item |
list_item (clickable) | ListItem with onClick |
ItemSwitcher |
toggle_item | ListItem with Toggle |
ItemCheck |
checkbox_item | ListItem with Checkbox |
ItemSlider |
slider_item | ListItem with Slider |
ItemTitle |
section_title | Text with section styling |
YesDialog |
confirm_dialog | AlertDialog (confirm) |
YesNoDialog |
confirm_cancel_dialog | AlertDialog (confirm/cancel) |
BottomSheetScaffold |
bottom_sheet_scaffold | Sheet + Stack |
Verification Criteria
| Check | Type | Pass Criteria |
|---|---|---|
| Activity Recall | Critical | 100% — every manifest activity has output |
| Route Constant Recall | Critical | 100% — every ScreenRoute constant in discovery |
| Composable Registration Coverage | Critical | 100% — every composable() has output |
| Tree Integrity | Critical | No structural issues |
| Shell Completeness | Critical | All shell components have output |
| Navigation Target Validity | Advisory | Some dynamic routes acceptable |
| Custom Composable Resolution | Advisory | Simple wrappers acceptable unresolved |
| Clickable Action Completeness | Advisory | Every clickable should have handler info |
| ViewModel Source Analysis | Advisory | Screens with ViewModel should have state info |
| String Resource Resolution | Advisory | Tracks R.string resolution rate |
| Phase 3a Coverage | Advisory | Summary of automated analysis coverage |
Iron Rules
- 100% Activity Recall — Every
<activity>in AndroidManifest.xml must have output - 100% Route Recall — Every
const valinScreenRoute.ktmust be in discovery - 100% Composable Registration Coverage — Every
composable()in ComposeNavigation.kt must have output - True Hierarchical Tree —
compose_treepreserves Compose component nesting - Salt UI Semantic Mapping — All Salt UI components must have
salt_semanticandarkts_hint - Custom Composable Flagging — Non-standard composables marked
"custom": true - Conditional UI Preservation —
if/whenblocks preserved as ConditionalBlock/WhenBlock nodes - Deep Semantic Analysis is MANDATORY — Phase 3a (automated) + Phase 3b (Claude) for every screen
- No Fabrication — Only output what exists in source code
Shared Infrastructure
Scripts
scripts/
├── discover_activities.py # Phase 1 (Activity agent): Activity discovery
├── discover_all_screens.py # Phase 1 (Compose agent): Activity + route + registration discovery
├── parse_layout_xml.py # Phase 2: XML -> hierarchical tree (shared)
├── parse_compose_tree.py # Phase 2 (Compose, v1): regex+brace-depth parser (legacy)
├── parse_compose_tree_v2.py # Phase 2 (Compose, v2): tree-sitter AST parser
├── salt_ui_mapping.py # Salt UI -> semantic type + ArkTS hint mapping
├── analyze_source.py # Phase 3 (Activity agent): regex-based source analysis
├── analyze_compose_source.py # Phase 3a (Compose agent): automated source analysis
├── analyze_viewmodels.py # Phase 1.5 (Compose agent): ViewModel property extraction
├── extract_theme_tokens.py # Phase 1.5b (Compose agent): SaltTheme token catalog
├── merge_ui_tree.py # Phase 4 (Activity agent): merge all sources
├── merge_compose_output.py # Phase 4 (Compose agent): merge all sources
├── verify_output.py # Phase 5 (Activity agent): automated verification
└── verify_compose_output.py # Phase 5 (Compose agent): automated verification (11 checks)
Requirements
- Python 3.6+ — for running analysis scripts
- tree-sitter + tree-sitter-kotlin — required for Compose agent Phase 2
- Android project source — with
app/src/main/AndroidManifest.xml - Claude Code — for Phase 3b deep semantic analysis
- adb + device/emulator (optional) — for runtime capture
Choosing the Right Agent
| Your Android Project Uses | Use This Agent |
|---|---|
XML layouts (res/layout/*.xml) with Activities/Fragments |
android-activity-ui-tree-analyzer |
| Jetpack Compose with NavHost routing | android-compose-ui-tree-analyzer |
| Mixed (some Activities use XML, some use Compose) | Run both agents; Compose agent handles XML Activities via Phase 2b |
Coverage Summary
| Dimension | Activity Agent | Compose Agent |
|---|---|---|
| Activity discovery | 100% | 100% |
| Route/registration discovery | N/A | 100% |
| XML layout hierarchy | 100% | Legacy only |
| Compose component tree | N/A | 100% (tree-sitter) |
| Resource resolution | 99.8% | R.string resolved |
| RecyclerView/adapter items | 100% | N/A (Compose uses LazyColumn) |
| Fragment layouts | 100% | N/A |
| Dialog layouts | ~53% | Via source analysis |
| Click handlers / navigation | ~95%+ | ~95%+ |
| ViewModel state | N/A | Full (sealed UiState) |
| Salt UI semantic mapping | N/A | Full |
| Runtime capture | Optional (uiautomator) | Optional (semantics/uiautomator) |
Known Limitations
- Theme/Style attribute expansion —
@style/MyStylenot expanded (Activity agent) - Conditional UI visibility — Static analysis captures all views but cannot determine runtime visibility state
- Custom View internals — Only available via runtime capture (Activity agent)
- Purely programmatic views — Views created entirely in code have limited accuracy
- Dynamic Compose content — Content generated from runtime data (API responses) captured structurally but not with actual values