Improve GetProfileInfoScript with polling mechanism for balance loading. Add parseBalanceFromHomeScreenOpt to differentiate loading state from zero balance. Update ScreenXmlParser logic accordingly.
This commit is contained in:
parent
c5a6524e16
commit
34e80f04bb
@ -1,5 +1,8 @@
|
||||
#include "GetProfileInfoScript.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QRegularExpression>
|
||||
@ -55,19 +58,41 @@ void GetProfileInfoScript::doStart() {
|
||||
}
|
||||
|
||||
// Всегда тапаем кнопку обновления — баланс на экране может быть закэширован
|
||||
// и не отражать актуальное состояние счёта. Лоадера у refresh нет,
|
||||
// поэтому ждём фиксированно.
|
||||
// и не отражать актуальное состояние счёта. Лоадера у refresh нет, но во
|
||||
// время загрузки родительский блок временно становится content-desc="TJS"
|
||||
// (без даты, без числа) — это надёжный маркер «идёт обновление». Поэтому
|
||||
// после тапа поллим экран до появления распарсиваемого баланса.
|
||||
Node refreshBtn = xmlScreenParser.findRefreshButtonOnHomeScreen();
|
||||
std::optional<double> parsedOpt;
|
||||
if (refreshBtn.x() > 0) {
|
||||
qDebug() << "[Dushanbe::GetProfileInfo] Tapping refresh to get up-to-date balance";
|
||||
AdbUtils::makeTap(m_deviceId, refreshBtn.x(), refreshBtn.y());
|
||||
QThread::msleep(3000);
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
|
||||
constexpr int kPollIntervalMs = 500;
|
||||
constexpr int kPollTimeoutMs = 15000;
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
while (true) {
|
||||
QThread::msleep(kPollIntervalMs);
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
parsedOpt = xmlScreenParser.parseBalanceFromHomeScreenOpt();
|
||||
if (parsedOpt.has_value()) break;
|
||||
if (t.elapsed() >= kPollTimeoutMs) break;
|
||||
}
|
||||
|
||||
if (!parsedOpt.has_value()) {
|
||||
m_error = "Balance did not load after refresh: " + m_deviceId + " " + m_appCode;
|
||||
qWarning() << m_error;
|
||||
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
||||
emit finishedWithResult(m_error);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
qWarning() << "[Dushanbe::GetProfileInfo] Refresh button not found, parsing as-is";
|
||||
parsedOpt = xmlScreenParser.parseBalanceFromHomeScreenOpt();
|
||||
}
|
||||
|
||||
const double parsedBalance = xmlScreenParser.parseBalanceFromHomeScreen();
|
||||
const double parsedBalance = parsedOpt.value_or(0.0);
|
||||
qDebug() << "[Dushanbe::GetProfileInfo] Parsed balance after refresh:" << parsedBalance;
|
||||
if (parsedBalance <= 0.0) {
|
||||
m_error = "Balance is zero after refresh: " + m_deviceId + " " + m_appCode;
|
||||
|
||||
@ -185,10 +185,13 @@ bool ScreenXmlParser::isHomeScreen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
double ScreenXmlParser::parseBalanceFromHomeScreen() {
|
||||
std::optional<double> ScreenXmlParser::parseBalanceFromHomeScreenOpt() {
|
||||
// На домашнем экране: parent node content-desc="TJS\n03.04.26 - 21:46:15"
|
||||
// внутри него child node content-desc="0.00" (или "10.00")
|
||||
// Стратегия: находим индекс parent, затем child сразу после него с числовым content-desc
|
||||
// Стратегия: находим индекс parent, затем child сразу после него с числовым content-desc.
|
||||
// Во время refresh-загрузки parent временно превращается в content-desc="TJS" (без даты)
|
||||
// и числового child нет — в этом случае возвращаем nullopt, чтобы вызывающий
|
||||
// код мог отличить состояние загрузки от реального нулевого баланса.
|
||||
for (int i = 0; i < m_nodes.size(); ++i) {
|
||||
if (!m_nodes[i].contentDesc.startsWith("TJS")) continue;
|
||||
// Проверяем что это блок с датой (не просто "TJS" текст)
|
||||
@ -205,7 +208,11 @@ double ScreenXmlParser::parseBalanceFromHomeScreen() {
|
||||
if (ok) return val;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
double ScreenXmlParser::parseBalanceFromHomeScreen() {
|
||||
return parseBalanceFromHomeScreenOpt().value_or(0.0);
|
||||
}
|
||||
|
||||
QString ScreenXmlParser::parseUserNameFromHomeScreen() {
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QDomElement>
|
||||
@ -37,9 +39,20 @@ public:
|
||||
|
||||
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();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user