#pragma once #include #include class AdbUtils final : public QObject { Q_OBJECT public: explicit AdbUtils(QObject *parent = nullptr); static QString adbPath(); static QList > getListDevices(); static QString tryToGetRunningAppPid(const QString &deviceId, const QString &packageName); static bool tryToStartApplication(const QString &deviceId, const QString &packageName); static bool tryToKillApplication(const QString &deviceId, const QString &packageName); static QString getScreenDumpAsXml(const QString &deviceId, int idx, bool screenshot = false); // Сырой дамп без фильтра "useful UI" — для ручного превью оператора: // отдаёт что есть на экране, даже если это PIN-пад / сплэш / WebView-shell. static QString getRawScreenDumpAsXml(const QString &deviceId); static bool takeScreenshot(const QString &deviceId, const QString &name); // IP телефона в Wi-Fi (кэш по serial с TTL). Для IP:port-серийника берётся из // серийника, иначе `adb shell ip route get 1.1.1.1` → src. Пусто, если не в Wi-Fi. // Используется и для http-скриншота, и для сохранения IP в БД (wifi_connections). static QString resolvePhoneIp(const QString &deviceId); // Текущий режим скриншотов устройства: "http" | "exec-out (fallback)" | "" (ещё не // определялся). Для индикации в UI. Ключ — adb-серийник. static QString screenshotMode(const QString &deviceId); // Захватывает скриншот напрямую в память (PNG-байты), без временного файла. // Выбирает стратегию по config.ini [screenshot] transport: http (через APK // arc-shot, мимо adb) с fallback на exec-out, либо сразу exec-out. static QByteArray captureScreenshotBytes(const QString &deviceId); // Скриншот через APK arc-shot: adb screencap в файл на /sdcard (дёшево, без // передачи байтов через adb) + HTTP-GET файла напрямую с телефона по его // Wi-Fi IP. Разгружает adb. Пусто при недоступности (нет APK/сети) — вызывающий // откатывается на exec-out. Возвращает PNG-байты. static QByteArray captureScreenshotHttp(const QString &deviceId); static bool takeXmlDump(const QString &deviceId, const QString &name); static bool makeTap(const QString &deviceId, int x, int y); static bool enableAdbIMEKeyboard(const QString &deviceId); static bool inputAdbIMEText(const QString &deviceId, const QString &text); static bool disableAdbIMEKeyboard(const QString &deviceId); static bool makeSwipe(const QString &deviceId, int x1, int y1, int x2, int y2); static bool makeSwipe(const QString &deviceId, int x1, int y1, int x2, int y2, int durationMs); static bool inputText(const QString &deviceId, const QString &text); static bool hideKeyboard(const QString &deviceId); static bool goBack(const QString &deviceId); static void unlockScreen(const QString &deviceId, int screenWidth, int screenHeight); static QSet getListApps(const QString &deviceId); // Возвращает таймзону устройства (напр. "Asia/Bangkok"), либо пустую строку при ошибке static QString getDeviceTimezone(const QString &deviceId); // Возвращает versionName установленного пакета (напр. "19.17.0"), либо пустую строку при ошибке/отсутствии static QString getAppVersion(const QString &deviceId, const QString &packageName); // Возвращает пакет приложения на переднем плане (foreground), либо пустую строку при ошибке static QString getForegroundPackage(const QString &deviceId); // Проверяет реальную ориентацию экрана (dumpsys input → SurfaceOrientation). // Если экран в горизонтальной ориентации (landscape, 90°/270°), принудительно // ставит вертикальную: выключает автоповорот и выставляет user_rotation=0. // Best-effort: при ошибке adb просто ничего не делает. Возвращает true, если // экран в портрете (изначально или после форсирования). static bool ensurePortraitOrientation(const QString &deviceId); // --- ADB через Wi-Fi (TCP/IP) --- // Эвристика: serial выглядит как сетевой endpoint "ip:port" (Wi-Fi-подключение), // а не USB-серийник (hex) или emulator-XXXX. static bool isWirelessSerial(const QString &serial); // Возвращает IPv4-адрес устройства в сети Wi-Fi (интерфейс wlan0), либо пустую // строку, если устройство не в Wi-Fi / адрес не прочитался. static QString getDeviceWifiIp(const QString &serial); // Возвращает MAC-адрес интерфейса wlan0 в нормализованном виде (lower-case, // разделитель ':'), либо пустую строку. Нужен, чтобы находить устройство в сети // после смены IP по DHCP (поиск по ARP-таблице). static QString getDeviceMac(const QString &serial); // Переводит adbd устройства в TCP-режим на указанном порту (adb -s tcpip ). // Вызывается на устройстве, подключённом по USB. ВНИМАНИЕ: перезапускает adbd и // кратковременно рвёт текущее соединение — запускать только на простаивающем устройстве. static bool enableTcpip(const QString &serial, int port); // adb connect (endpoint вида "192.168.1.50:5555"). // Возвращает true при "connected"/"already connected". Exit-code adb недостаточен — // adb connect возвращает 0 даже при "failed to connect", поэтому разбираем stdout. static bool connectWireless(const QString &endpoint); // adb disconnect . static bool disconnectWireless(const QString &endpoint); private: static QString adbDir(); static bool startProcess(const QString &program, const QStringList &args, QString *stdOut = nullptr, QString *stdErr = nullptr); // Старый способ: exec-out screencap -p (стримит PNG через adb). Fallback для http. static QByteArray captureScreenshotExecOut(const QString &deviceId); // Синхронный HTTP/1.0 GET по QTcpSocket (без event-loop, безопасно из worker-потоков). // Возвращает тело ответа при 200, иначе пусто. static QByteArray httpGet(const QString &ip, int port, const QString &path, int timeoutMs); // Поднимает foreground-сервис arc-shot (ставит APK, если удалён). Дёргается из // http-fail ветки, т.е. не чаще раза в бэкофф-окно. static void kickArcShot(const QString &deviceId); };