1
0
forked from BRT/arc
arc/android/ozon/GetLastTransactionsScript.cpp

668 lines
29 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "GetLastTransactionsScript.h"
#include <QProcess>
#include <QRegularExpression>
#include <QThread>
#include <QTimeZone>
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
#include "adb/AdbUtils.h"
#include "dao/MaterialDAO.h"
#include "dao/BankProfileDAO.h"
#include "dao/DeviceDAO.h"
#include "dao/TransactionDAO.h"
#include "time/DateUtils.h"
#include "AppLogger.h"
#include <QCoreApplication>
static QString recognizeTextFromImage(const QByteArray &pngData) {
auto *api = new tesseract::TessBaseAPI();
if (api->Init(QCoreApplication::applicationDirPath().toUtf8().constData(), "rus+eng")) {
qWarning() << "[OCR] Failed to init Tesseract";
delete api;
return {};
}
Pix *pix = pixReadMem(reinterpret_cast<const l_uint8 *>(pngData.constData()),
static_cast<size_t>(pngData.size()));
if (!pix) {
qWarning() << "[OCR] Failed to read image";
api->End();
delete api;
return {};
}
Pix *gray = pixConvertRGBToGray(pix, 0.0f, 0.0f, 0.0f);
pixDestroy(&pix);
if (!gray) {
qWarning() << "[OCR] Failed to convert to grayscale";
api->End();
delete api;
return {};
}
api->SetImage(gray);
api->SetSourceResolution(300);
char *text = api->GetUTF8Text();
QString result = QString::fromUtf8(text).trimmed();
delete[] text;
pixDestroy(&gray);
api->End();
delete api;
return result;
}
static QString extractTransactionId(const QString &ocrText) {
// ID может быть на одной строке: "ID операции B606513042211200000120011700501"
// или с дефисами: "ID операции po-3140eb01-002c-5000-8000-033396b637e1"
// или разбит на 2 строки: "ID ... po-3140eb01-002c-5000-8000-033396b\n ... 637e1"
QRegularExpression reId(R"(ID\s+операции\s*=?\s*([A-Za-z0-9\-]+(?:[^\S\n]+[A-Za-z0-9\-]+)?))");
if (auto m = reId.match(ocrText); m.hasMatch()) {
QString id = m.captured(1);
id.remove(' ');
id.remove(QChar(0x00A0));
return id;
}
QRegularExpression reRef(R"(№\s*(Ф-[\d-]+))");
if (auto m = reRef.match(ocrText); m.hasMatch()) return m.captured(1);
return {};
}
namespace Ozon {
GetLastTransactionsScript::GetLastTransactionsScript(
QString deviceId,
QString appCode,
int maxCount,
double searchAmount,
QDateTime searchTime,
QObject *parent
) : CommonScript(parent),
m_deviceId(std::move(deviceId)),
m_appCode(std::move(appCode)),
m_maxCount(maxCount),
m_searchAmount(searchAmount),
m_searchTime(std::move(searchTime)) {
}
GetLastTransactionsScript::~GetLastTransactionsScript() = default;
void GetLastTransactionsScript::doStart() {
DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId);
if (device.id.isEmpty()) device = DeviceDAO::findByAdbSerial(m_deviceId);
if (!device.adbSerial.isEmpty()) m_deviceId = device.adbSerial;
const BankProfileInfo app = BankProfileDAO::getApplication(device.id, m_appCode);
if (device.id.isEmpty() || app.id == -1) {
m_error = "Device or app not found: " + m_deviceId + " " + m_appCode;
qCritical() << m_error;
emit finishedWithResult(m_error);
return;
}
if (!navigateToTransactionList(device, app)) {
emit finishedWithResult(m_error);
return;
}
// Находим accountId
int accountId = -1;
const QList<MaterialInfo> accounts = MaterialDAO::getAccounts(device.id, m_appCode);
if (!accounts.isEmpty()) {
accountId = accounts.first().id;
} else {
qWarning() << "[GetLastTransactions] No account found for" << device.id << m_appCode;
}
// Выбор режима: поиск конкретной транзакции или сканирование последних
if (qAbs(m_searchAmount) > 0.01) {
searchTransaction(device, accountId);
} else {
scanRecentTransactions(device, accountId);
}
emit finishedWithResult(m_error);
}
bool GetLastTransactionsScript::navigateToTransactionList(const DeviceInfo &device, const BankProfileInfo &app) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// Закрываем рекламный bottom sheet
if (Node adNode = xmlScreenParser.tryToFindAdBanner(); !adNode.isEmpty()) {
AdbUtils::makeTap(m_deviceId, adNode.x(), adNode.y());
QThread::msleep(1000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (Node stillAd = xmlScreenParser.tryToFindAdBanner(); !stillAd.isEmpty()) {
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
}
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// Если не на домашнем экране — перезапускаем
if (!xmlScreenParser.isHomeScreen()) {
if (!runAppAndGoToHomeScreen(m_deviceId, app.package, app.pinCode,
device.screenWidth, device.screenHeight)) {
m_error = "Cannot reach home screen: " + device.name + " (" + m_deviceId + ")";
qWarning() << m_error;
m_resultScreenshot = AdbUtils::captureScreenshotBytes(m_deviceId);
if (!m_resultScreenshot.isEmpty()) emit screenshotReady(m_resultScreenshot);
AppLogger::log("ozon/GetLastTransactions", m_deviceId, m_error, {}, "FETCH_TRANSACTIONS");
AdbUtils::tryToKillApplication(m_deviceId, app.package);
return false;
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
}
// Закрываем баннеры на главной
tryToCloseAllBannersOnHomePage(m_deviceId, device.screenWidth, device.screenHeight);
QThread::msleep(500);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// Скроллим до "Все" и тапаем (быстрый fling чтобы пропустить баннеры)
const int navBarTop = device.screenHeight - (device.screenHeight / 10);
Node allBtn;
for (int i = 0; i < 10; ++i) {
if (QThread::currentThread()->isInterruptionRequested()) return false;
const int x = device.screenWidth / 2;
AdbUtils::makeSwipe(m_deviceId, x, device.screenHeight - 200, x, 200, 100);
QThread::msleep(1500);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
allBtn = xmlScreenParser.findButtonNode("Все", false);
if (!allBtn.isEmpty() && allBtn.y() > 0 && allBtn.y() < navBarTop) break;
allBtn = {};
}
if (allBtn.isEmpty()) {
m_error = "Button 'Все' not found: " + m_deviceId;
qWarning() << m_error;
return false;
}
AdbUtils::makeTap(m_deviceId, allBtn.x(), allBtn.y());
QThread::msleep(2000);
// Ждём экран "Операции"
for (int attempt = 0; attempt < 10; ++attempt) {
if (QThread::currentThread()->isInterruptionRequested()) return false;
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isTransactionListScreen()) return true;
QThread::msleep(1500);
}
m_error = "Transaction list screen not loaded: " + m_deviceId;
qWarning() << m_error;
return false;
}
void GetLastTransactionsScript::searchTransaction(const DeviceInfo &device, int accountId) {
const QTimeZone msk("Europe/Moscow");
const int maxScrolls = 30;
const int navBarTop = device.screenHeight - (device.screenHeight / 10);
// Целевая дата в МСК для поиска заголовка дня в списке
const QDateTime searchMsk = m_searchTime.toTimeZone(msk);
const QDate searchDate = searchMsk.date();
qDebug() << "[FetchTransactions] Searching: amount=" << m_searchAmount
<< "time=" << m_searchTime.toString(Qt::ISODate)
<< "date(MSK)=" << searchDate.toString("dd.MM.yyyy");
// ── Шаг 1: скроллим до нужной даты ──────────────────────────────
bool dateFound = false;
for (int scroll = 0; scroll < maxScrolls; ++scroll) {
if (QThread::currentThread()->isInterruptionRequested()) {
m_error = "Interrupted";
return;
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// Ищем заголовки дат на экране (формат: "4 января, вс")
static const QMap<QString, int> monthMap = {
{"января", 1}, {"февраля", 2}, {"марта", 3}, {"апреля", 4},
{"мая", 5}, {"июня", 6}, {"июля", 7}, {"августа", 8},
{"сентября", 9}, {"октября", 10}, {"ноября", 11}, {"декабря", 12}
};
bool hasOlderDate = false;
for (const Node &node : xmlScreenParser.getNodes()) {
if (node.className != "android.widget.TextView") continue;
const QString t = node.text.trimmed();
// Парсим "4 января, вс" → QDate
static const QRegularExpression dateRx(R"((\d{1,2})\s+(\S+),\s+\S+)");
const auto match = dateRx.match(t);
if (!match.hasMatch()) continue;
const int day = match.captured(1).toInt();
const int month = monthMap.value(match.captured(2), 0);
if (month == 0) continue;
const QDate headerDate(searchDate.year(), month, day);
if (!headerDate.isValid()) continue;
if (headerDate == searchDate) {
dateFound = true;
qDebug() << "[FetchTransactions] Found target date header:" << t;
break;
}
if (headerDate < searchDate) {
hasOlderDate = true;
}
}
if (dateFound) break;
if (hasOlderDate) {
// Проскочили мимо нужной даты — её нет в списке
qDebug() << "[FetchTransactions] Passed target date, not found in list";
break;
}
// Скроллим вниз
AdbUtils::makeSwipe(m_deviceId, device.screenWidth / 2,
device.screenHeight - 300, device.screenWidth / 2, 300, 300);
QThread::msleep(1500);
}
if (!dateFound) {
m_error = "Date not found in transaction list: " + searchDate.toString("dd.MM.yyyy");
qWarning() << "[FetchTransactions]" << m_error;
m_resultScreenshot = AdbUtils::captureScreenshotBytes(m_deviceId);
if (!m_resultScreenshot.isEmpty()) emit screenshotReady(m_resultScreenshot);
return;
}
// ── Шаг 2: на экране с нужной датой ищем транзакцию по сумме ────
for (int attempt = 0; attempt < 10; ++attempt) {
if (QThread::currentThread()->isInterruptionRequested()) {
m_error = "Interrupted";
return;
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
QList<Node> txButtons = xmlScreenParser.findTransactionButtons(20);
for (int bi = 0; bi < txButtons.size(); ++bi) {
if (QThread::currentThread()->isInterruptionRequested()) {
m_error = "Interrupted";
return;
}
const TransactionInfo parsed = xmlScreenParser.parseTransactionButtonText(txButtons[bi].text);
if (qAbs(parsed.amount - m_searchAmount) > 0.01) continue;
// Если кнопка за экраном — скроллим до неё, рассчитывая дистанцию
if (txButtons[bi].y() <= 0 || txButtons[bi].y() >= navBarTop) {
bool scrolledIntoView = false;
for (int s = 0; s < 5; ++s) {
const int btnY = txButtons[bi].y();
const int x = device.screenWidth / 2;
const int visibleCenter = navBarTop / 2;
// Расстояние от кнопки до центра видимой области + небольшой запас
const int delta = btnY - visibleCenter;
const int fromY = qBound(100, visibleCenter + delta / 2, device.screenHeight - 100);
const int toY = qBound(100, visibleCenter - delta / 2, device.screenHeight - 100);
AdbUtils::makeSwipe(m_deviceId, x, fromY, x, toY, 300);
QThread::msleep(1000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
txButtons = xmlScreenParser.findTransactionButtons(20);
if (bi < txButtons.size() && txButtons[bi].y() > 0 && txButtons[bi].y() < navBarTop) {
scrolledIntoView = true;
break;
}
}
if (!scrolledIntoView) continue;
}
const Node &visibleBtn = txButtons[bi];
qDebug() << "[FetchTransactions] Amount match:" << parsed.amount
<< "at" << visibleBtn.x() << visibleBtn.y();
// Тапаем и ждём деталей
AdbUtils::makeTap(m_deviceId, visibleBtn.x(), visibleBtn.y());
QThread::msleep(2000);
TransactionInfo detail;
for (int da = 0; da < 10; ++da) {
if (QThread::currentThread()->isInterruptionRequested()) {
m_error = "Interrupted";
return;
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
detail = xmlScreenParser.parseTransactionDetail();
if (!detail.bankTime.isEmpty()) break;
if (xmlScreenParser.isTransactionListScreen()) {
AdbUtils::makeTap(m_deviceId, visibleBtn.x(), visibleBtn.y());
}
QThread::msleep(1500);
}
if (detail.bankTime.isEmpty()) {
qWarning() << "[FetchTransactions] Could not load detail, going back";
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
continue;
}
// Парсим дату из деталей
QString dtStr = detail.bankTime;
for (int ci = 0; ci < dtStr.size(); ++ci) {
const QChar ch = dtStr.at(ci);
if (ch != ' ' && ch.category() == QChar::Separator_Space) dtStr[ci] = ' ';
}
QDateTime txTime = QDateTime::fromString(dtStr, "dd.MM.yyyy, HH:mm");
if (!txTime.isValid()) txTime = QDateTime::fromString(dtStr, "dd.MM.yyyy,HH:mm");
if (txTime.isValid()) txTime.setTimeZone(msk);
// Проверяем что дата и время совпадают (±1 час от m_searchTime)
if (txTime.isValid()) {
if (txTime.date() != searchDate) {
qDebug() << "[FetchTransactions] Wrong date:" << dtStr << "expected" << searchDate;
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
continue;
}
if (m_searchTime.isValid()) {
const qint64 diffSecs = qAbs(txTime.toUTC().secsTo(m_searchTime.toUTC()));
if (diffSecs > 3600) {
qDebug() << "[FetchTransactions] Time mismatch:" << dtStr
<< "expected" << searchMsk.toString("dd.MM.yyyy, HH:mm")
<< "diff=" << diffSecs << "sec";
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
continue;
}
}
}
// Нашли!
qDebug() << "[FetchTransactions] FOUND transaction:"
<< "amount=" << parsed.amount << "time=" << dtStr
<< "name=" << detail.name;
m_resultScreenshot = AdbUtils::captureScreenshotBytes(m_deviceId);
// Пробуем получить ID через "Документы" → OCR
QString externalId = detail.bankTrExternalId;
if (externalId.isEmpty() && !QThread::currentThread()->isInterruptionRequested()) {
Node docBtn = xmlScreenParser.findButtonNode("Документы", false);
if (!docBtn.isEmpty()) {
qDebug() << "[FetchTransactions] Tapping 'Документы'...";
AdbUtils::makeTap(m_deviceId, docBtn.x(), docBtn.y());
QThread::msleep(2000);
bool docLoaded = false;
for (int da = 0; da < 15; ++da) {
if (QThread::currentThread()->isInterruptionRequested()) break;
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (!xmlScreenParser.findNodeByContentDesc("Поделиться").isEmpty()) {
docLoaded = true;
break;
}
QThread::msleep(1500);
}
if (docLoaded) {
QThread::msleep(500);
const QByteArray screenshot = AdbUtils::captureScreenshotBytes(m_deviceId);
if (!screenshot.isEmpty()) {
m_resultScreenshot = screenshot;
const QString ocrText = recognizeTextFromImage(screenshot);
if (!ocrText.isEmpty()) {
qDebug() << "[FetchTransactions] OCR result:" << ocrText;
externalId = extractTransactionId(ocrText);
if (!externalId.isEmpty()) {
qDebug() << "[FetchTransactions] Extracted ID:" << externalId;
}
}
}
}
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
}
}
// Сохраняем
if (accountId != -1) {
TransactionInfo tx;
tx.accountId = accountId;
tx.amount = parsed.amount;
tx.bankName = m_appCode;
tx.bankTime = dtStr;
tx.bankTrExternalId = externalId;
tx.status = TransactionStatus::Complete;
tx.timestamp = DateUtils::getUtcNow();
if (txTime.isValid()) tx.completeTime = txTime;
if (!detail.name.isEmpty()) tx.name = detail.name;
if (!detail.description.isEmpty()) tx.description = detail.description;
if (!detail.phone.isEmpty()) {
QString cleanPhone;
for (const QChar &ch : detail.phone)
if (ch.isDigit()) cleanPhone.append(ch);
tx.phone = cleanPhone;
tx.type = TransactionType::Phone;
}
TransactionDAO::insertTransactionIfNotExists(tx);
}
if (!m_resultScreenshot.isEmpty())
emit screenshotReady(m_resultScreenshot);
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
return; // успех
}
// Скроллим чуть вниз чтобы увидеть больше транзакций за этот день
AdbUtils::makeSwipe(m_deviceId, device.screenWidth / 2,
device.screenHeight - 300, device.screenWidth / 2, 300, 300);
QThread::msleep(1000);
}
m_error = "Transaction not found: amount=" + QString::number(m_searchAmount, 'f', 2)
+ " date=" + searchDate.toString("dd.MM.yyyy");
qWarning() << "[FetchTransactions]" << m_error;
m_resultScreenshot = AdbUtils::captureScreenshotBytes(m_deviceId);
if (!m_resultScreenshot.isEmpty()) emit screenshotReady(m_resultScreenshot);
}
void GetLastTransactionsScript::scanRecentTransactions(const DeviceInfo &device, int accountId) {
const QTimeZone msk("Europe/Moscow");
const int navBarTop = device.screenHeight - (device.screenHeight / 10);
// Ждём появления транзакций
QList<Node> txButtons;
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
txButtons = xmlScreenParser.findTransactionButtons(8);
if (!txButtons.isEmpty()) break;
QThread::msleep(1500);
}
qDebug() << "[GetLastTransactions] Found" << txButtons.size() << "transaction buttons";
QList<TransactionInfo> transactions;
for (int i = 0; i < txButtons.size(); ++i) {
const Node &btn = txButtons[i];
// Скроллим до кнопки если за экраном
if (btn.y() <= 0 || btn.y() >= navBarTop) {
for (int s = 0; s < 5; ++s) {
const int x = device.screenWidth / 2;
const int offset = device.screenHeight / 4;
AdbUtils::makeSwipe(m_deviceId, x, device.screenHeight - offset, x, offset);
QThread::msleep(1000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
txButtons = xmlScreenParser.findTransactionButtons(8);
if (i < txButtons.size() && txButtons[i].y() > 0 && txButtons[i].y() < navBarTop) break;
}
if (i >= txButtons.size()) break;
}
const Node &visibleBtn = txButtons[i];
TransactionInfo tx = xmlScreenParser.parseTransactionButtonText(visibleBtn.text);
AdbUtils::makeTap(m_deviceId, visibleBtn.x(), visibleBtn.y());
QThread::msleep(2000);
// Ждём загрузки деталей
TransactionInfo detail;
for (int attempt = 0; attempt < 10; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
detail = xmlScreenParser.parseTransactionDetail();
if (!detail.bankTime.isEmpty()) break;
if (xmlScreenParser.isTransactionListScreen()) {
AdbUtils::makeTap(m_deviceId, visibleBtn.x(), visibleBtn.y());
}
QThread::msleep(1500);
}
tx.bankName = m_appCode;
tx.bankTime = detail.bankTime;
tx.bankTrExternalId = detail.bankTrExternalId;
// Если ID не найден в XML — пробуем через "Документы" + OCR
if (tx.bankTrExternalId.isEmpty()) {
Node docBtn = xmlScreenParser.findButtonNode("Документы", false);
if (!docBtn.isEmpty()) {
AdbUtils::makeTap(m_deviceId, docBtn.x(), docBtn.y());
QThread::msleep(2000);
bool docLoaded = false;
for (int da = 0; da < 15; ++da) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (!xmlScreenParser.findNodeByContentDesc("Поделиться").isEmpty()) {
docLoaded = true;
break;
}
QThread::msleep(1500);
}
if (docLoaded) {
QThread::msleep(500);
const QByteArray screenshot = AdbUtils::captureScreenshotBytes(m_deviceId);
if (!screenshot.isEmpty()) {
const QString ocrText = recognizeTextFromImage(screenshot);
if (!ocrText.isEmpty()) {
tx.bankTrExternalId = extractTransactionId(ocrText);
qDebug() << "[GetLastTransactions] OCR extracted ID:" << tx.bankTrExternalId;
}
}
}
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
}
}
tx.status = TransactionStatus::Complete;
tx.timestamp = DateUtils::getUtcNow();
if (!detail.name.isEmpty()) tx.name = detail.name;
if (!detail.phone.isEmpty()) {
QString cleanPhone;
for (const QChar &ch : detail.phone)
if (ch.isDigit()) cleanPhone.append(ch);
tx.phone = cleanPhone;
tx.type = TransactionType::Phone;
}
// Парсим время
if (!tx.bankTime.isEmpty()) {
QString dtStr = tx.bankTime;
for (int ci = 0; ci < dtStr.size(); ++ci) {
const QChar ch = dtStr.at(ci);
if (ch != ' ' && ch.category() == QChar::Separator_Space) dtStr[ci] = ' ';
}
tx.bankTime = dtStr;
QDateTime parsed = QDateTime::fromString(dtStr, "dd.MM.yyyy, HH:mm");
if (!parsed.isValid()) parsed = QDateTime::fromString(dtStr, "dd.MM.yyyy,HH:mm");
if (parsed.isValid()) {
parsed.setTimeZone(msk);
tx.completeTime = parsed;
}
}
// Фильтр по времени (24ч) если нет maxCount
if (m_maxCount <= 0 && tx.completeTime.isValid()) {
const qint64 ageSecs = tx.completeTime.toUTC().secsTo(QDateTime::currentDateTimeUtc());
if (ageSecs > 24 * 3600) {
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
break;
}
}
// Сохраняем
if (accountId != -1) {
tx.accountId = accountId;
if (TransactionDAO::insertTransactionIfNotExists(tx)) {
// Скриншот чека
Node docBtn = xmlScreenParser.findButtonNode("Документы", false);
if (!docBtn.isEmpty()) {
AdbUtils::makeTap(m_deviceId, docBtn.x(), docBtn.y());
QThread::msleep(2000);
bool docLoaded = false;
for (int da = 0; da < 20; ++da) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (!xmlScreenParser.findNodeByContentDesc("Поделиться").isEmpty()) {
docLoaded = true; break;
}
QThread::msleep(1500);
}
if (docLoaded) {
QThread::msleep(500);
QProcess screencapProc;
screencapProc.start("adb", QStringList() << "-s" << m_deviceId << "exec-out" << "screencap" << "-p");
screencapProc.waitForFinished(5000);
const QByteArray screenshot = screencapProc.readAllStandardOutput();
if (!screenshot.isEmpty()) {
TransactionInfo saved = TransactionDAO::findByBankTimeAndAmount(
accountId, tx.bankName, tx.bankTime, tx.amount);
if (saved.id != -1) {
TransactionDAO::updateReceiptImage(saved.id, screenshot);
const QString ocrText = recognizeTextFromImage(screenshot);
if (!ocrText.isEmpty()) {
const QString txId = extractTransactionId(ocrText);
if (!txId.isEmpty()) {
TransactionDAO::updateBankTrExternalId(saved.id, txId);
}
}
}
}
}
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
}
}
}
transactions.append(tx);
if (m_maxCount > 0 && transactions.size() >= m_maxCount) {
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
break;
}
AdbUtils::goBack(m_deviceId);
QThread::msleep(1500);
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isTransactionListScreen()) break;
QThread::msleep(1000);
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
txButtons = xmlScreenParser.findTransactionButtons(8);
}
}
} // namespace Ozon