diff --git a/node_cli.py b/node_cli.py index d4e1ce89..65fdcd85 100755 --- a/node_cli.py +++ b/node_cli.py @@ -38,7 +38,7 @@ from Framework.install_handler.android.java import update_java_path from settings import ZEUZ_NODE_PRIVATE_RSA_KEYS_DIR from Framework.install_handler.long_poll_handler import InstallHandler -from server.mobile import upload_android_ui_dump, upload_ios_ui_dump +from server.mobile import start_ui_dump_uploads from Framework.install_handler.android.android_sdk import update_android_sdk_path def adjust_python_path(): @@ -1347,8 +1347,7 @@ async def main(): update_android_sdk_path() update_outdated_modules() asyncio.create_task(start_server()) - asyncio.create_task(upload_android_ui_dump()) - asyncio.create_task(upload_ios_ui_dump()) + start_ui_dump_uploads() asyncio.create_task(delete_old_automationlog_folders()) await destroy_session() diff --git a/server/main.py b/server/main.py index 8098c5ae..3e295b8d 100644 --- a/server/main.py +++ b/server/main.py @@ -8,7 +8,7 @@ from server.connect import router as connect_router from server.evaluator import router as evaluator_router from server.node_operator import router as operator_router -from server.mobile import router as mobile_router, upload_android_ui_dump +from server.mobile import router as mobile_router, start_ui_dump_uploads from server.mac import router as mac_router from server.linux import router as linux_router from server.installers import router as installers_router @@ -45,6 +45,10 @@ def main() -> FastAPI: v1router.include_router(linux_router) v1router.include_router(installers_router) app = FastAPI() + + @app.on_event("startup") + async def _start_background_uploads(): + start_ui_dump_uploads() app.include_router(v1router) origins = [ diff --git a/server/mobile.py b/server/mobile.py index 68154b45..75aa2a6d 100644 --- a/server/mobile.py +++ b/server/mobile.py @@ -29,6 +29,18 @@ router = APIRouter(prefix="/mobile", tags=["mobile"]) +_UPLOAD_TASKS_STARTED = False + + +def start_ui_dump_uploads() -> None: + """Start background UI dump uploads once per process.""" + global _UPLOAD_TASKS_STARTED + if _UPLOAD_TASKS_STARTED: + return + _UPLOAD_TASKS_STARTED = True + asyncio.create_task(upload_android_ui_dump()) + asyncio.create_task(upload_ios_ui_dump()) + def is_wda_running(port: int) -> bool: """Check if WebDriverAgent is running on given port.""" @@ -332,26 +344,35 @@ def run_adb_command(command): def capture_ui_dump(device_serial: str | None = None): """Capture the current UI hierarchy from the device""" + # Try to get from active Appium driver first (like web does) + try: + from Framework.Built_In_Automation.Mobile.CrossPlatform.Appium.BuiltInFunctions import appium_driver + + if appium_driver is not None: + page_src = appium_driver.page_source + with open(UI_XML_PATH, "w") as xml_file: + xml_file.write(page_src) + return + except Exception as e: + pass + + # Fallback to ADB device_flag = f"-s {device_serial}" if device_serial else "" + + if os.path.exists(UI_XML_PATH): + os.remove(UI_XML_PATH) + out = run_adb_command( f"{ADB_PATH} {device_flag} shell uiautomator dump /sdcard/ui.xml".strip() ) if out.startswith("Error:"): - from Framework.Built_In_Automation.Mobile.CrossPlatform.Appium.BuiltInFunctions import ( - appium_driver, - ) - - if appium_driver is None: - return - page_src = appium_driver.page_source - with open(UI_XML_PATH, "w") as xml_file: - xml_file.write(page_src) - else: - out = run_adb_command( - f"{ADB_PATH} {device_flag} pull /sdcard/ui.xml {UI_XML_PATH}" - ) - if out.startswith("Error:"): - return + return + + out = run_adb_command( + f"{ADB_PATH} {device_flag} pull /sdcard/ui.xml {UI_XML_PATH}" + ) + if os.path.exists(UI_XML_PATH): + size = os.path.getsize(UI_XML_PATH) def capture_screenshot(device_serial: str | None = None): @@ -444,6 +465,19 @@ def get_real_ios_hierarchy(device_udid: str): def capture_ios_ui_dump(device_udid: str): + # Try to get from active Appium driver first (like web does) + try: + from Framework.Built_In_Automation.Mobile.CrossPlatform.Appium.BuiltInFunctions import appium_driver + + if appium_driver is not None: + page_src = appium_driver.page_source + with open(IOS_XML_PATH, 'w', encoding='utf-8') as xml_file: + xml_file.write(page_src) + return + except Exception as e: + pass + + # Fallback to WDA real_hierarchy = get_real_ios_hierarchy(device_udid) if real_hierarchy: try: @@ -456,17 +490,6 @@ def capture_ios_ui_dump(device_udid: str): with open(IOS_XML_PATH, 'w', encoding='utf-8') as xml_file: xml_file.write(xml_content) return - - # Fallback to Appium driver - try: - from Framework.Built_In_Automation.Mobile.CrossPlatform.Appium.BuiltInFunctions import appium_driver - if appium_driver is not None: - page_src = appium_driver.page_source - with open(IOS_XML_PATH, 'w', encoding='utf-8') as xml_file: - xml_file.write(page_src) - return - except: - pass async def upload_android_ui_dump(): @@ -778,4 +801,4 @@ def is_ios_app_installed(sim_udid: str, bundle_id: str): return {"installed": False} except Exception as e: - return {"installed": False, "error": str(e)} \ No newline at end of file + return {"installed": False, "error": str(e)}