1
0
forked from BRT/arc

Add AppVersionTrack enum and related helpers to support version-based parsing. Include XML assets for Dushanbe 3.2.38 version.

This commit is contained in:
slava 2026-06-01 18:17:38 +05:00
parent e08514ea2e
commit c6c35cff2c
18 changed files with 105 additions and 0 deletions

View File

@ -105,6 +105,7 @@ add_executable(ARC WIN32
views/log/LogDetailWindow.h
database/dao/GeneralLogDAO.cpp
database/dao/GeneralLogDAO.h
android/dushanbe/AppVersionTrack.h
android/dushanbe/CommonScript.cpp
android/dushanbe/CommonScript.h
android/dushanbe/GetCardInfoScript.cpp
@ -136,6 +137,7 @@ elseif (APPLE)
views/log/LogDetailWindow.h
database/dao/GeneralLogDAO.cpp
database/dao/GeneralLogDAO.h
android/dushanbe/AppVersionTrack.h
android/dushanbe/CommonScript.cpp
android/dushanbe/CommonScript.h
android/dushanbe/GetCardInfoScript.cpp

View File

@ -0,0 +1,54 @@
#pragma once
#include <QString>
#include <QVersionNumber>
namespace Dushanbe {
// «Трек» формата UI приложения Dushanbe City Bank. Экранные дампы и порядок
// нод в content-desc менялись между версиями приложения; трек — это
// нормализованный ярлык версии, по которому код выбирает версионно-зависимую
// ветку парсинга (см. ScreenXmlParser::setVersionTrack).
//
// На момент введения каркаса форматы 3.2.33 и 3.2.38 практически идентичны,
// поэтому отдельных веток ещё нет — трек служит явным швом для будущих
// расхождений и читаемым маркером в логах/дампах.
enum class AppVersionTrack {
Legacy, // < 3.2.33 — старый blob-формат (один content-desc с "Версия:"/"Сборка:")
V3_2_33, // 3.2.33 .. < 3.2.38
V3_2_38, // >= 3.2.38 (и новейший по умолчанию)
};
// Резолвит трек из строки версии (например "3.2.38", полученной через
// AdbUtils::getAppVersion → BankProfileInfo.appVersion). Сравнение —
// через QVersionNumber, а не лексикографически по строке.
//
// Неизвестная/непарсящаяся версия, а также любая версия новее последнего
// известного порога, трактуется как новейший известный трек (V3_2_38): свежие
// апдейты по умолчанию должны идти по последнему формату, а не падать на Legacy.
inline AppVersionTrack trackForVersion(const QString &appVersion) {
static const QVersionNumber kV3_2_33(3, 2, 33);
static const QVersionNumber kV3_2_38(3, 2, 38);
qsizetype suffixIndex = 0;
const QVersionNumber v = QVersionNumber::fromString(appVersion.trimmed(), &suffixIndex);
if (v.isNull()) {
// Не смогли распарсить — считаем новейшим известным форматом.
return AppVersionTrack::V3_2_38;
}
if (v < kV3_2_33) return AppVersionTrack::Legacy;
if (v < kV3_2_38) return AppVersionTrack::V3_2_33;
return AppVersionTrack::V3_2_38;
}
// Человекочитаемое имя трека для логов.
inline const char *trackName(AppVersionTrack track) {
switch (track) {
case AppVersionTrack::Legacy: return "Legacy(<3.2.33)";
case AppVersionTrack::V3_2_33: return "3.2.33";
case AppVersionTrack::V3_2_38: return "3.2.38";
}
return "unknown";
}
} // namespace Dushanbe

View File

@ -39,6 +39,11 @@ void GetCardInfoScript::doStart() {
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
// Версионный трек приложения → версионно-зависимый парсинг (см. AppVersionTrack.h).
xmlScreenParser.setVersionTrack(Dushanbe::trackForVersion(app.appVersion));
qDebug() << "[Dushanbe::GetCardInfo] appVersion=" << app.appVersion
<< "→ track" << Dushanbe::trackName(xmlScreenParser.versionTrack());
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);

View File

@ -281,6 +281,11 @@ void GetLastTransactionsScript::doStart() {
const TaskDumpMeta dumpMeta{m_materialId, firstAmount, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_TRANSACTIONS", m_deviceId, dumpMeta, &m_error);
// Версионный трек приложения → версионно-зависимый парсинг (см. AppVersionTrack.h).
xmlScreenParser.setVersionTrack(Dushanbe::trackForVersion(app.appVersion));
qDebug() << "[Dushanbe::GetLastTransactions] appVersion=" << app.appVersion
<< "→ track" << Dushanbe::trackName(xmlScreenParser.versionTrack());
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (!xmlScreenParser.isHomeScreen()) {

View File

@ -42,6 +42,11 @@ void GetProfileInfoScript::doStart() {
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
// Версионный трек приложения → версионно-зависимый парсинг (см. AppVersionTrack.h).
xmlScreenParser.setVersionTrack(Dushanbe::trackForVersion(app.appVersion));
qDebug() << "[Dushanbe::GetProfileInfo] appVersion=" << app.appVersion
<< "→ track" << Dushanbe::trackName(xmlScreenParser.versionTrack());
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (!xmlScreenParser.isHomeScreen()) {

View File

@ -44,6 +44,11 @@ void LoginAndCheckAccountsScript::doStart() {
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
// Версионный трек приложения → версионно-зависимый парсинг (см. AppVersionTrack.h).
xmlScreenParser.setVersionTrack(Dushanbe::trackForVersion(app.appVersion));
qDebug() << "[Dushanbe::Login] appVersion=" << app.appVersion
<< "→ track" << Dushanbe::trackName(xmlScreenParser.versionTrack());
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (!runAppAndGoToHomeScreen(m_deviceId, app.package, app.pinCode, device.screenWidth, device.screenHeight)) {

View File

@ -126,6 +126,11 @@ void PayByCardScript::doStart() {
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, {}, m_cardNumber, app.bankProfileId, app.phone, device.id, app.appVersion};
TaskDumpRecorder::Scope dumpScope("dushanbe", "CREATE_TRANSACTION_CARD", device.adbSerial, dumpMeta, &m_error);
// Версионный трек приложения → версионно-зависимый парсинг (см. AppVersionTrack.h).
xmlScreenParser.setVersionTrack(Dushanbe::trackForVersion(app.appVersion));
qDebug() << "[Dushanbe::PayByCard] appVersion=" << app.appVersion
<< "→ track" << Dushanbe::trackName(xmlScreenParser.versionTrack());
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
if (!xmlScreenParser.isHomeScreen()) {

View File

@ -123,6 +123,11 @@ void PayByPhoneScript::doStart() {
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, m_phone, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
TaskDumpRecorder::Scope dumpScope("dushanbe", "CREATE_TRANSACTION_PHONE", device.adbSerial, dumpMeta, &m_error);
// Версионный трек приложения → версионно-зависимый парсинг (см. AppVersionTrack.h).
xmlScreenParser.setVersionTrack(Dushanbe::trackForVersion(app.appVersion));
qDebug() << "[Dushanbe::PayByPhone] appVersion=" << app.appVersion
<< "→ track" << Dushanbe::trackName(xmlScreenParser.versionTrack());
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
if (!xmlScreenParser.isHomeScreen()) {

View File

@ -6,6 +6,7 @@
#include <QObject>
#include <QDomElement>
#include "android/xml/Node.h"
#include "AppVersionTrack.h"
#include "db/MaterialInfo.h"
namespace Dushanbe {
@ -20,6 +21,14 @@ public:
void parseAndSaveXml(const QString &xml);
// Версионный трек приложения, под которое сейчас идёт парсинг. Скрипт
// выставляет его один раз после загрузки BankProfileInfo:
// parser.setVersionTrack(Dushanbe::trackForVersion(app.appVersion));
// Версионно-зависимые методы парсинга используют m_track как явный шов
// (см. AppVersionTrack.h). По умолчанию — новейший известный формат.
void setVersionTrack(AppVersionTrack track) { m_track = track; }
AppVersionTrack versionTrack() const { return m_track; }
const QList<Node> &findAllNodes() const;
Node findTextNode(const QString &text);
@ -173,6 +182,7 @@ public:
private:
QList<Node> m_nodes;
QDomElement m_xml;
AppVersionTrack m_track = AppVersionTrack::V3_2_38;
};
} // namespace Dushanbe

View 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,1444]"><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,1444]"><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,1444]"><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,1444]"><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,1444]"><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,1444]"><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,1444]"><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,1444]"><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,1444]"><node index="0" text="" resource-id="" class="android.widget.Button" package="tj.dc.next1" content-desc="Подтверждение платежа&#10;1.00 TJS&#10;Поставщик:&#10;DC (по номеру карты) &#10;Номер/Счет&#10;9762000104225205&#10;Способ оплаты:&#10;9762 **** **** 0819&#10;Сумма зачисления:&#10;1.00 TJS&#10;Комиссия:&#10;0.00 TJS&#10;Сумма списания:&#10;1.00 TJS&#10;Подтверждаю" 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,1444]"><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,1444]" /></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,1444]" /></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,0][0,0]" /></node></hierarchy>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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,1444]"><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,1444]"><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,1444]"><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,1444]"><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="true" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][720,1444]"><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,1444]"><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,1444]"><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,1444]"><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,1444]"><node index="0" text="" resource-id="" class="android.widget.Button" package="tj.dc.next1" content-desc="Подтверждение платежа&#10;1.00 TJS&#10;Поставщик:&#10;DC (по номеру телефона) &#10;Номер/Счет&#10;9762000186263942&#10;Способ оплаты:&#10;9762 **** **** 0819&#10;Сумма зачисления:&#10;1.00 TJS&#10;Комиссия:&#10;0.00 TJS&#10;Сумма списания:&#10;1.00 TJS&#10;Подтверждаю" 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,1444]"><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,1444]" /></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,1444]" /></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,0][0,0]" /></node></hierarchy>

File diff suppressed because one or more lines are too long