1
0
forked from BRT/arc

Enable reliable activation of ADB Keyboard and improve fallback handling:

- Add comprehensive verification and logging for IME activation steps in `AdbUtils`.
- Update `PayByPhoneScript` to handle IME activation failures with error reporting.
- Improve edit text detection in `ScreenXmlParser` for devices with missing hint attributes.
This commit is contained in:
slava 2026-04-24 21:31:37 +07:00
parent f200eae2bd
commit ac8b5922ad
3 changed files with 84 additions and 12 deletions

View File

@ -338,6 +338,7 @@ bool AdbUtils::makeTap(const QString &deviceId, const int x, const int y) {
bool AdbUtils::enableAdbIMEKeyboard(const QString &deviceId) {
const QString adb = adbPath();
const QString imeId = QStringLiteral("com.android.adbkeyboard/.AdbIME");
// Проверяем, установлен ли ADB Keyboard
QString installedList;
@ -353,20 +354,63 @@ bool AdbUtils::enableAdbIMEKeyboard(const QString &deviceId) {
qDebug() << "[AdbUtils] ADB Keyboard installed successfully";
}
struct Step { QStringList args; };
Step steps[] = {
{{"-s", deviceId, "shell", "ime", "enable", "com.android.adbkeyboard/.AdbIME"}},
{{"-s", deviceId, "shell", "ime", "set", "com.android.adbkeyboard/.AdbIME"}}
};
// Пакет мог быть отключён производителем (pm disable-user).
// Пытаемся включить; ошибку не считаем фатальной (на некоторых прошивках команда
// отвергается, но сам IME при этом включается).
{
QString pmOut, pmErr;
if (!startProcess(adb, {"-s", deviceId, "shell", "pm", "enable", "com.android.adbkeyboard"}, &pmOut, &pmErr)) {
qWarning() << "[AdbUtils] pm enable com.android.adbkeyboard failed:" << pmErr.trimmed()
<< "(продолжаем)";
}
}
for (const auto &step : steps) {
QString stdErr;
if (!startProcess(adb, step.args, nullptr, &stdErr)) {
qWarning() << "ADB command failed:" << step.args.join(' ');
qWarning() << "Error:" << stdErr;
// ime enable
{
QString out, err;
if (!startProcess(adb, {"-s", deviceId, "shell", "ime", "enable", imeId}, &out, &err)) {
qWarning() << "[AdbUtils] ime enable failed:" << err.trimmed();
return false;
}
qDebug() << "[AdbUtils] ime enable:" << out.trimmed();
}
// Верификация: IME должен появиться в списке разрешённых
{
QString enabledList;
startProcess(adb, {"-s", deviceId, "shell", "ime", "list", "-s"}, &enabledList);
if (!enabledList.contains(imeId)) {
qWarning() << "[AdbUtils] ADB Keyboard отсутствует в 'ime list -s' после 'ime enable'."
<< "Вывод:" << enabledList.trimmed()
<< "(прошивка/OEM может блокировать сторонние IME)";
return false;
}
}
// ime set
{
QString out, err;
if (!startProcess(adb, {"-s", deviceId, "shell", "ime", "set", imeId}, &out, &err)) {
qWarning() << "[AdbUtils] ime set failed:" << err.trimmed();
return false;
}
qDebug() << "[AdbUtils] ime set:" << out.trimmed();
}
// Верификация: активный IME должен совпасть с нашим
{
QString defaultIme;
startProcess(adb, {"-s", deviceId, "shell", "settings", "get", "secure", "default_input_method"}, &defaultIme);
const QString trimmed = defaultIme.trimmed();
if (!trimmed.contains(imeId)) {
qWarning() << "[AdbUtils] default_input_method =" << trimmed
<< "— ожидали" << imeId
<< "(прошивка откатывает смену IME)";
return false;
}
qDebug() << "[AdbUtils] Active IME verified:" << trimmed;
}
return true;
}

View File

@ -315,7 +315,11 @@ void PayByPhoneScript::makePayment(
QThread::msleep(500);
// Ввод кириллицы через ADB Keyboard
AdbUtils::enableAdbIMEKeyboard(deviceId);
if (!AdbUtils::enableAdbIMEKeyboard(deviceId)) {
m_error = "Не удалось активировать ADB Keyboard (см. логи 'ime list -s' / default_input_method)";
qWarning() << "[PayByPhone]" << m_error;
return;
}
QThread::msleep(500);
AdbUtils::makeTap(deviceId, bankInput.x(), bankInput.y());
QThread::msleep(500);

View File

@ -149,7 +149,31 @@ Node ScreenXmlParser::findEditTextByHint(const QString &hintPrefix) {
return node;
}
}
return {};
// Некоторые устройства/прошивки (напр. Infinix) в uiautomator-дампе не отдают
// атрибут hint для EditText внутри WebView. В этом случае ищем TextView-label
// с нужным текстом и ближайший к нему EditText с горизонтальным перекрытием.
Node label;
for (const Node &node : m_nodes) {
if (node.className == "android.widget.TextView" && node.text.contains(hintPrefix)) {
label = node;
break;
}
}
if (label.isEmpty()) return {};
Node best;
int bestDy = INT_MAX;
for (const Node &node : m_nodes) {
if (node.className != "android.widget.EditText") continue;
const bool hOverlap = node.x1() < label.x2() && node.x2() > label.x1();
if (!hOverlap) continue;
const int dy = std::abs(node.y() - label.y());
if (dy < bestDy) {
bestDy = dy;
best = node;
}
}
return best;
}
Node ScreenXmlParser::findButtonNode(const QString &text, const bool contains) {