From 2c2d58021fa1e5dc272c7be99a1431784d611ad8 Mon Sep 17 00:00:00 2001 From: slava Date: Mon, 8 Jun 2026 21:06:00 +0500 Subject: [PATCH] Force portrait orientation on connected devices, remove `x100` heuristic from transaction matching, and add secondary text search filter in monitoring app. --- android/adb/AdbUtils.cpp | 27 +++++++++++++++++++ android/adb/AdbUtils.h | 7 +++++ .../dushanbe/GetLastTransactionsScript.cpp | 14 +++++----- monitoring/app.py | 17 +++++++----- services/EventHandler.cpp | 14 ++++++++++ 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/android/adb/AdbUtils.cpp b/android/adb/AdbUtils.cpp index 3b697bd..9aa7c52 100644 --- a/android/adb/AdbUtils.cpp +++ b/android/adb/AdbUtils.cpp @@ -816,6 +816,33 @@ QString AdbUtils::getForegroundPackage(const QString &deviceId) { return {}; } +bool AdbUtils::ensurePortraitOrientation(const QString &deviceId) { + if (deviceId.isEmpty()) { + return false; + } + const QString adb = adbPath(); + QString out; + if (!startProcess(adb, {"-s", deviceId, "shell", "dumpsys", "input"}, &out)) { + return false; // adb недоступен — пропускаем (best-effort) + } + + // SurfaceOrientation: 0=0°(portrait) 1=90°(landscape) 2=180° 3=270°(landscape) + static const QRegularExpression re(R"(SurfaceOrientation:\s*(\d))"); + const QRegularExpressionMatch m = re.match(out); + const int orient = m.hasMatch() ? m.captured(1).toInt() : 0; + + if (orient == 1 || orient == 3) { + qInfo() << "[AdbUtils] device" << deviceId + << "in landscape (orient" << orient << ") → forcing portrait"; + // Автоповорот выключаем, иначе сенсор вернёт landscape. + startProcess(adb, {"-s", deviceId, "shell", "settings", "put", + "system", "accelerometer_rotation", "0"}); + startProcess(adb, {"-s", deviceId, "shell", "settings", "put", + "system", "user_rotation", "0"}); + } + return true; +} + void AdbUtils::unlockScreen(const QString &deviceId, const int screenWidth, const int screenHeight) { const QString adb = adbPath(); diff --git a/android/adb/AdbUtils.h b/android/adb/AdbUtils.h index 52dd923..8113c49 100644 --- a/android/adb/AdbUtils.h +++ b/android/adb/AdbUtils.h @@ -62,6 +62,13 @@ public: // Возвращает пакет приложения на переднем плане (foreground), либо пустую строку при ошибке static QString getForegroundPackage(const QString &deviceId); + // Проверяет реальную ориентацию экрана (dumpsys input → SurfaceOrientation). + // Если экран в горизонтальной ориентации (landscape, 90°/270°), принудительно + // ставит вертикальную: выключает автоповорот и выставляет user_rotation=0. + // Best-effort: при ошибке adb просто ничего не делает. Возвращает true, если + // экран в портрете (изначально или после форсирования). + static bool ensurePortraitOrientation(const QString &deviceId); + private: static QString adbDir(); static bool startProcess(const QString &program, const QStringList &args, diff --git a/android/dushanbe/GetLastTransactionsScript.cpp b/android/dushanbe/GetLastTransactionsScript.cpp index 18a46f0..439857e 100644 --- a/android/dushanbe/GetLastTransactionsScript.cpp +++ b/android/dushanbe/GetLastTransactionsScript.cpp @@ -719,14 +719,16 @@ void GetLastTransactionsScript::searchAll( const double absListAmt = qAbs(tx.amount); // Выбираем подходящую цель: совпадение по сумме + пред-фильтры даты/времени. + // Список Выписки, экран деталей и поисковый таргет — все в мажорных + // единицах (TJS, 2 знака), поэтому сверяем сумму напрямую. Эвристик + // "x100" убран: он ложно матчил реальную транзакцию на 1.00 к поиску + // на 100.00 (1.00*100==100.00) и затем домножал результат до 10000. int matchIdx = -1; - bool matchedX100 = false; for (int ti = 0; ti < pending.size(); ++ti) { const double searchAbs = qAbs(pending[ti].first); const QDateTime &cand = pending[ti].second; const bool amountDirect = qAbs(absListAmt - searchAbs) < 0.01; - const bool amountX100 = qAbs(absListAmt * 100.0 - searchAbs) < 0.01; - if (!amountDirect && !amountX100) continue; + if (!amountDirect) continue; // Pre-filter по ДАТЕ: только если дата надёжная (из content-desc P2P формата). // DCWallet транзакции получают дату из groupDates ImageView, которая @@ -753,14 +755,12 @@ void GetLastTransactionsScript::searchAll( } matchIdx = ti; - matchedX100 = amountX100 && !amountDirect; break; } if (matchIdx < 0) continue; const QDateTime searchTime = pending[matchIdx].second; qDebug() << "[Dushanbe::GetLastTransactions] Amount match:" << tx.amount - << (matchedX100 ? "(scaled x100)" : "(direct)") << "date:" << tx.date << "time:" << tx.time << "→ target idx" << matchIdx; @@ -947,8 +947,8 @@ void GetLastTransactionsScript::searchAll( // Формируем task_result напрямую из распарсенных данных и отдаём // EventHandler через сигнал — без записи/чтения БД. { - const double finalAmount = matchedX100 ? detail.amount * 100.0 : detail.amount; - const double finalFee = matchedX100 ? detail.fee * 100.0 : detail.fee; + const double finalAmount = detail.amount; + const double finalFee = detail.fee; QDateTime txTime = QDateTime::fromString( detail.date + " " + detail.time, "dd.MM.yyyy HH:mm:ss"); diff --git a/monitoring/app.py b/monitoring/app.py index f3ee481..4825d91 100644 --- a/monitoring/app.py +++ b/monitoring/app.py @@ -982,7 +982,7 @@ def admin_logout() -> Response: return resp -def _query_entries(kind, type_, event, q, date_from, date_to, page): +def _query_entries(kind, type_, event, q, date_from, date_to, page, q2=""): where, params = [], [] if kind: where.append("kind=?"); params.append(kind) @@ -992,6 +992,8 @@ def _query_entries(kind, type_, event, q, date_from, date_to, page): where.append("event LIKE ?"); params.append(f"%{event}%") if q: where.append("message LIKE ?"); params.append(f"%{q}%") + if q2: + where.append("message LIKE ?"); params.append(f"%{q2}%") df = _parse_date(date_from) if df is not None: where.append("ts>=?"); params.append(df) @@ -1096,6 +1098,7 @@ def admin_dashboard( type: str = "", event: str = "", q: str = "", + q2: str = "", date_from: str = "", date_to: str = "", page: int = 0, @@ -1103,11 +1106,11 @@ def admin_dashboard( if not _is_authed(request): return RedirectResponse("/admin/login", status_code=303) - rows, total, types = _query_entries(kind, type, event, q, date_from, date_to, page) + rows, total, types = _query_entries(kind, type, event, q, date_from, date_to, page, q2) page = max(0, page) current_url = (f"/admin?kind={kind}&type={html_lib.escape(type)}&event={html_lib.escape(event)}" - f"&q={html_lib.escape(q)}&date_from={date_from}&date_to={date_to}&page={page}") + f"&q={html_lib.escape(q)}&q2={html_lib.escape(q2)}&date_from={date_from}&date_to={date_to}&page={page}") trs = _build_entry_trs(rows, current_url) @@ -1116,7 +1119,7 @@ def admin_dashboard( def link(p: int) -> str: qs = (f"kind={kind}&type={html_lib.escape(type)}&event={html_lib.escape(event)}" - f"&q={html_lib.escape(q)}&date_from={date_from}&date_to={date_to}&page={p}") + f"&q={html_lib.escape(q)}&q2={html_lib.escape(q2)}&date_from={date_from}&date_to={date_to}&page={p}") return f"/admin?{qs}" shown_from = page * PAGE_SIZE + 1 if total else 0 @@ -1133,6 +1136,7 @@ def admin_dashboard( + @@ -1219,6 +1223,7 @@ def admin_entries_fragment( type: str = "", event: str = "", q: str = "", + q2: str = "", date_from: str = "", date_to: str = "", page: int = 0, @@ -1227,8 +1232,8 @@ def admin_entries_fragment( raise HTTPException(status_code=401, detail="auth required") page = max(0, page) current_url = (f"/admin?kind={kind}&type={html_lib.escape(type)}&event={html_lib.escape(event)}" - f"&q={html_lib.escape(q)}&date_from={date_from}&date_to={date_to}&page={page}") - rows, total, _ = _query_entries(kind, type, event, q, date_from, date_to, page) + f"&q={html_lib.escape(q)}&q2={html_lib.escape(q2)}&date_from={date_from}&date_to={date_to}&page={page}") + rows, total, _ = _query_entries(kind, type, event, q, date_from, date_to, page, q2) trs = _build_entry_trs(rows, current_url) return JSONResponse({"rows_html": "".join(trs), "total": total}) diff --git a/services/EventHandler.cpp b/services/EventHandler.cpp index c862f9f..4b5f104 100644 --- a/services/EventHandler.cpp +++ b/services/EventHandler.cpp @@ -719,6 +719,20 @@ void EventHandler::startThreadForTask(const MaterialInfo &account, const EventIn const auto thr = new QThread(this); const QString key = account.deviceId; + // Перед запуском любой задачи гарантируем вертикальную ориентацию экрана: + // если телефон в landscape — принудительно ставим portrait. Выполняется в + // рабочем потоке (functor без context → исполняется в потоке-эмиттере thr), + // поэтому adb-I/O не блокирует поток диспетчера. Коннект добавлен до setup(), + // значит сработает раньше, чем worker::start → doStart(). + const QString canonicalDeviceId = account.deviceId; + connect(thr, &QThread::started, [canonicalDeviceId]() { + DeviceInfo dev = DeviceDAO::getDeviceById(canonicalDeviceId); + if (dev.id.isEmpty()) dev = DeviceDAO::findByAdbSerial(canonicalDeviceId); + const QString serial = dev.adbSerial.isEmpty() ? canonicalDeviceId + : dev.adbSerial; + AdbUtils::ensurePortraitOrientation(serial); + }); + // Generic helper: подключает сигналы и переносит worker в поток auto setup = [&](auto *worker) { using W = std::remove_pointer_t;