diff --git a/android/ozon/ScreenXmlParser.cpp b/android/ozon/ScreenXmlParser.cpp index f0f5951..0c71a85 100644 --- a/android/ozon/ScreenXmlParser.cpp +++ b/android/ozon/ScreenXmlParser.cpp @@ -398,12 +398,14 @@ QString ScreenXmlParser::parseProfilePhone() { QList ScreenXmlParser::findPinCodeNodes(const QString &pinCode) { // Способ 1: ищем кликабельные ноды с текстом-цифрой (0-9) + // Поддерживаем форматы: "1", "key 1", и т.п. QMap digitMap; + static const QRegularExpression digitRx(R"(^(?:key\s+)?(\d)$)", QRegularExpression::CaseInsensitiveOption); for (const Node &node : m_nodes) { if (!node.clickable || node.width() <= 0 || node.height() <= 0) continue; const QString t = node.text.trimmed(); - if (t.size() == 1 && t.at(0).isDigit()) { - digitMap[t] = node; + if (const auto match = digitRx.match(t); match.hasMatch()) { + digitMap[match.captured(1)] = node; } } @@ -421,42 +423,62 @@ QList ScreenXmlParser::findPinCodeNodes(const QString &pinCode) { return result; } - // Способ 2 (fallback): группируем clickable-ноды одинакового размера, + // Способ 2 (fallback): группируем clickable-ноды похожего размера, // ищем сетку 4x3 по паттерну (не привязываясь к координатам экрана) - QMap, QList> sizeGroups; + constexpr int SIZE_TOLERANCE = 10; // допуск в пикселях + + // Собираем все clickable ноды с положительным размером + QList allClickable; for (const Node &node : m_nodes) { if (!node.clickable || node.width() <= 0 || node.height() <= 0) continue; - if (node.width() == node.height()) { // кнопки пин-пада квадратные - sizeGroups[{node.width(), node.height()}].append(node); - } + allClickable.append(node); } - // Ищем группу из >= 12 одинаковых кнопок (пин-пад: 3x4 = 12) - QList candidates; - for (auto it = sizeGroups.begin(); it != sizeGroups.end(); ++it) { - if (it.value().size() >= 12 && - (candidates.isEmpty() || it.value().size() < candidates.size())) { - candidates = it.value(); - } - } + // Группируем с допуском: для каждой ноды проверяем, подходит ли она к существующей группе + auto fuzzyGroup = [&](bool squareOnly) -> QList { + QList> groups; + QList> groupSizes; // эталонный размер каждой группы - if (candidates.size() < 12) { - // Попробуем без ограничения на квадрат, но с >= 12 - sizeGroups.clear(); - for (const Node &node : m_nodes) { - if (!node.clickable || node.width() <= 0 || node.height() <= 0) continue; - sizeGroups[{node.width(), node.height()}].append(node); - } - for (auto it = sizeGroups.begin(); it != sizeGroups.end(); ++it) { - if (it.value().size() >= 12 && - (candidates.size() < 12 || it.value().size() < candidates.size())) { - candidates = it.value(); + for (const Node &node : allClickable) { + if (squareOnly && std::abs(node.width() - node.height()) > SIZE_TOLERANCE) + continue; + bool placed = false; + for (int g = 0; g < groups.size(); ++g) { + if (std::abs(node.width() - groupSizes[g].first) <= SIZE_TOLERANCE && + std::abs(node.height() - groupSizes[g].second) <= SIZE_TOLERANCE) { + groups[g].append(node); + placed = true; + break; + } + } + if (!placed) { + groups.append({node}); + groupSizes.append({node.width(), node.height()}); } } - } + + QList best; + for (const auto &grp : groups) { + if (grp.size() >= 12 && (best.size() < 12 || grp.size() < best.size())) + best = grp; + } + return best; + }; + + // Сначала квадратные, потом любые + QList candidates = fuzzyGroup(true); + if (candidates.size() < 12) + candidates = fuzzyGroup(false); if (candidates.size() < 12) { - qWarning() << "[findPinCodeNodes] grid not found: no group of 12+ same-sized clickable nodes"; + // Дебаг: выводим все clickable ноды и их размеры + qWarning() << "[findPinCodeNodes] grid not found. Total clickable nodes:" << allClickable.size(); + for (const Node &n : allClickable) { + qDebug() << " clickable:" << n.width() << "x" << n.height() + << "pos:" << n.x() << "," << n.y() + << "class:" << n.className + << "text:" << n.text.left(30); + } return {}; }