Improve GetLastTransactionsScript to parse bottom-sheet transaction details for version 3.2.33. Add parseDushanbeAmount for enhanced number extraction.
This commit is contained in:
parent
3670e34938
commit
8abe5a915f
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <QSet>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimeZone>
|
#include <QTimeZone>
|
||||||
|
|
||||||
@ -16,6 +17,24 @@
|
|||||||
#include "AppLogger.h"
|
#include "AppLogger.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
// Душанбе показывает суммы как "100,50", "100.50 TJS", "1 000,50",
|
||||||
|
// "1 000.50 TJS" и т.п. Голый QString::toDouble на таком вернёт 0,
|
||||||
|
// потому что требует, чтобы вся строка была валидным числом.
|
||||||
|
static double parseDushanbeAmount(const QString &raw) {
|
||||||
|
QString s = raw;
|
||||||
|
s.remove(QRegularExpression(R"([^\d,.\-])"));
|
||||||
|
s.replace(',', '.');
|
||||||
|
const int firstDot = s.indexOf('.');
|
||||||
|
if (firstDot >= 0) {
|
||||||
|
QString head = s.left(firstDot);
|
||||||
|
QString tail = s.mid(firstDot + 1);
|
||||||
|
tail.remove('.');
|
||||||
|
s = head + '.' + tail;
|
||||||
|
}
|
||||||
|
return s.toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
static QString recognizeTextFromImage(const QByteArray &pngData) {
|
static QString recognizeTextFromImage(const QByteArray &pngData) {
|
||||||
auto *api = new tesseract::TessBaseAPI();
|
auto *api = new tesseract::TessBaseAPI();
|
||||||
if (api->Init(QCoreApplication::applicationDirPath().toUtf8().constData(), "rus+eng")) {
|
if (api->Init(QCoreApplication::applicationDirPath().toUtf8().constData(), "rus+eng")) {
|
||||||
@ -649,9 +668,72 @@ void GetLastTransactionsScript::doStart() {
|
|||||||
if (interrupted()) { m_error = "Interrupted"; emit finishedWithResult(m_error); return; }
|
if (interrupted()) { m_error = "Interrupted"; emit finishedWithResult(m_error); return; }
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||||||
// 1) Старый blob-формат (до 3.2.33): "Успешный платеж" / "Поступление" —
|
// 1) Bottom-sheet формат (3.2.33, см. assets/dushanbe/3.2.33/tx_detail.xml):
|
||||||
// одна нода с ключами и значениями через \n.
|
// одна нода со всем content-desc через \n. Лейблы без двоеточий, сумма —
|
||||||
|
// отдельной строкой со знаком сразу после даты+времени.
|
||||||
|
// "Успешный платёж\n21.05.2026 16:58:32\n-10\nКарта\n...\nПолучатель\n...\nЗакрыть"
|
||||||
for (const Node &n : xmlScreenParser.findAllNodes()) {
|
for (const Node &n : xmlScreenParser.findAllNodes()) {
|
||||||
|
const QString cd = n.contentDesc;
|
||||||
|
if (!cd.contains(QString::fromUtf8("Карта"))) continue;
|
||||||
|
if (!cd.contains(QString::fromUtf8("Получатель"))) continue;
|
||||||
|
if (!cd.contains(QString::fromUtf8("Операция"))) continue;
|
||||||
|
const QStringList lines = cd.split('\n', Qt::SkipEmptyParts);
|
||||||
|
if (lines.size() < 6) continue;
|
||||||
|
|
||||||
|
// lines[0] — заголовок: "Успешный платёж" / "Поступление" / ...
|
||||||
|
if (lines[0].contains(QString::fromUtf8("Успешн"))
|
||||||
|
|| lines[0].contains(QString::fromUtf8("Поступление"))
|
||||||
|
|| lines[0].contains(QString::fromUtf8("выполнен"))) {
|
||||||
|
detail.status = QString::fromUtf8("Успешный");
|
||||||
|
}
|
||||||
|
// lines[1] — "dd.MM.yyyy HH:mm:ss"
|
||||||
|
{
|
||||||
|
static const QRegularExpression reDt(
|
||||||
|
R"((\d{2}\.\d{2}\.\d{4})\s+(\d{2}:\d{2}:\d{2}))");
|
||||||
|
const auto m = reDt.match(lines[1]);
|
||||||
|
if (m.hasMatch()) {
|
||||||
|
detail.date = m.captured(1);
|
||||||
|
detail.time = m.captured(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// lines[2] — сумма со знаком: "-10" / "30" / "30.50" / "1 000,50".
|
||||||
|
detail.amount = parseDushanbeAmount(lines[2]);
|
||||||
|
|
||||||
|
// Дальше — пары label/value; значение может быть multi-line (Получатель).
|
||||||
|
// На сервер шлём первую строку получателя — таков сложившийся контракт.
|
||||||
|
static const QSet<QString> labels{
|
||||||
|
QString::fromUtf8("Карта"),
|
||||||
|
QString::fromUtf8("Получатель"),
|
||||||
|
QString::fromUtf8("Операция"),
|
||||||
|
QString::fromUtf8("Дата"),
|
||||||
|
QString::fromUtf8("Время"),
|
||||||
|
QString::fromUtf8("Закрыть"),
|
||||||
|
QString::fromUtf8("Назад"),
|
||||||
|
};
|
||||||
|
for (int i = 3; i < lines.size(); ++i) {
|
||||||
|
const QString lbl = lines[i].trimmed();
|
||||||
|
if (!labels.contains(lbl)) continue;
|
||||||
|
QStringList vals;
|
||||||
|
for (int j = i + 1; j < lines.size(); ++j) {
|
||||||
|
const QString cand = lines[j].trimmed();
|
||||||
|
if (labels.contains(cand)) break;
|
||||||
|
vals << cand;
|
||||||
|
}
|
||||||
|
if (vals.isEmpty()) continue;
|
||||||
|
if (lbl == QString::fromUtf8("Получатель")) {
|
||||||
|
detail.receiverAccount = vals.first();
|
||||||
|
} else if (lbl == QString::fromUtf8("Дата") && detail.date.isEmpty()) {
|
||||||
|
detail.date = vals.first();
|
||||||
|
} else if (lbl == QString::fromUtf8("Время") && detail.time.isEmpty()) {
|
||||||
|
detail.time = vals.first();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
detailLoaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 2) Старый blob-формат (до 3.2.33): "Успешный платеж" / "Поступление" —
|
||||||
|
// одна нода с ключами и значениями через \n.
|
||||||
|
if (!detailLoaded) for (const Node &n : xmlScreenParser.findAllNodes()) {
|
||||||
if (!n.contentDesc.contains(QString::fromUtf8("Дата"))) continue;
|
if (!n.contentDesc.contains(QString::fromUtf8("Дата"))) continue;
|
||||||
if (!n.contentDesc.contains(QString::fromUtf8("Время"))) continue;
|
if (!n.contentDesc.contains(QString::fromUtf8("Время"))) continue;
|
||||||
const QStringList lines = n.contentDesc.split('\n');
|
const QStringList lines = n.contentDesc.split('\n');
|
||||||
@ -663,7 +745,7 @@ void GetLastTransactionsScript::doStart() {
|
|||||||
if (key == QString::fromUtf8("Получатель")) {
|
if (key == QString::fromUtf8("Получатель")) {
|
||||||
detail.receiverAccount = val;
|
detail.receiverAccount = val;
|
||||||
} else if (key == QString::fromUtf8("Сумма")) {
|
} else if (key == QString::fromUtf8("Сумма")) {
|
||||||
detail.amount = val.toDouble();
|
detail.amount = parseDushanbeAmount(val);
|
||||||
} else if (key == QString::fromUtf8("Дата")) {
|
} else if (key == QString::fromUtf8("Дата")) {
|
||||||
detail.date = val;
|
detail.date = val;
|
||||||
} else if (key == QString::fromUtf8("Время")) {
|
} else if (key == QString::fromUtf8("Время")) {
|
||||||
@ -673,7 +755,8 @@ void GetLastTransactionsScript::doStart() {
|
|||||||
detailLoaded = true;
|
detailLoaded = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// 2) Новый split-формат (с 3.2.33): лейблы и значения в отдельных View-нодах.
|
// 3) Split-формат: лейблы (с ':') и значения в отдельных View-нодах
|
||||||
|
// на одной Y-полосе. Промежуточная версия между blob и bottom-sheet.
|
||||||
if (!detailLoaded) {
|
if (!detailLoaded) {
|
||||||
const QMap<QString, QString> pairs = xmlScreenParser.parseLabelValuePairs();
|
const QMap<QString, QString> pairs = xmlScreenParser.parseLabelValuePairs();
|
||||||
const QString d = pairs.value(QString::fromUtf8("Дата"));
|
const QString d = pairs.value(QString::fromUtf8("Дата"));
|
||||||
@ -682,11 +765,11 @@ void GetLastTransactionsScript::doStart() {
|
|||||||
detail.date = d;
|
detail.date = d;
|
||||||
detail.time = t;
|
detail.time = t;
|
||||||
detail.receiverAccount = pairs.value(QString::fromUtf8("Получатель"));
|
detail.receiverAccount = pairs.value(QString::fromUtf8("Получатель"));
|
||||||
detail.amount = pairs.value(QString::fromUtf8("Сумма")).toDouble();
|
detail.amount = parseDushanbeAmount(pairs.value(QString::fromUtf8("Сумма")));
|
||||||
if (detail.amount == 0.0) {
|
if (detail.amount == 0.0) {
|
||||||
detail.amount = pairs.value(QString::fromUtf8("К зачислению")).toDouble();
|
detail.amount = parseDushanbeAmount(pairs.value(QString::fromUtf8("К зачислению")));
|
||||||
}
|
}
|
||||||
detail.fee = pairs.value(QString::fromUtf8("Комиссия")).toDouble();
|
detail.fee = parseDushanbeAmount(pairs.value(QString::fromUtf8("Комиссия")));
|
||||||
detail.operationId = pairs.value(QString::fromUtf8("Номер операции"));
|
detail.operationId = pairs.value(QString::fromUtf8("Номер операции"));
|
||||||
detail.status = pairs.value(QString::fromUtf8("Статус"));
|
detail.status = pairs.value(QString::fromUtf8("Статус"));
|
||||||
detailLoaded = true;
|
detailLoaded = true;
|
||||||
|
|||||||
BIN
assets/dushanbe/3.2.33/tx_detail.jpg
Normal file
BIN
assets/dushanbe/3.2.33/tx_detail.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
1
assets/dushanbe/3.2.33/tx_detail.xml
Normal file
1
assets/dushanbe/3.2.33/tx_detail.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><hierarchy rotation="0"><node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="" class="android.widget.LinearLayout" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="android:id/content" class="android.widget.FrameLayout" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="" class="android.view.View" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="" class="android.view.View" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="" class="android.view.View" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="" class="android.view.View" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="" class="android.widget.ImageView" package="tj.dc.next1" content-desc="Успешный платёж 21.05.2026 16:58:32 -10 Карта 9762 0002 2091 6042 Получатель DCWallet*WDC0000**TAJIKISTAN 992176827668-9762000191282739- Операция Дебетная часть P2P/операции Дата 21.05.2026 Время 16:58:32 Закрыть" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="true" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]"><node index="0" text="" resource-id="" class="android.view.View" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]" /></node></node></node><node index="1" text="" resource-id="" class="android.view.View" package="tj.dc.next1" content-desc="Закрыть" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1640]" /></node></node></node></node></node></node><node index="1" text="" resource-id="android:id/navigationBarBackground" class="android.view.View" package="tj.dc.next1" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,1544][720,1640]" /></node></hierarchy>
|
||||||
Loading…
Reference in New Issue
Block a user