#include "ScreenXmlParser.h" #include #include #include #include "android/xml/Node.h" namespace Black { static const QRegularExpression boundsRegex(R"(\[([0-9]+),([0-9]+)\]\[([0-9]+),([0-9]+)\])"); ScreenXmlParser::ScreenXmlParser(QObject *parent) : QObject(parent) { } ScreenXmlParser::~ScreenXmlParser() = default; void ScreenXmlParser::parseAndSaveXml(const QString &xml) { QDomDocument doc; doc.setContent(xml); m_xml = doc.documentElement(); m_nodes.clear(); QXmlStreamReader reader(xml); while (!reader.atEnd()) { reader.readNext(); if (reader.isStartElement() && reader.name() == u"node") { Node node; if (reader.attributes().hasAttribute("bounds")) { QString bounds = reader.attributes().value("bounds").toString(); if (QRegularExpressionMatch match = boundsRegex.match(bounds); match.hasMatch()) { node.setBounds( match.captured(1).toInt(), match.captured(2).toInt(), match.captured(3).toInt(), match.captured(4).toInt() ); } } if (reader.attributes().hasAttribute("text")) { node.text = reader.attributes().value("text").toString(); } if (reader.attributes().hasAttribute("content-desc")) { node.contentDesc = reader.attributes().value("content-desc").toString(); } if (reader.attributes().hasAttribute("class")) { node.className = reader.attributes().value("class").toString(); } if (reader.attributes().hasAttribute("clickable")) { node.clickable = reader.attributes().value("clickable").toString() == "true"; } if (reader.attributes().hasAttribute("focusable")) { node.focusable = reader.attributes().value("focusable").toString() == "true"; } if (reader.attributes().hasAttribute("resource-id")) { node.resourceId = reader.attributes().value("resource-id").toString(); } if (reader.attributes().hasAttribute("hint")) { node.hint = reader.attributes().value("hint").toString(); } if (reader.attributes().hasAttribute("password")) { node.password = reader.attributes().value("password").toString() == "true"; } m_nodes.append(node); } } } const QList &ScreenXmlParser::findAllNodes() const { return m_nodes; } Node ScreenXmlParser::findTextNode(const QString &text) { for (const Node &node : m_nodes) { if (node.text.trimmed() == text) { return node; } } return {}; } Node ScreenXmlParser::findNodeByContentDesc(const QString &contentDesc) { for (const Node &node : m_nodes) { if (node.contentDesc == contentDesc) { return node; } } return {}; } Node ScreenXmlParser::findNodeByContentDesc(const QString &contentDesc, bool contains) { for (const Node &node : m_nodes) { if (contains) { if (node.contentDesc.contains(contentDesc)) { return node; } } else { if (node.contentDesc == contentDesc) { return node; } } } return {}; } Node ScreenXmlParser::findButtonNode(const QString &text, bool contains) { for (const Node &node : m_nodes) { if (!node.clickable) continue; if (contains) { if (node.text.contains(text) || node.contentDesc.contains(text)) { return node; } } else { if (node.text.trimmed() == text || node.contentDesc == text) { return node; } } } return {}; } Node ScreenXmlParser::findFirstEditText() { for (const Node &node : m_nodes) { if (node.className == "android.widget.EditText") { return node; } } return {}; } bool ScreenXmlParser::isPinCodeScreen() { for (const Node &node : m_nodes) { if (node.contentDesc.contains(QString::fromUtf8("¿Olvidaste tu PIN?"))) { return true; } if (node.contentDesc.contains(QString::fromUtf8("Ingresá tu PIN"))) { return true; } } return false; } Node ScreenXmlParser::findPinCodeEditText() { for (const Node &node : m_nodes) { if (node.className == "android.widget.EditText" && node.password) { return node; } } return {}; } bool ScreenXmlParser::isHomeScreen() { bool hasSaldo = false; bool hasInicio = false; for (const Node &node : m_nodes) { if (node.contentDesc == "Saldo disponible") { hasSaldo = true; } if (node.contentDesc == "Inicio") { hasInicio = true; } if (hasSaldo && hasInicio) { return true; } } return false; } bool ScreenXmlParser::isSideMenu() { bool hasTuInfo = false; bool hasCerrar = false; for (const Node &node : m_nodes) { if (node.contentDesc == QString::fromUtf8("Tu Información")) { hasTuInfo = true; } if (node.contentDesc == QString::fromUtf8("Cerrar sesión")) { hasCerrar = true; } if (hasTuInfo && hasCerrar) { return true; } } return false; } Node ScreenXmlParser::findHolaButton() { for (const Node &node : m_nodes) { if (node.clickable && node.contentDesc.startsWith(QString::fromUtf8("¡Hola!"))) { return node; } } return {}; } QString ScreenXmlParser::parseUserNameFromHomeScreen() { // content-desc: "¡Hola!\nveronica cintia heredia \nvc" for (const Node &node : m_nodes) { if (node.contentDesc.startsWith(QString::fromUtf8("¡Hola!"))) { QStringList lines = node.contentDesc.split('\n'); if (lines.size() >= 2) { return lines[1].trimmed(); } } } return {}; } double ScreenXmlParser::parseBalanceFromHomeScreen() { // Баланс идёт после "Saldo disponible", формат: "$1.857,00" или "$0,00" bool afterSaldo = false; for (const Node &node : m_nodes) { if (node.contentDesc == "Saldo disponible") { afterSaldo = true; continue; } if (afterSaldo && node.contentDesc.startsWith("$")) { // "$1.857,00" → "1857.00" QString raw = node.contentDesc.mid(1); // убираем "$" raw.remove('.'); // убираем разделитель тысяч raw.replace(',', '.'); // запятая → точка bool ok = false; double val = raw.toDouble(&ok); if (ok) return val; } } return 0.0; } bool ScreenXmlParser::isProfilePage() { for (const Node &node : m_nodes) { if (node.contentDesc == QString::fromUtf8("Tu información")) { return true; } } return false; } QString ScreenXmlParser::parseProfileNombre() { for (const Node &node : m_nodes) { if (node.hint == "Nombre" && !node.text.isEmpty()) { return node.text.trimmed(); } } return {}; } QString ScreenXmlParser::parseProfileApellido() { for (const Node &node : m_nodes) { if (node.hint == "Apellido" && !node.text.isEmpty()) { return node.text.trimmed(); } } return {}; } QString ScreenXmlParser::parseProfileEmail() { for (const Node &node : m_nodes) { if (node.hint == "E-mail" && !node.text.isEmpty()) { return node.text.trimmed(); } } return {}; } QString ScreenXmlParser::parseProfilePhone() { for (const Node &node : m_nodes) { if (node.hint == QString::fromUtf8("Teléfono celular") && !node.text.isEmpty()) { return node.text.trimmed(); } } return {}; } bool ScreenXmlParser::isCVUScreen() { for (const Node &node : m_nodes) { if (node.contentDesc == "CVU") { return true; } } return false; } QString ScreenXmlParser::parseCVUNumber() { // CVU номер идёт после content-desc="CVU", это 22-значный номер bool afterCVU = false; for (const Node &node : m_nodes) { if (node.contentDesc == "CVU") { afterCVU = true; continue; } if (afterCVU && !node.contentDesc.isEmpty()) { // Проверяем что это числовая строка (CVU — 22 цифры) QString cvu = node.contentDesc.trimmed(); bool allDigits = true; for (const QChar &c : cvu) { if (!c.isDigit()) { allDigits = false; break; } } if (allDigits && !cvu.isEmpty()) { return cvu; } } } return {}; } } // namespace Black