189 lines
8.7 KiB
C++
189 lines
8.7 KiB
C++
#pragma once
|
||
|
||
#include <optional>
|
||
|
||
#include <QMap>
|
||
#include <QObject>
|
||
#include <QDomElement>
|
||
#include "android/xml/Node.h"
|
||
#include "AppVersionTrack.h"
|
||
#include "db/MaterialInfo.h"
|
||
|
||
namespace Dushanbe {
|
||
|
||
class ScreenXmlParser final : public QObject {
|
||
Q_OBJECT
|
||
|
||
public:
|
||
explicit ScreenXmlParser(QObject *parent = nullptr);
|
||
|
||
~ScreenXmlParser() override;
|
||
|
||
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);
|
||
|
||
Node findNodeByContentDesc(const QString &contentDesc);
|
||
|
||
Node findNodeByContentDesc(const QString &contentDesc, bool contains);
|
||
|
||
Node findButtonNode(const QString &text, bool contains);
|
||
|
||
Node findFirstEditText();
|
||
|
||
// TODO: реализовать после снятия XML дампов экранов
|
||
bool isPinCodeScreen();
|
||
|
||
Node findPinCodeEditText();
|
||
|
||
bool isHomeScreen();
|
||
|
||
// Парсит баланс с домашнего экрана. Возвращает 0.0, если блок "TJS\n<дата>"
|
||
// не найден или внутри него нет числовой ноды (это может быть как реально
|
||
// нулевой баланс, так и loading state сразу после тапа refresh). Для
|
||
// различения этих случаев используйте parseBalanceFromHomeScreenOpt().
|
||
double parseBalanceFromHomeScreen();
|
||
|
||
// То же, что parseBalanceFromHomeScreen, но возвращает std::nullopt, когда
|
||
// экран в состоянии загрузки (родитель имеет content-desc "TJS" без даты,
|
||
// либо блок "TJS\n<дата>" найден, но числовая нода рядом отсутствует) или
|
||
// когда мы вообще не на home screen. При nullopt вызывающий код может
|
||
// подождать и попробовать снова — это безопаснее, чем интерпретировать
|
||
// loading как реальный нулевой баланс.
|
||
std::optional<double> parseBalanceFromHomeScreenOpt();
|
||
|
||
// Находит кнопку обновления данных в блоке "TJS\n<дата>" — правый clickable
|
||
// ImageView (рядом слева сидит глазик-Button, его не трогаем).
|
||
Node findRefreshButtonOnHomeScreen();
|
||
|
||
// Парсит имя пользователя с домашнего экрана
|
||
QString parseUserNameFromHomeScreen();
|
||
|
||
// Определяет боковое меню (профиль)
|
||
bool isSideMenu();
|
||
|
||
// Парсит данные профиля из бокового меню
|
||
// content-desc: "Имя Фамилия\n+992...\nВерсия:...\nСборка:..."
|
||
QString parseProfileFullName();
|
||
QString parseProfilePhone();
|
||
|
||
// Определяет экран "Мои карты"
|
||
bool isCardsScreen();
|
||
|
||
// Структура для карты из экрана "Мои карты"
|
||
struct CardInfo {
|
||
double amount = 0.0;
|
||
QString currency;
|
||
QString lastNumbers; // "3258"
|
||
QString expiry; // "11/35"
|
||
QString holderName; // "IMOMKULZODA MEKHRUBON"
|
||
QString description; // "Кредитная карта" и т.д.
|
||
};
|
||
|
||
// Парсит все карты с экрана "Мои карты" (lastNumbers только)
|
||
QList<CardInfo> parseCards();
|
||
|
||
// Определяет экран детали карты (заголовок "Карта")
|
||
bool isCardDetailScreen();
|
||
|
||
// Парсит полный номер карты с экрана детали ("9762 0002 0711 3258")
|
||
QString parseFullCardNumber();
|
||
|
||
// Парсит баланс с экрана детали ("0,00 TJS")
|
||
double parseCardDetailBalance();
|
||
|
||
// Парсит имя держателя с экрана детали
|
||
QString parseCardDetailHolder();
|
||
|
||
// Кнопка "Настройки карты" на экране детали карты
|
||
Node findCardSettingsButton();
|
||
|
||
// Bottom sheet с пунктами "Реквизиты карты" / "Заблокировать карту" и т.д.
|
||
bool isCardSettingsBottomSheet();
|
||
|
||
// Кнопка "Реквизиты карты" внутри bottom sheet "Настройки карты"
|
||
Node findCardRequisitesMenuItem();
|
||
|
||
// Экран "Реквизиты и информация" с табами "Реквизиты карты" / "Реквизиты счета"
|
||
bool isCardRequisitesScreen();
|
||
|
||
// Таб "Реквизиты счета" (правый) на экране "Реквизиты и информация"
|
||
Node findAccountRequisitesTab();
|
||
|
||
// Значение под лейблом "Номер счета" — длинная цифровая строка
|
||
QString parseAccountNumber();
|
||
|
||
// Определяет экран истории операций (табы "Операции" + "Выписка")
|
||
bool isHistoryScreen();
|
||
|
||
// Структура для транзакции из экрана истории
|
||
struct TransactionItem {
|
||
QString cardMask; // "9762***3258"
|
||
QString phone; // "992882770011"
|
||
double amount = 0.0; // 493.00
|
||
QString description; // "DC (по номеру телефона)"
|
||
QString time; // "18:57:10"
|
||
};
|
||
|
||
// Парсит транзакции с экрана истории
|
||
QList<TransactionItem> parseTransactionItems();
|
||
|
||
// Определяет экран детали транзакции (содержит "Дата операции:" и "Номер операции:")
|
||
bool isTransactionDetailScreen();
|
||
|
||
// Структура для деталей транзакции
|
||
struct TransactionDetail {
|
||
QString date; // "03.04.2026"
|
||
QString time; // "18:57:10"
|
||
QString operationId; // "1593369670"
|
||
QString provider; // "DC (по номеру телефона)"
|
||
QString senderAccount; // "9762***3258"
|
||
QString receiverAccount;// "992882770011"
|
||
double amount = 0.0; // 493.00
|
||
double fee = 0.0; // 0.00
|
||
QString status; // "Успешный"
|
||
};
|
||
|
||
// Парсит детали транзакции
|
||
TransactionDetail parseTransactionDetail();
|
||
|
||
// Находит EditText по hint
|
||
// Парсит пары "Лейбл: " → "Значение" для экранов, где поля разложены по
|
||
// отдельным View-узлам в две колонки (формат с 3.2.33). Метод сканирует
|
||
// все ноды: лейблом считается контент, оканчивающийся на ':', значением —
|
||
// ближайший справа узел на той же Y-полосе. Возвращает map без двоеточий.
|
||
QMap<QString, QString> parseLabelValuePairs();
|
||
|
||
Node findEditTextByHint(const QString &hint);
|
||
|
||
// Поле ввода суммы на экране перевода. У EditText'а нет hint/content-desc
|
||
// (NAF), поэтому ищем по якорю — View "Мин. сумма: ..." под полем —
|
||
// и берём ближайший EditText выше якоря.
|
||
Node findSumEditText();
|
||
|
||
// Находит кнопку закрытия Google Play in-app update bottom sheet
|
||
// ("com.android.vending" оверлей "Доступно обновление"), если он показан
|
||
Node tryToFindGooglePlayBanner(int screenWidth, int screenHeight);
|
||
|
||
// Находит кнопку "ЗАПРЕТИТЬ" в системном диалоге запроса разрешения Android
|
||
// (com.google.android.permissioncontroller). Возвращает пустой Node если диалог не виден.
|
||
Node tryToFindSystemPermissionDenyButton();
|
||
|
||
private:
|
||
QList<Node> m_nodes;
|
||
QDomElement m_xml;
|
||
AppVersionTrack m_track = AppVersionTrack::V3_2_38;
|
||
};
|
||
|
||
} // namespace Dushanbe
|