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