Source code for ui_node
import re
from typing import Optional, Tuple
[docs]
class UINode:
"""
A class to represent a UI node in an Android UI hierarchy.
"""
def __init__(self, element, parent: Optional['UINode'] = None):
"""
Initializes the UINode class.
:param element: The XML element representing the UI node.
:param parent: The parent UINode, defaults to None.
"""
self.element = element
self.parent = parent
self.children = []
self._parse_attributes()
[docs]
def add_child(self, child: 'UINode') -> None:
"""
Adds a child node to this node.
:param child: The child UINode to add.
"""
self.children.append(child)
[docs]
def tree(self, level: int = 0) -> str:
"""
Returns a string representation of the node tree.
:param level: The current level in the tree, defaults to 0.
:return: A string representation of the node tree.
"""
ret = " " * level + str(self) + "\n"
for child in self.children:
ret += child.tree(level + 1)
return ret
def _parse_attributes(self) -> None:
"""
Parses the attributes of the XML element and sets them as attributes of the node.
"""
self.index = int(self.element.get('index', '0'))
self.text = self.element.get('text', '')
self.resource_id = self.element.get('resource-id', '')
self.node_class = self.element.get('class', '')
self.package = self.element.get('package', '')
self.content_desc = self.element.get('content-desc', '')
self.checkable = self.element.get('checkable', 'false').lower() == 'true'
self.checked = self.element.get('checked', 'false').lower() == 'true'
self.clickable = self.element.get('clickable', 'false').lower() == 'true'
self.enabled = self.element.get('enabled', 'true').lower() == 'true'
self.focusable = self.element.get('focusable', 'false').lower() == 'true'
self.focused = self.element.get('focused', 'false').lower() == 'true'
self.scrollable = self.element.get('scrollable', 'false').lower() == 'true'
self.long_clickable = self.element.get('long-clickable', 'false').lower() == 'true'
self.password = self.element.get('password', 'false').lower() == 'true'
self.selected = self.element.get('selected', 'false').lower() == 'true'
self.bounds = self._parse_bounds(self.element.get('bounds', ''))
if self.bounds:
self.center = ((self.bounds[0] + self.bounds[2]) / 2,
(self.bounds[1] + self.bounds[3]) / 2)
else:
self.center = None
def _parse_bounds(self, bounds_str: str) -> Optional[Tuple[int, int, int, int]]:
"""
Parses the bounds attribute and returns it as a tuple.
:param bounds_str: The bounds attribute as a string.
:return: A tuple representing the bounds, or None if parsing fails.
"""
match = re.match(r'\[(\d+),(\d+)\]\[(\d+),(\d+)\]', bounds_str)
if match:
return (int(match.group(1)), int(match.group(2)), int(match.group(3)), int(match.group(4)))
return None
def __str__(self) -> str:
"""
Returns a string representation of the node.
:return: A string representation of the node.
"""
return (f"UINode(class={self.node_class}, resource_id={self.resource_id}, "
f"text={self.text}, children={len(self.children)})")