From 7cba3b141e0715ab808d30468d7c4931bc223119 Mon Sep 17 00:00:00 2001 From: AmirS Date: Sun, 27 Apr 2025 18:35:22 +0400 Subject: [PATCH] RAZRABOTKA-6 task completed --- automation_script/AdbUtils.cpp | 38 +-- automation_script/Script.cpp | 417 +++++++++++++++++--------------- automation_script/Script.h | 85 ++++--- automation_script/XmlParser.cpp | 49 ++++ automation_script/XmlParser.h | 3 +- main.cpp | 2 +- 6 files changed, 338 insertions(+), 256 deletions(-) diff --git a/automation_script/AdbUtils.cpp b/automation_script/AdbUtils.cpp index edd71c5..ebf0c65 100644 --- a/automation_script/AdbUtils.cpp +++ b/automation_script/AdbUtils.cpp @@ -9,31 +9,35 @@ AdbUtils::AdbUtils(QObject *parent) : QObject(parent) { bool AdbUtils::checkDevices() { QProcess process; - process.start("adb devices"); + process.start("adb", QStringList() << "devices"); process.waitForFinished(); - QString output = process.readAllStandardOutput(); - return output.contains("device"); + const QString output = process.readAllStandardOutput(); + qDebug() << "ADB devices output:" << output; + + // Проверка на наличие строки "\tdevice" в выводе + return output.contains("\tdevice"); } bool AdbUtils::startApp(const QString &packageName) { QProcess process; - QString command = QString("adb shell monkey -p %1 -c android.intent.category.LAUNCHER 1").arg(packageName); - process.start(command); + process.start("adb", QStringList() << "shell" << "monkey" << "-p" << packageName << "-c" << "android.intent.category.LAUNCHER" << "1"); process.waitForFinished(); + const QString output = process.readAllStandardOutput(); + qDebug() << "Start app output:" << output; + return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0; } bool AdbUtils::dumpAndPullUiXml(const QString &localPath) { QProcess process; - process.start("adb shell uiautomator dump /sdcard/view.xml"); + process.start("adb", QStringList() << "shell" << "uiautomator" << "dump" << "/sdcard/ui_dump.xml"); process.waitForFinished(); if (process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) { qDebug() << "Ошибка в получении дампа UI на устройстве"; return false; } - QString pullCmd = QString("adb pull /sdcard/view.xml %1").arg(localPath); - process.start(pullCmd); + process.start("adb", QStringList() << "pull" << "/sdcard/ui_dump.xml" << localPath); process.waitForFinished(); if (process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) { qDebug() << "Ошибка в передаче xml файла на компьютер"; @@ -45,33 +49,35 @@ bool AdbUtils::dumpAndPullUiXml(const QString &localPath) { bool AdbUtils::tap(int x, int y) { QProcess process; - QString cmd = QString("adb shell input tap %1 %2").arg(x).arg(y); - process.start(cmd); + process.start("adb", QStringList() << "shell" << "input" << "tap" << QString::number(x) << QString::number(y)); process.waitForFinished(); + const QString output = process.readAllStandardOutput(); + qDebug() << "Tap output:" << output; return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0; } bool AdbUtils::swipe(const int x1, const int y1, const int x2, const int y2) { QProcess process; - QString cmd = QString("adb shell input swipe %1 %2 %3 %4").arg(x1).arg(y1).arg(x2).arg(y2); - process.start(cmd); + process.start("adb", QStringList() << "shell" << "input" << "swipe" << QString::number(x1) << QString::number(y1) << QString::number(x2) << QString::number(y2)); process.waitForFinished(); + const QString output = process.readAllStandardOutput(); + qDebug() << "Swipe output:" << output; return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0; } bool AdbUtils::inputText(const QString &text) { QProcess process; - QString cmd = QString("adb shell input text \"%1\"").arg(text); - process.start(cmd); + process.start("adb", QStringList() << "shell" << "input" << "text" << text); process.waitForFinished(); + const QString output = process.readAllStandardOutput(); + qDebug() << "Input text output:" << output; return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0; } // bool AdbUtils::takeScreenshot(const QString& filename) { // QProcess process; // QDir().mkdir("screens"); // Создаем папку, если её нет -// QString cmd = QString("adb exec-out screencap -p > screens/%1").arg(filename); -// process.start(cmd); +// process.start("adb", QStringList() << "exec-out" << "screencap" << "-p" << ">" << "screens/" + filename); // process.waitForFinished(); // return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0; // } diff --git a/automation_script/Script.cpp b/automation_script/Script.cpp index acddeb7..a1a470f 100644 --- a/automation_script/Script.cpp +++ b/automation_script/Script.cpp @@ -7,15 +7,6 @@ #include "XmlParser.h" #include "QDebug" #include -#include -#include -#include - -// Вспомогательные функции - -void wait(const int milliseconds) { - std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); -} Script::Script(QObject *parent) : QObject(parent) { } @@ -23,12 +14,25 @@ Script::Script(QObject *parent) : QObject(parent) { Script::~Script() = default; void Script::start() { - qDebug("Запуск скрипта..."); + qDebug() << "Worker started in thread:" << QThread::currentThread(); processUiLoop(); } -void Script::processUiLoop() { +[[noreturn]] void Script::processUiLoop() { + qDebug() << QString::fromUtf8("Запуск скрипта для ru.rshb.dbo"); + + if (!AdbUtils::checkDevices()) { + qDebug() << QString::fromUtf8("ADB: Нет устройств"); + return; + } + + if (!AdbUtils::startApp("ru.rshb.dbo")) { + qDebug("Не удалось запустить приложение"); + return; + } + + QThread::msleep(4500); while (true) { QString uiDumpPath = "ui_dump.xml"; AdbUtils::dumpAndPullUiXml(uiDumpPath); @@ -36,6 +40,11 @@ void Script::processUiLoop() { QList elements = XmlParser::parseUiDumpUsingQXml(uiDumpPath); switch (currentState) { + case State::HandlingSecurityBanner: + handleSecurityBanner(elements); + // Переход в состояние ожидания ПИН-кода после закрытия баннера + currentState = State::WaitingForPinInput; + case State::WaitingForPinInput: if (XmlParser::isPinCodeScreen(elements)) { qDebug() << "Экран ввода ПИН-кода обнаружен"; @@ -58,30 +67,34 @@ void Script::processUiLoop() { case State::SearchingCard: qDebug() << "State: SearchingCard -> Поиск блока карты"; - // Добавить сюда поиск карты по последним 4 цифрам, известным для выбора карты - currentState = State::OnCardDetails; + // Добавить сюда поиск карты по последним 4 цифрам, известным заранее, для выбора карты + // currentState = State::OnCardDetails; + currentState = State::Idle; break; case State::OnCardDetails: - // Обработка экрана перевода - handleTransferScreen(elements); + // Обработка экрана деталей банковской карты + handleCardDetailsScreen(elements); break; case State::OnTransferScreen: qDebug() << "State: OnTransferScreen -> Перевод выполнен успешно."; + // handleTransferScreen(elements); currentState = State::Idle; break; case State::Idle: default: - qDebug() << "State: Idle -> Waiting for action"; - if (XmlParser::isPinCodeScreen(elements)) { + qDebug() << "State: Idle -> Жду следующего действия"; + if (XmlParser::isSecurityBanner(elements)) { + currentState = State::HandlingSecurityBanner; + } else if (XmlParser::isPinCodeScreen(elements)) { currentState = State::WaitingForPinInput; } break; } - QThread::sleep(1); // Пауза между проверками экрана + QThread::msleep(1000); // Пауза между проверками экрана } } @@ -197,13 +210,38 @@ void Script::processUiLoop() { // wait(1000); // } +// Вспомогательные функции + +// Функция для нажатия по элементу, передавая элемент +void tapElement(const UiElement &el) { + const int x = (el.x1() + el.x2()) / 2; + const int y = (el.y1() + el.y2()) / 2; + // Тапаем по кнопке + AdbUtils::tap(x, y); +} + // Функции для определения экранов и действий +void Script::handleSecurityBanner(const QList& elements) { + if (XmlParser::isSecurityBanner(elements)) { + qDebug() << "State: HandlingSecurityBanner -> Найден баннер о безопасности."; + + // Ищем кликабельную кнопку "Отложить" + for (const UiElement& el : elements) { + if (el.text() == "Отложить" && el.isClickable() && el.className() == "android.widget.Button") { + qDebug() << "Найдена кнопка 'Отложить'. Нажимаем её!"; + tapElement(el); // Нажимаем на кнопку "Отложить" + break; + } + } + } +} + void Script::inputPinCode(const QString &pinCode) { // Перебираем каждый символ из ПИН-кода for (int i = 0; i < pinCode.length(); ++i) { QString digit = pinCode.mid(i, 1); - qDebug() << "Finding button for digit: " << digit; + qDebug() << "Найдена кнопка для символа: " << digit; // Поиск кнопки с соответствующим текстом QList elements = XmlParser::parseUiDumpUsingQXml("ui_dump.xml"); @@ -212,9 +250,7 @@ void Script::inputPinCode(const QString &pinCode) { bool found = false; for (const UiElement &el: elements) { if (el.text() == digit && el.isClickable()) { - int x = (el.x1() + el.x2()) / 2; - int y = (el.y1() + el.y2()) / 2; - AdbUtils::tap(x, y); // Тапаем по кнопке + tapElement(el); found = true; break; } @@ -222,46 +258,38 @@ void Script::inputPinCode(const QString &pinCode) { // Если не нашли кнопку для цифры, выводим предупреждение if (!found) { - qWarning() << "Digit button for " << digit << " not found!"; + qWarning() << "Цифровая кнопка для " << digit << " не найдена!"; } // Пауза между нажатиями, чтобы избежать слишком быстрого ввода - QThread::sleep(1); + QThread::msleep(200); } } -void Script::handleClosingBanner(QList &elements) { +void Script::handleClosingBanner(const QList &elements) { while (true) { // Проверка на наличие баннера или диалога if (XmlParser::isBannerOrDialog(elements)) { - qDebug() << "State: ClosingBanner -> Found banner/dialog. Closing it."; + qDebug() << "State: ClosingBanner -> Найден баннер/диалог. Закрываем его."; - // Найдем кнопку "Закрыть" или кнопку без текста в правом верхнем углу + // Флаг для отслеживания, если мы нашли хотя бы одно окно для закрытия bool closed = false; for (const UiElement &el: elements) { if (el.isClickable()) { - // Флаг для отслеживания, если мы нашли хотя бы одно окно для закрытия - bool bannerClosed = false; // Пробуем закрыть баннер, если есть кнопка "Закрыть" if ((el.text() == "Закрыть" || el.text() == "Позже") && el.className() == "android.widget.Button") { - int x = (el.x1() + el.x2()) / 2; - int y = (el.y1() + el.y2()) / 2; - AdbUtils::tap(x, y); // Тапаем по кнопке "Закрыть" + tapElement(el); closed = true; - bannerClosed = true; - qDebug() << "Banner/dialog closed!"; + qDebug() << "Баннер/диалог закрыт!"; break; } - // Если кнопка без текста и в правой верхней части + // Если кнопка без текста и в правой верхней части (проверяем по координатам приблизительно) if (el.text().isEmpty() && el.className() == "android.widget.Button" && - el.x1() > 900 && el.y1() < 100) { - int x = (el.x1() + el.x2()) / 2; - int y = (el.y1() + el.y2()) / 2; - AdbUtils::tap(x, y); // Тапаем по кнопке в правом верхнем углу + el.x1() > 800 && el.y1() < 200) { + tapElement(el); closed = true; - bannerClosed = true; - qDebug() << "Banner/dialog closed by tapping button in the top right corner."; + qDebug() << "Баннер/диалог закрыт по нажатию кнопки в правом верхнем углу."; break; } } @@ -273,54 +301,17 @@ void Script::handleClosingBanner(QList &elements) { } // Пауза перед следующим закрытием окна - QThread::sleep(1); + QThread::msleep(1000); } else { - qDebug() << "State: ClosingBanner -> No banners/dialogs left to close."; - break; // Заканчиваем цикл, если баннеров/диалогов больше нет + qDebug() << "State: ClosingBanner -> Не осталось баннеров/диалогов для закрытия."; + // Заканчиваем цикл, если баннеров/диалогов больше нет + break; } } // Переход к следующему состоянию - currentState = State::OnMainScreen; // Если все модальные окна закрыты, переходим к основному экрану -} - -void Script::inputAmount(const QString &amount) { - // Разделяем сумму на отдельные цифры - for (int i = 0; i < amount.length(); ++i) { - QString digit = amount.mid(i, 1); - qDebug() << "Inputting digit: " << digit; - - // Поиск кнопки с соответствующей цифрой - QList elements = XmlParser::parseUiDumpUsingQXml("ui_dump.xml"); - - bool found = false; - for (const UiElement &el: elements) { - if (el.text() == digit && el.isClickable()) { - int x = (el.x1() + el.x2()) / 2; - int y = (el.y1() + el.y2()) / 2; - AdbUtils::tap(x, y); // Тапаем по кнопке цифры - found = true; - break; - } - } - - if (!found) { - qWarning() << "Digit button for " << digit << " not found!"; - } - - QThread::sleep(1); // Пауза между нажатиями - } - - // Если нужно, нажимаем кнопку для завершения ввода суммы - QList elements = XmlParser::parseUiDumpUsingQXml("ui_dump.xml"); - for (const UiElement &el: elements) { - if (el.text() == "ОК" && el.isClickable()) { - int x = (el.x1() + el.x2()) / 2; - int y = (el.y1() + el.y2()) / 2; - AdbUtils::tap(x, y); // Тапаем по кнопке "ОК" - break; - } - } + // Если все модальные окна закрыты, переходим к основному экрану + currentState = State::OnMainScreen; } void Script::swipeToFindElement(const QString &searchText) { @@ -331,152 +322,182 @@ void Script::swipeToFindElement(const QString &searchText) { while (attempts < 5 && !found) { QList elements = XmlParser::parseUiDumpUsingQXml("ui_dump.xml"); - // Ищем нужный элемент на экране + // Ищем нужный элемент на экране и если он найден - нажимаем на него for (const UiElement &el: elements) { - if (el.text() == searchText && el.isClickable()) { - qDebug() << "Element found: " << searchText; - int x = (el.x1() + el.x2()) / 2; - int y = (el.y1() + el.y2()) / 2; - AdbUtils::tap(x, y); // Тапаем по найденному элементу + if (el.text() == searchText && el.isClickable() && el.className() == "android.widget.Button") { + qDebug() << "Найден элемент: " << searchText; + tapElement(el); found = true; break; } } - // Если элемент не найден, выполняем свайп + // Если элемент не найден, выполняем свайп, прибавляем попытку и ищем снова if (!found) { - qDebug() << "Element not found, swiping..."; - AdbUtils::swipe(500, 1500, 500, 500); // Свайп вверх + qDebug() << "Элемент не найден, свайпаем..."; + // Свайп вверх + AdbUtils::swipe(500, 1500, 500, 500); attempts++; - QThread::sleep(2); // Пауза между свайпами + // Пауза между свайпами + QThread::msleep(1000); } } if (!found) { - qWarning() << "Element " << searchText << " not found after " << attempts << " swipe attempts."; + qWarning() << "Элемент " << searchText << " не найден после " << attempts << " попыток свайпа."; } } -void Script::handleTransferScreen(QList &elements) { - // Проверка на экран перевода - if (XmlParser::isTransferScreen(elements)) { - qDebug() << "State: OnTransferScreen -> Looking for 'Enter amount' field or 'Confirm' button."; +void Script::handleCardDetailsScreen(const QList& elements) { + // Проверяем, что мы на экране детали карты + if (XmlParser::isDetailCardScreen(elements)) { + qDebug() << "State: OnCardDetails -> Найден экран детализированной карты."; + + // Ищем кликабельную кнопку "Оплатить" + bool foundPaymentButton = false; + for (const UiElement& el : elements) { + if (el.text() == "Оплатить" && el.isClickable()) { + qDebug() << "Найдена кнопка 'Оплатить'. Нажимаем её!"; + // Нажимаем на кнопку "Оплатить" + tapElement(el); + foundPaymentButton = true; + break; + } + } + + if (!foundPaymentButton) { + qWarning() << "Payment button 'Оплатить' not found on the card details screen."; + } + } +} + +// void Script::handleTransferScreen(const QList &elements) { +// // Проверка на экран перевода +// if (XmlParser::isTransferScreen(elements)) { +// qDebug() << "State: OnTransferScreen -> Looking for 'Enter amount' field or 'Confirm' button."; +// +// bool found = false; +// +// // Если поле для ввода суммы найдено, вводим сумму +// for (const UiElement &el: elements) { +// if (el.text() == "Сумма" && el.isClickable()) { +// qDebug() << "Found 'Amount' input field"; +// inputAmount("1000"); // Пример суммы +// found = true; +// break; +// } +// } +// +// if (!found) { +// qDebug() << "Поле ввода 'Сумма' не найдено!"; +// } +// +// // После ввода суммы ищем кнопку 'Оплатить' или 'Подтвердить' +// for (const UiElement &el: elements) { +// if ((el.text() == "Оплатить" || el.text() == "Подтвердить") && el.isClickable()) { +// tapElement(el); +// qDebug() << "Payment confirmed."; +// break; +// } +// } +// } +// } + +void Script::inputAmount(const QString &amount) { + // Разделяем сумму на отдельные цифры + for (int i = 0; i < amount.length(); ++i) { + QString digit = amount.mid(i, 1); + qDebug() << "Вводим цифру: " << digit; + + // Поиск кнопки с соответствующей цифрой + QList elements = XmlParser::parseUiDumpUsingQXml("ui_dump.xml"); bool found = false; - - // Если поле для ввода суммы найдено, вводим сумму for (const UiElement &el: elements) { - if (el.text() == "Сумма" && el.isClickable()) { - qDebug() << "Found 'Amount' input field"; - inputAmount("1000"); // Пример суммы + if (el.text() == digit && el.isClickable()) { + tapElement(el); found = true; break; } } if (!found) { - qWarning() << "'Amount' input field not found!"; + qWarning() << "Цифровая кнопка для " << digit << " не найдена!"; } - // После ввода суммы ищем кнопку 'Оплатить' или 'Подтвердить' - for (const UiElement &el: elements) { - if (el.text() == "Оплатить" && el.isClickable()) { - int x = (el.x1() + el.x2()) / 2; - int y = (el.y1() + el.y2()) / 2; - AdbUtils::tap(x, y); // Тапаем по кнопке "Оплатить" - qDebug() << "Payment confirmed."; + QThread::msleep(1000); // Пауза между нажатиями + } + + // Если нужно, нажимаем кнопку для завершения ввода суммы + QList elements = XmlParser::parseUiDumpUsingQXml("ui_dump.xml"); + for (const UiElement &el: elements) { + if (el.isClickable()) { + if (el.text() == "ОК" || el.text() == "Подтвердить" || el.text() == "Готово") { + tapElement(el); break; } } } } -void tapByText(const std::vector& ui, const std::string& text) { - for (const auto& el : ui) { - if (el.text == QString::fromStdString(text)) { - tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2); - break; - } - } -} - -// Тап по полю ввода -void tapInputField(const std::vector& ui, const std::string& fieldText) { - for (const auto& el : ui) { - if (el.text == QString::fromStdString(fieldText)) { - tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2); - break; - } - } -} - -// Тап по кнопке "Далее" -void tapNextButton(const std::vector& ui) { - for (const auto& el : ui) { - if (el.text == "Войти" || el.text == "Далее" || el.text == "Продолжить") { - tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2); - break; - } - } -} - -bool handlePostLoginScreen(const std::vector& ui) { - for (const auto& el : ui) { - // Ищем баннеры и модальные окна, которые нужно закрыть - if (el.text == "Получайте новости с помощью" || el.text == "Не пропускайте специальные предложения и рекомендации") { - // Тапаем по пустой области - tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2); - wait(900); - return true; - } - if (el.text == "Позже" || el.text == "Окей" || el.text == "Закрыть") { - // Тапаем по кнопке закрытия или кнопке "Позже" - tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2); - wait(900); - return true; - } - } - return false; -} - -bool isMainScreen(const std::vector& ui) { - for (const auto& el : ui) { - if (el.text == "Счета и карты" || el.text == "Доступно баллов") { - return true; - } - } - return false; -} - -UiElement findCardByLast4Digits(const std::vector& ui, const std::string& last4digits) { - // Проходим по всем элементам UI, которые парсились из XML - for (const auto& el : ui) { - // Используем регулярное выражение для поиска 4 цифр в тексте - std::smatch m; - std::string str = el.text.toUtf8().constData(); // Преобразуем QString в std::string - - // Если в тексте найдено совпадение с регулярным выражением для 4 цифр - if (std::regex_search(str, m, std::regex(R"(\*\*\s?(\d{4}))"))) { - // Проверяем, совпадают ли последние 4 цифры с переданными - if (m[1] == last4digits) { - // Если нашли совпадение, возвращаем элемент - return el; - } - } - } - - // Если карта с такими последними цифрами не найдена, возвращаем пустой элемент - return {}; -} - -UiElement findPayButton(const std::vector& ui) { - for (const auto& el : ui) { - if (el.text == "Оплатить") { - return el; - } - } - return {}; -} +// void tapByText(const std::vector &ui, const std::string &text) { +// for (const auto &el: ui) { +// if (el.text == QString::fromStdString(text)) { +// tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2); +// break; +// } +// } +// } +// +// // Тап по полю ввода +// void tapInputField(const std::vector &ui, const std::string &fieldText) { +// for (const auto &el: ui) { +// if (el.text == QString::fromStdString(fieldText)) { +// tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2); +// break; +// } +// } +// } +// +// // Тап по кнопке "Далее" +// void tapNextButton(const std::vector &ui) { +// for (const auto &el: ui) { +// if (el.text == "Войти" || el.text == "Далее" || el.text == "Продолжить") { +// tap((el.x1 + el.x2) / 2, (el.y1 + el.y2) / 2); +// break; +// } +// } +// } +// +// UiElement findCardByLast4Digits(const std::vector &ui, const std::string &last4digits) { +// // Проходим по всем элементам UI, которые парсились из XML +// for (const auto &el: ui) { +// // Используем регулярное выражение для поиска 4 цифр в тексте +// std::smatch m; +// std::string str = el.text.toUtf8().constData(); // Преобразуем QString в std::string +// +// // Если в тексте найдено совпадение с регулярным выражением для 4 цифр +// if (std::regex_search(str, m, std::regex(R"(\*\*\s?(\d{4}))"))) { +// // Проверяем, совпадают ли последние 4 цифры с переданными +// if (m[1] == last4digits) { +// // Если нашли совпадение, возвращаем элемент +// return el; +// } +// } +// } +// +// // Если карта с такими последними цифрами не найдена, возвращаем пустой элемент +// return {}; +// } +// +// UiElement findPayButton(const std::vector &ui) { +// for (const auto &el: ui) { +// if (el.text == "Оплатить") { +// return el; +// } +// } +// return {}; +// } void Script::stop() { m_running = false; diff --git a/automation_script/Script.h b/automation_script/Script.h index 172d946..0f55ec8 100644 --- a/automation_script/Script.h +++ b/automation_script/Script.h @@ -31,46 +31,47 @@ signals: private: bool m_running = true; - // Функция для запуска скрипта с передачей параметров - void runScript(const QString &pin, const QString &last4digits); - - // Функции для определения различных экранов и действий - - // Проверка, что перед нами экран логина - bool isLoginScreen(const QList &ui); - - // Проверка, что перед нами экран ввода ПИН - bool isPinScreen(const QList &ui); - - // Получение списка кнопок с цифрами для ввода ПИН - std::vector getPinButtons(const QList &ui); - - // Тап по элементу с текстом - void tapByText(const QList &ui, const QString &text); - - // Тап по полю ввода - void tapInputField(const QList &ui, const QString &fieldText); - - // Тап по кнопке "Далее" и её вариациям - void tapNextButton(const QList &ui); - - // Обработка экрана с пост-логин баннерами или диалогами - bool handlePostLoginScreen(const QList &ui); - - // Проверка, что перед нами главный экран - bool isMainScreen(const QList &ui); - - // Тап по главному экрану - void tapMainScreen(const QList &ui); - - // Функция для поиска карты по последним 4 цифрам - UiElement findCardByLast4Digits(const QList &ui, const QString &last4digits); - - // Поиск кнопки "Оплатить" на экране - UiElement findPayButton(const QList &ui); + // // Функция для запуска скрипта с передачей параметров + // void runScript(const QString &pin, const QString &last4digits); + // + // // Функции для определения различных экранов и действий + // + // // Проверка, что перед нами экран логина + // bool isLoginScreen(const QList &ui); + // + // // Проверка, что перед нами экран ввода ПИН + // bool isPinScreen(const QList &ui); + // + // // Получение списка кнопок с цифрами для ввода ПИН + // std::vector getPinButtons(const QList &ui); + // + // // Тап по элементу с текстом + // void tapByText(const QList &ui, const QString &text); + // + // // Тап по полю ввода + // void tapInputField(const QList &ui, const QString &fieldText); + // + // // Тап по кнопке "Далее" и её вариациям + // void tapNextButton(const QList &ui); + // + // // Обработка экрана с пост-логин баннерами или диалогами + // bool handlePostLoginScreen(const QList &ui); + // + // // Проверка, что перед нами главный экран + // bool isMainScreen(const QList &ui); + // + // // Тап по главному экрану + // void tapMainScreen(const QList &ui); + // + // // Функция для поиска карты по последним 4 цифрам + // UiElement findCardByLast4Digits(const QList &ui, const QString &last4digits); + // + // // Поиск кнопки "Оплатить" на экране + // UiElement findPayButton(const QList &ui); private: enum class State { + HandlingSecurityBanner, WaitingForPinInput, ClosingBanner, OnMainScreen, @@ -83,15 +84,19 @@ private: // Основной цикл скрипта void processUiLoop(); - void handleState(const QList &elements); + // void handleState(const QList &elements); + + static void handleSecurityBanner(const QList& elements); static void inputPinCode(const QString &pinCode); - void handleClosingBanner(QList &elements); + void handleClosingBanner(const QList &elements); static void swipeToFindElement(const QString &searchText); - void handleTransferScreen(QList &elements); + static void handleCardDetailsScreen(const QList &elements); + + // void handleTransferScreen(const QList &elements); static void inputAmount(const QString &amount); diff --git a/automation_script/XmlParser.cpp b/automation_script/XmlParser.cpp index d8dcc05..0418b05 100644 --- a/automation_script/XmlParser.cpp +++ b/automation_script/XmlParser.cpp @@ -49,6 +49,30 @@ QList XmlParser::parseUiDumpUsingQXml(const QString &filepath) { return elements; } +bool XmlParser::isSecurityBanner(const QList& elements) { + bool found = false; + + // Проверка на текст "Мы заботимся о вашей безопасности" + for (const UiElement& el : elements) { + if (el.text() == "Мы заботимся о вашей безопасности" && !el.text().isEmpty()) { + found = true; + break; + } + } + + // Если не нашли текст, ищем кнопку "Отложить" + if (!found) { + for (const UiElement& el : elements) { + if (el.text() == "Отложить" && el.isClickable() && el.className() == "android.widget.Button") { + found = true; + break; + } + } + } + + return found; +} + bool XmlParser::isPinCodeScreen(const QList &elements) { for (const UiElement &element: elements) { // Проверка на наличие текста для ввода PIN @@ -101,3 +125,28 @@ bool XmlParser::isMainScreen(const QList &elements) { } return false; } + +bool XmlParser::isDetailCardScreen(const QList &elements) { + bool isDetailCardScreen = false; + + // 1. Проверка на текст "Мои продукты" + for (const UiElement &el: elements) { + if (el.text().contains("Мои продукты") && !el.text().isEmpty()) { + isDetailCardScreen = true; + break; + } + } + + // 2. Если не нашли "Мои продукты", проверяем наличие кликабельной кнопки "Оплатить" + if (!isDetailCardScreen) { + for (const UiElement &el: elements) { + if (el.text() == "Оплатить" && el.isClickable() && el.className() == "android.widget.Button") { + // Кнопка "Оплатить" найдена и кликабельна + isDetailCardScreen = true; + break; + } + } + } + + return isDetailCardScreen; +} diff --git a/automation_script/XmlParser.h b/automation_script/XmlParser.h index 39b1604..cb744e1 100644 --- a/automation_script/XmlParser.h +++ b/automation_script/XmlParser.h @@ -13,10 +13,11 @@ public: explicit XmlParser(QObject *parent = nullptr); static QList parseUiDumpUsingQXml(const QString &filepath); + static bool isSecurityBanner(const QList& elements); static bool isPinCodeScreen(const QList& elements); static bool isBannerOrDialog(const QList& elements); static bool isMainScreen(const QList& elements); - + static bool isDetailCardScreen(const QList& elements); private: static QList extractElementsFromXml(QXmlStreamReader &xmlReader); }; diff --git a/main.cpp b/main.cpp index 6bca3b2..c22b4ca 100644 --- a/main.cpp +++ b/main.cpp @@ -85,7 +85,7 @@ int main(int argc, char *argv[]) { // qDebug() << "Account info: " << AccountDAO::upsertAccount(info); // } - // setupScriptAutoPaymentAndThread(app); + setupScriptAutoPaymentAndThread(app); // setupWorkerAndThread(app); //