"""Utility helpers for retrieving UI hierarchy dumps from connected devices."""
from __future__ import annotations
import logging
import os
import time
from typing import Optional
from appium import webdriver
from appium.options.android.uiautomator2.base import UiAutomator2Options
logger = logging.getLogger(__name__)
def get_ui_dump(
device_name: str = "emulator-5554",
output_dir: str = "output",
appium_server_url: str = "http://localhost:4723/wd/hub",
settle_seconds: int = 5,
) -> Optional[str]:
"""Retrieve a UiAutomator hierarchy dump and persist it as XML."""
options = UiAutomator2Options()
options.device_name = device_name
options.platform_name = "Android"
options.no_reset = True
logger.info("Connecting to Appium server at %s", appium_server_url)
try:
driver = webdriver.Remote(appium_server_url, options=options)
except Exception as exc: # noqa: BLE001 - Appium raises many exception types
logger.error("Error connecting to Appium server: %s", exc, exc_info=True)
return None
logger.debug("Waiting %s seconds for device to settle", settle_seconds)
time.sleep(settle_seconds)
logger.info("Retrieving UiAutomator dump")
try:
ui_dump = driver.execute_script(
"mobile: uiautomator",
{
"strategy": "uiautomator",
"action": "dumpWindowHierarchy",
"args": {},
},
)
except Exception as exc: # noqa: BLE001
logger.error("Error executing UiAutomator dump: %s", exc, exc_info=True)
driver.quit()
return None
if not ui_dump:
logger.warning("UiAutomator dump was not available.")
driver.quit()
return None
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, "ui-automator-dump.xml")
try:
with open(output_path, "w", encoding="utf-8") as handle:
handle.write(ui_dump)
logger.info("UiAutomator dump saved to %s", output_path)
except Exception as exc: # noqa: BLE001
logger.error("Error writing UI dump to file: %s", exc, exc_info=True)
driver.quit()
return None
finally:
driver.quit()
return output_path
__all__ = ["get_ui_dump"]