MCPcopy Create free account
hub / github.com/Action-State-Labs/android-action-kernel / get_interactive_elements

Function get_interactive_elements

sanitizer.py:4–69  ·  view source on GitHub ↗

Parses Android Accessibility XML and returns a lean list of interactive elements. Calculates center coordinates (x, y) for every clickable element.

(xml_content: str)

Source from the content-addressed store, hash-verified

2from typing import List, Dict, Optional
3
4def get_interactive_elements(xml_content: str) -> List[Dict]:
5 """
6 Parses Android Accessibility XML and returns a lean list of interactive elements.
7 Calculates center coordinates (x, y) for every clickable element.
8 """
9 try:
10 root = ET.fromstring(xml_content)
11 except ET.ParseError:
12 print("⚠️ Error parsing XML. The screen might be loading.")
13 return []
14
15 elements = []
16
17 # Recursively find all nodes
18 for node in root.iter():
19 # Filter: We only care about elements that are interactive or have information
20 is_clickable = node.attrib.get("clickable") == "true"
21 # Check for actual text input fields (not just focusable elements)
22 element_class = node.attrib.get("class", "")
23 is_editable = (
24 "EditText" in element_class or
25 "AutoCompleteTextView" in element_class or
26 node.attrib.get("editable") == "true"
27 )
28 text = node.attrib.get("text", "")
29 desc = node.attrib.get("content-desc", "")
30 resource_id = node.attrib.get("resource-id", "")
31
32 # Skip empty layout containers that do nothing
33 if not is_clickable and not is_editable and not text and not desc:
34 continue
35
36 # Parse Bounds: "[140,200][400,350]" -> Center X, Y
37 bounds = node.attrib.get("bounds")
38 if bounds:
39 try:
40 # Extract coordinates
41 coords = bounds.replace("][", ",").replace("[", "").replace("]", "").split(",")
42 x1, y1, x2, y2 = map(int, coords)
43
44 center_x = (x1 + x2) // 2
45 center_y = (y1 + y2) // 2
46
47 # Determine suggested action based on element type
48 if is_editable:
49 suggested_action = "type"
50 elif is_clickable:
51 suggested_action = "tap"
52 else:
53 suggested_action = "read"
54
55 element = {
56 "id": resource_id,
57 "text": text or desc, # Fallback to content-desc if text is empty
58 "type": node.attrib.get("class", "").split(".")[-1],
59 "bounds": bounds,
60 "center": (center_x, center_y),
61 "clickable": is_clickable,

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected