CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-uiautomator2

A simple, easy-to-use, and stable Android automation library

Overview
Eval results
Files

ui-interaction.mddocs/

UI Element Selection and Interaction

Comprehensive UI element selection using UiAutomator selectors, coordinate-based targeting, and gesture operations. Supports clicking, text input, gestures, and element property inspection.

Capabilities

Element Selection

Select UI elements using UiAutomator selector attributes.

class Device:
    def __call__(self, **kwargs) -> UiObject:
        """
        Create UI element selector.

        Parameters:
        - text: Exact text match
        - textContains: Partial text match
        - textMatches: Regular expression text match
        - textStartsWith: Text prefix match
        - className: UI element class name
        - classNameMatches: Regular expression class name match
        - description: Content description match
        - descriptionContains: Partial description match
        - descriptionMatches: Regular expression description match
        - descriptionStartsWith: Description prefix match
        - resourceId: Resource ID match  
        - resourceIdMatches: Regular expression resource ID match
        - packageName: Package name match
        - packageNameMatches: Regular expression package name match
        - checkable: Boolean checkable state
        - checked: Boolean checked state
        - clickable: Boolean clickable state
        - longClickable: Boolean long clickable state
        - scrollable: Boolean scrollable state
        - enabled: Boolean enabled state
        - focusable: Boolean focusable state
        - focused: Boolean focused state
        - selected: Boolean selected state
        - index: Element index within parent
        - instance: Element instance number

        Returns:
        UiObject for interaction
        """

    def exists(self, **kwargs) -> bool:
        """Check if UI element exists using selector attributes"""

Usage examples:

d = u2.connect()

# Select by text
button = d(text="OK")
login_btn = d(textContains="Login")
submit_btn = d(textMatches=r"Submit|Send")

# Select by resource ID
username_field = d(resourceId="com.example:id/username")
password_field = d(resourceId="com.example:id/password")

# Select by class and attributes
text_view = d(className="android.widget.TextView", clickable=True)
edit_text = d(className="android.widget.EditText", enabled=True)

# Select by index
first_item = d(className="android.widget.LinearLayout", index=0)

# Check existence
if d(text="Settings").exists:
    print("Settings button found")

Element Properties and Information

Access UI element properties and attributes.

class UiObject:
    @property
    def exists(self) -> bool:
        """Check if element exists in current UI"""

    @property
    def info(self) -> Dict[str, Any]:
        """Get element attributes including bounds, text, className, etc."""

    def screenshot(self, display_id: Optional[int] = None) -> Image.Image:
        """Take screenshot of element bounds"""

Usage examples:

d = u2.connect()

# Get element info
element = d(resourceId="com.example:id/title")
if element.exists:
    info = element.info
    print(f"Text: {info['text']}")
    print(f"Bounds: {info['bounds']}")
    print(f"Class: {info['className']}")
    print(f"Clickable: {info['clickable']}")

# Screenshot element
element_image = element.screenshot()
element_image.save("element.png")

Coordinate-based Interaction

Direct interaction using screen coordinates.

class Device:
    def click(self, x: Union[float, int], y: Union[float, int]):
        """
        Click at coordinates.

        Parameters:
        - x: X coordinate (absolute pixels or relative 0-1)
        - y: Y coordinate (absolute pixels or relative 0-1)
        """

    def double_click(self, x, y, duration=0.1):
        """
        Double click at coordinates.

        Parameters:
        - x, y: Coordinates
        - duration: Delay between clicks in seconds
        """

    def long_click(self, x, y, duration: float = 0.5):
        """
        Long click at coordinates.

        Parameters:
        - x, y: Coordinates  
        - duration: Press duration in seconds
        """

    @property
    def pos_rel2abs(self):
        """Function to convert relative (0-1) to absolute pixel coordinates"""

Usage examples:

d = u2.connect()

# Absolute coordinates
d.click(100, 200)
d.double_click(300, 400)
d.long_click(500, 600, duration=2.0)

# Relative coordinates (0-1 range)
d.click(0.5, 0.3)  # Center horizontally, 30% from top
d.click(0.1, 0.9)  # Near bottom-left corner

# Convert relative to absolute
convert = d.pos_rel2abs
abs_x, abs_y = convert(0.5, 0.5)  # Get center coordinates

Gesture Operations

Swipe, drag, and multi-touch gestures.

class Device:
    def swipe(self, fx, fy, tx, ty, duration: Optional[float] = None, steps: Optional[int] = None):
        """
        Swipe from one point to another.

        Parameters:
        - fx, fy: From coordinates
        - tx, ty: To coordinates
        - duration: Swipe duration in seconds
        - steps: Number of interpolation steps (overrides duration)
        """

    def swipe_points(self, points: List[Tuple[int, int]], duration: float = 0.5):
        """
        Multi-point swipe gesture.

        Parameters:
        - points: List of (x, y) coordinate tuples
        - duration: Total gesture duration
        """

    def drag(self, sx, sy, ex, ey, duration=0.5):
        """
        Drag from start to end coordinates.

        Parameters:
        - sx, sy: Start coordinates
        - ex, ey: End coordinates  
        - duration: Drag duration in seconds
        """

    @property
    def touch(self) -> TouchActions:
        """Touch gesture builder for complex multi-touch operations"""

Usage examples:

d = u2.connect()

# Basic swipe gestures
d.swipe(100, 500, 100, 100)  # Swipe up
d.swipe(500, 300, 100, 300)  # Swipe left
d.swipe(100, 100, 500, 500, duration=2.0)  # Slow diagonal swipe

# Multi-point swipe
points = [(100, 100), (200, 150), (300, 100), (400, 200)]
d.swipe_points(points, duration=1.5)

# Drag operation
d.drag(100, 100, 500, 500, duration=1.0)

# Complex touch gestures
d.touch.down(100, 100).move(200, 200).sleep(0.5).up(200, 200)

Advanced Gesture Support

Extended swipe functionality for common UI patterns.

class Device:
    @cached_property
    def swipe_ext(self) -> SwipeExt:
        """Extended swipe gestures for common UI patterns"""

class SwipeExt:
    def up(self, scale: float = 0.8):
        """Swipe up gesture"""

    def down(self, scale: float = 0.8):
        """Swipe down gesture"""

    def left(self, scale: float = 0.8):
        """Swipe left gesture"""

    def right(self, scale: float = 0.8):
        """Swipe right gesture"""

Usage examples:

d = u2.connect()

# Extended swipe gestures
d.swipe_ext.up(scale=0.9)    # Swipe up 90% of screen
d.swipe_ext.down()           # Swipe down default 80%
d.swipe_ext.left(scale=0.7)  # Swipe left 70% of screen
d.swipe_ext.right()          # Swipe right default 80%

Element Interaction

Direct interaction with selected UI elements.

class UiObject:
    def click(self, timeout: Optional[float] = None):
        """Click the UI element"""

    def long_click(self, duration: float = 0.5):
        """Long click the UI element"""

    def set_text(self, text: str):
        """Set text content of element"""

    def clear_text(self):
        """Clear text content of element"""

    def wait(self, timeout: Optional[float] = None) -> bool:
        """Wait for element to appear"""

    def wait_gone(self, timeout: Optional[float] = None) -> bool:
        """Wait for element to disappear"""

    def click_exists(self, timeout=0) -> bool:
        """Click element if it exists within timeout"""

    def click_gone(self, maxretry=10, interval=1.0):
        """Click element repeatedly until it disappears"""

    def bounds(self) -> Tuple[int, int, int, int]:
        """Get element bounds (left, top, right, bottom)"""

    def center(self, offset=(0.5, 0.5)) -> Tuple[int, int]:
        """Get element center coordinates with optional offset"""

    def drag_to(self, **kwargs):
        """Drag element to another element or coordinates"""

Usage examples:

d = u2.connect()

# Element interaction
button = d(text="Submit")
button.click()

# Text input
username = d(resourceId="com.example:id/username")
username.clear_text()
username.set_text("john_doe")

# Wait for elements
loading = d(text="Loading...")
loading.wait(timeout=10)  # Wait up to 10 seconds
loading.wait_gone(timeout=30)  # Wait for loading to disappear

# Advanced element interaction
button = d(text="Optional Button")
if button.click_exists(timeout=5):  # Click if exists within 5 seconds
    print("Button clicked")

# Remove popup by clicking repeatedly
popup_close = d(resourceId="close_popup")
popup_close.click_gone(maxretry=5, interval=0.5)  # Click until gone

# Get element position and size
element = d(resourceId="target_element")
bounds = element.bounds()  # (left, top, right, bottom) 
print(f"Element bounds: {bounds}")

center_x, center_y = element.center()  # Get center point
print(f"Element center: ({center_x}, {center_y})")

# Get center with offset (0.8, 0.2 means 80% right, 20% down within element)
offset_point = element.center(offset=(0.8, 0.2))

# Drag element to another location
source = d(text="Drag Me")
target = d(text="Drop Here") 
source.drag_to(target)  # Drag to another element
source.drag_to(x=500, y=300)  # Drag to coordinates

Element Navigation and Hierarchy

Navigate through UI hierarchy using parent-child relationships.

class UiObject:
    def child(self, **kwargs) -> UiObject:
        """Find child element using selector attributes"""
    
    def sibling(self, **kwargs) -> UiObject:
        """Find sibling element using selector attributes"""
    
    def child_by_text(self, txt: str, **kwargs) -> UiObject:
        """Find child element by text match"""
    
    def child_by_description(self, txt: str, **kwargs) -> UiObject:
        """Find child element by description match"""
    
    def child_by_instance(self, inst: int, **kwargs) -> UiObject:
        """Find child element by instance number"""

Usage examples:

d = u2.connect()

# Navigate through hierarchy
container = d(resourceId="container")
submit_button = container.child(text="Submit")
submit_button.click()

# Find specific child by text
menu = d(className="android.widget.LinearLayout")
settings_item = menu.child_by_text("Settings")
settings_item.click()

# Find sibling elements
current_item = d(text="Current Item")
next_item = current_item.sibling(text="Next Item")
next_item.click()

Relative Positioning

Find elements relative to other elements on screen.

class UiObject:
    def right(self, **kwargs) -> UiObject:
        """Find element to the right of current element"""
    
    def left(self, **kwargs) -> UiObject:
        """Find element to the left of current element"""
    
    def up(self, **kwargs) -> UiObject:
        """Find element above current element"""
    
    def down(self, **kwargs) -> UiObject:
        """Find element below current element"""

Usage examples:

d = u2.connect()

# Relative positioning
username_field = d(resourceId="username")
password_field = username_field.down(resourceId="password")
password_field.set_text("secret123")

# Find button to the right of a label
price_label = d(text="Price:")
edit_button = price_label.right(text="Edit")
edit_button.click()

Scrolling and Flinging

Scroll elements within containers with precise control.

class UiObject:
    @property
    def scroll(self):
        """Scroll gesture builder for element"""
    
    @property  
    def fling(self):
        """Fling gesture builder for element"""

class ScrollBuilder:
    def forward(self, steps: int = 10): """Scroll forward (up/left)"""
    def backward(self, steps: int = 10): """Scroll backward (down/right)"""
    def toBeginning(self, steps: int = 10): """Scroll to beginning"""
    def toEnd(self, steps: int = 10): """Scroll to end"""
    def to(self, **kwargs): """Scroll until element is visible"""

class FlingBuilder:
    def forward(self): """Fling forward quickly"""
    def backward(self): """Fling backward quickly"""
    def toBeginning(self): """Fling to beginning"""
    def toEnd(self): """Fling to end"""

Usage examples:

d = u2.connect()

# Scroll within a list or container
scrollable_list = d(scrollable=True)
scrollable_list.scroll.forward(steps=5)  # Scroll up 5 steps
scrollable_list.scroll.backward(steps=10)  # Scroll down 10 steps
scrollable_list.scroll.toBeginning()  # Scroll to top
scrollable_list.scroll.toEnd()  # Scroll to bottom

# Scroll until specific element is visible
scrollable_list.scroll.to(text="Target Item")

# Fast fling gestures
scrollable_list.fling.forward()  # Quick fling up
scrollable_list.fling.toEnd()  # Quick fling to bottom

Advanced Gestures

Multi-finger gestures for complex interactions.

class UiObject:
    def gesture(self, start1: Tuple[float, float], start2: Tuple[float, float], 
                end1: Tuple[float, float], end2: Tuple[float, float], steps: int = 100):
        """Two-finger gesture between specified points"""
    
    def pinch_in(self, percent: int = 100, steps: int = 50):
        """Pinch in gesture (zoom out)"""
    
    def pinch_out(self, percent: int = 100, steps: int = 50):
        """Pinch out gesture (zoom in)"""

Usage examples:

d = u2.connect()

# Pinch gestures on image or map
image_view = d(className="ImageView")
image_view.pinch_out(percent=200, steps=30)  # Zoom in 200%
image_view.pinch_in(percent=50, steps=30)  # Zoom out 50%

# Custom two-finger gesture
map_view = d(resourceId="map_view")
# Two-finger drag from corners to center
map_view.gesture(start1=(0.1, 0.1), start2=(0.9, 0.9), 
                end1=(0.4, 0.4), end2=(0.6, 0.6), steps=50)

Element Indexing and Iteration

Work with multiple matching elements using indexing and iteration.

class UiObject:
    def __getitem__(self, index: int) -> UiObject:
        """Get element by index when multiple matches exist"""
    
    def __len__(self) -> int:
        """Get count of matching elements"""
    
    def __iter__(self):
        """Iterate over all matching elements"""
    
    @property
    def count(self) -> int:
        """Get count of matching elements"""

Usage examples:

d = u2.connect()

# Work with multiple matching elements
buttons = d(className="android.widget.Button")
print(f"Found {len(buttons)} buttons")  # Get count
print(f"Found {buttons.count} buttons")  # Alternative count

# Access specific element by index
first_button = buttons[0]  # First button
last_button = buttons[-1]  # Last button
first_button.click()

# Iterate over all matching elements
for i, button in enumerate(buttons):
    print(f"Button {i}: {button.get_text()}")
    if button.get_text() == "Submit":
        button.click()
        break

Install with Tessl CLI

npx tessl i tessl/pypi-uiautomator2@3.2.1

docs

app-management.md

device-management.md

image-processing.md

index.md

screen-input.md

ui-interaction.md

watchers-automation.md

xpath-selection.md

tile.json