From 1baa2d85bda8079a4194496a05daf9a1dcbe8801 Mon Sep 17 00:00:00 2001 From: dranik Date: Wed, 13 May 2026 11:56:58 +0300 Subject: [PATCH] remove dead code --- client/CMakeLists.txt | 4 - client/amneziaApplication.cpp | 4 - .../org/amnezia/vpn/PairingQrScanGeometry.kt | 88 ------------------- .../controllers/api/pairingController.cpp | 50 ----------- .../core/controllers/api/pairingController.h | 4 - .../controllers/api/pairingUiController.cpp | 5 -- .../ui/controllers/api/pairingUiController.h | 35 -------- .../Pages2/PageSettingsApiQrPairingSend.qml | 84 +----------------- client/ui/qml/Pages2/PageStart.qml | 74 ---------------- 9 files changed, 1 insertion(+), 347 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 2b4dc98eb..ea61ea82e 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -211,10 +211,6 @@ if(AMNEZIA_QR_PAIRING_ALLOW) target_compile_definitions(${PROJECT} PRIVATE AMNEZIA_QR_PAIRING_ALLOW) endif() -if(AMNEZIA_QR_PAIRING_ALLOW_DUPLICATE_VPN_KEY) - target_compile_definitions(${PROJECT} PRIVATE AMNEZIA_QR_PAIRING_ALLOW_DUPLICATE_VPN_KEY) -endif() - target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC}) # Finalize the executable so Qt can gather/deploy QML modules and plugins correctly (Android needs this). diff --git a/client/amneziaApplication.cpp b/client/amneziaApplication.cpp index 47befffe3..1fa2c6fae 100644 --- a/client/amneziaApplication.cpp +++ b/client/amneziaApplication.cpp @@ -254,10 +254,6 @@ bool AmneziaApplication::parseCommands() #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(MACOS_NE) void AmneziaApplication::startLocalServer() { -#ifdef AMNEZIA_QR_PAIRING_ALLOW - return; -#endif - const QString serverName("AmneziaVPNInstance"); QLocalServer::removeServer(serverName); diff --git a/client/android/src/org/amnezia/vpn/PairingQrScanGeometry.kt b/client/android/src/org/amnezia/vpn/PairingQrScanGeometry.kt index a3e438d24..b3cb8db74 100644 --- a/client/android/src/org/amnezia/vpn/PairingQrScanGeometry.kt +++ b/client/android/src/org/amnezia/vpn/PairingQrScanGeometry.kt @@ -7,40 +7,7 @@ import kotlin.math.floor import kotlin.math.max import kotlin.math.min -/** - * Same proportions as [PageSettingsApiQrPairingSend.qml] (iOS embedded scan): sq = 0.72 * min(w,h), - * vertical bias -0.06 * height (square shifted up slightly). - */ object PairingQrScanGeometry { - const val SQ_FRACTION = 0.72f - const val VERTICAL_BIAS = 0.06f - - fun holeRectF(viewW: Int, viewH: Int): RectF { - val w = viewW.toFloat() - val h = viewH.toFloat() - val side = min(w, h) * SQ_FRACTION - val left = (w - side) / 2f - var top = (h - side) / 2f - h * VERTICAL_BIAS - top = max(0f, top) - var bottom = top + side - if (bottom > h) { - bottom = h - } - val adjSide = bottom - top - return RectF(left, top, left + adjSide, bottom) - } - - /** ML Kit [Barcode] box is in [InputImage] pixel space (same as analysis frame WxH). */ - fun barcodeCenterInPairingHole(imageW: Int, imageH: Int, barcode: Barcode): Boolean { - val box = barcode.boundingBox ?: return true - val r = holeRectF(imageW, imageH) - return r.contains(box.centerX().toFloat(), box.centerY().toFloat()) - } - - /** - * Maps a rectangle in [PreviewView] / overlay pixel space to [InputImage] pixel space, - * assuming the preview uses default FILL_CENTER scaling (same as typical CameraX [PreviewView]). - */ fun viewRectToInputImageRectFillCenter( viewW: Int, viewH: Int, @@ -59,16 +26,6 @@ object PairingQrScanGeometry { ) } - /** Pairing hole (same geometry as overlay) expressed in ML Kit / [InputImage] coordinates. */ - fun pairingHoleInImageCoords(viewW: Int, viewH: Int, imageW: Int, imageH: Int): RectF { - val holeView = holeRectF(viewW, viewH) - return viewRectToInputImageRectFillCenter(viewW, viewH, imageW, imageH, holeView) - } - - /** - * Rounded scan window corner radius — same formula as iOS `layoutScanOverlayGeometry` (`holeR`). - * [sidePx] is the scan square side in pixels. - */ fun pairingIosStyleHoleCornerRadiusPx(sidePx: Float, density: Float): Float { val d = density var holeR = min(28f * d, max(10f * d, sidePx * 0.056f)) @@ -77,7 +34,6 @@ object PairingQrScanGeometry { return max(holeR, 1f) } - /** Area(roi ∩ box) / area(box); 0 if disjoint. */ fun barcodeBoxOverlapFraction(roi: RectF, box: Rect): Float { val bf = RectF(box) val inter = RectF(roi) @@ -87,40 +43,6 @@ object PairingQrScanGeometry { return if (boxArea <= 0f) 0f else interArea / boxArea } - /** - * Accept only codes whose bounding box overlaps the on-screen pairing square by at least - * [minOverlapFraction] when that square is mapped into image space (preview FILL_CENTER model). - */ - fun barcodeMostlyInsidePairingHole( - viewW: Int, - viewH: Int, - imageW: Int, - imageH: Int, - barcode: Barcode, - minOverlapFraction: Float = 0.82f - ): Boolean { - val box = barcode.boundingBox ?: return true - if (viewW <= 0 || viewH <= 0 || imageW <= 0 || imageH <= 0) { - return barcodeCenterInPairingHole(imageW, imageH, barcode) - } - val roi = pairingHoleInImageCoords(viewW, viewH, imageW, imageH) - val inset = 0.02f * min(imageW, imageH) - roi.inset(inset, inset) - if (roi.width() <= 0f || roi.height() <= 0f) { - return barcodeCenterInPairingHole(imageW, imageH, barcode) - } - return barcodeBoxOverlapFraction(roi, box) >= minOverlapFraction - } - - /** - * Pairing send: accept only if the QR lies fully inside the on-screen square. - * [roiInImageSpace] is [holeRectF] in [PreviewView] coords mapped into the same space as ML Kit - * geometry ([CoordinateTransform] in [CameraActivity]). - * - * When [Barcode.getCornerPoints] is present (typical for QR), all corners must lie inside the ROI — - * tighter than [BoundingBox], which is often padded. - * Otherwise falls back to bbox center inside ROI plus [minOverlapFraction] of bbox area inside ROI. - */ fun barcodeMatchesPairingHole( roiInImageSpace: RectF, imageW: Int, @@ -161,14 +83,8 @@ object PairingQrScanGeometry { return barcodeBoxOverlapFraction(roi, box) >= minOverlapFraction } - /** Bbox-only fallback when corner points are missing (unusual for QR). */ private const val PAIRING_SEND_MIN_OVERLAP_BBOX_FALLBACK = 0.72f - /** - * Native pairing scan hole — same rules as iOS `layoutScanOverlayGeometry` in - * `iosPairingQrOverlayWindow.mm` (0.72 × min side, header / bottom band clamps). - * [headerBottomPx] is the bottom edge of the chrome row in this view’s coordinate system. - */ fun pairingIosStyleHoleRectF( viewW: Int, viewH: Int, @@ -201,9 +117,6 @@ object PairingQrScanGeometry { return RectF(sqX, sqY, sqX + sqSz, sqY + sqSz) } - /** - * Vertical center of the torch control in px (same math as iOS `torchCenterYConstraint` update). - */ fun pairingIosStyleTorchCenterYPx( holeBottomPx: Float, bandBottomPx: Float, @@ -224,7 +137,6 @@ object PairingQrScanGeometry { return max(torchCy, hdr) } - /** [pairingIosStyleHoleRectF] mapped with the legacy FILL_CENTER preview model (transform fallback). */ fun pairingIosStyleHoleInImageCoords( viewW: Int, viewH: Int, diff --git a/client/core/controllers/api/pairingController.cpp b/client/core/controllers/api/pairingController.cpp index 4dc9e1b71..badfca753 100644 --- a/client/core/controllers/api/pairingController.cpp +++ b/client/core/controllers/api/pairingController.cpp @@ -2,7 +2,6 @@ #include #include -#include "core/controllers/gatewayController.h" #include "core/repositories/secureAppSettingsRepository.h" #include "core/utils/api/apiUtils.h" #include "core/utils/constants/apiConstants.h" @@ -13,8 +12,6 @@ using namespace amnezia; namespace { -constexpr auto kGenerateQrEndpoint = "%1api/v1/generate_qr"; -constexpr auto kScanQrEndpoint = "%1api/v1/scan_qr"; constexpr qsizetype kPairingMaxQrUuidChars = 128; constexpr qsizetype kPairingMaxVpnConfigChars = 256 * 1024; constexpr qsizetype kPairingMaxApiKeyChars = 8192; @@ -186,50 +183,3 @@ QJsonObject PairingController::buildScanQrPayload(const QString &qrUuid, const Q o[apiDefs::key::osVersion] = QSysInfo::productType(); return o; } - -ErrorCode PairingController::startPairing(const QString &qrUuid, QrPairingConfigPayload &outPayload) -{ - outPayload = QrPairingConfigPayload {}; - if (qrUuid.isEmpty()) { - return ErrorCode::ApiConfigEmptyError; - } - - GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(), m_appSettingsRepository->isDevGatewayEnv(), - pairingLongPollTimeoutMsecs(), m_appSettingsRepository->isStrictKillSwitchEnabled()); - - QByteArray responseBody; - const ErrorCode transportError = gatewayController.post(QString::fromLatin1(kGenerateQrEndpoint), buildGenerateQrPayload(qrUuid), responseBody); - if (transportError != ErrorCode::NoError) { - return transportError; - } - - const QJsonObject obj = QJsonDocument::fromJson(responseBody).object(); - return interpretGenerateQrJson(obj, outPayload); -} - -ErrorCode PairingController::completePairing(const QString &qrUuid, const QString &vpnConfig, const QJsonObject &serviceInfo, - const QJsonArray &supportedProtocols, const QString &apiKey) -{ - if (qrUuid.isEmpty() || vpnConfig.isEmpty() || apiKey.isEmpty()) { - return ErrorCode::ApiConfigEmptyError; - } - - const ErrorCode fieldErr = validatePairingScanFields(qrUuid, vpnConfig, apiKey); - if (fieldErr != ErrorCode::NoError) { - return fieldErr; - } - - GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(), m_appSettingsRepository->isDevGatewayEnv(), - apiDefs::requestTimeoutMsecs, m_appSettingsRepository->isStrictKillSwitchEnabled()); - - QByteArray responseBody; - const ErrorCode transportError = - gatewayController.post(QString::fromLatin1(kScanQrEndpoint), - buildScanQrPayload(qrUuid, vpnConfig, serviceInfo, supportedProtocols, apiKey), responseBody); - if (transportError != ErrorCode::NoError) { - return transportError; - } - - const QJsonObject obj = QJsonDocument::fromJson(responseBody).object(); - return interpretScanQrJson(obj); -} diff --git a/client/core/controllers/api/pairingController.h b/client/core/controllers/api/pairingController.h index 66d65c7c3..957192f37 100644 --- a/client/core/controllers/api/pairingController.h +++ b/client/core/controllers/api/pairingController.h @@ -37,10 +37,6 @@ public: /** Length bounds before `scan_qr` (avoids huge JSON / abuse). */ static amnezia::ErrorCode validatePairingScanFields(const QString &qrUuid, const QString &vpnConfig, const QString &apiKey); - amnezia::ErrorCode startPairing(const QString &qrUuid, QrPairingConfigPayload &outPayload); - amnezia::ErrorCode completePairing(const QString &qrUuid, const QString &vpnConfig, const QJsonObject &serviceInfo, - const QJsonArray &supportedProtocols, const QString &apiKey); - private: SecureAppSettingsRepository *m_appSettingsRepository; }; diff --git a/client/ui/controllers/api/pairingUiController.cpp b/client/ui/controllers/api/pairingUiController.cpp index 1e34c2f4d..bcf43a9e2 100644 --- a/client/ui/controllers/api/pairingUiController.cpp +++ b/client/ui/controllers/api/pairingUiController.cpp @@ -55,7 +55,6 @@ int pairingRetryDelayMs(int zeroBasedAttempt) return baseMs * (1 << zeroBasedAttempt); } -/** Legacy TV QR: generateQrCodeImageSeries base64url-wrapped QDataStream chunk (see qrCodeUtils.cpp). */ bool tryDecodeLegacyChunkedPairingQrPayload(const QString &t, QString *outUuid) { static const QRegularExpression binUrlSafe(QStringLiteral("^[A-Za-z0-9_-]+$")); @@ -99,10 +98,6 @@ bool tryDecodeLegacyChunkedPairingQrPayload(const QString &t, QString *outUuid) return true; } -/** - * Extract a pairing session UUID from raw QR text without touching QObject / signals. - * Safe from CameraX / JNI threads while AmneziaActivity is stopped (Qt event loop may not run). - */ QString extractPairingSessionUuidFromScanText(const QString &raw) { const QString t = raw.trimmed(); diff --git a/client/ui/controllers/api/pairingUiController.h b/client/ui/controllers/api/pairingUiController.h index 66e726608..f77cc3060 100644 --- a/client/ui/controllers/api/pairingUiController.h +++ b/client/ui/controllers/api/pairingUiController.h @@ -24,7 +24,6 @@ class PairingUiController : public QObject Q_PROPERTY(QString tvSessionUuid READ tvSessionUuid NOTIFY tvSessionUuidChanged) Q_PROPERTY(bool tvPairingBusy READ tvPairingBusy NOTIFY tvPairingBusyChanged) Q_PROPERTY(QString tvStatusMessage READ tvStatusMessage NOTIFY tvStatusMessageChanged) - /** Long-poll window for generate_qr (seconds), for receive UI countdown. */ Q_PROPERTY(int tvPairingWaitWindowSeconds READ tvPairingWaitWindowSeconds NOTIFY tvQrCodesChanged) Q_PROPERTY(bool phonePairingBusy READ phonePairingBusy NOTIFY phonePairingBusyChanged) @@ -33,19 +32,11 @@ class PairingUiController : public QObject pendingPhonePairingUuidChanged) Q_PROPERTY(QString lastSuccessfulPhonePairingDisplayName READ lastSuccessfulPhonePairingDisplayName NOTIFY lastSuccessfulPhonePairingDisplayNameChanged) - /** TV flow for QA: 0=idle, 1=waitingForPeer, 2=error, 3=sessionExpired */ Q_PROPERTY(int tvPairingUiPhase READ tvPairingUiPhase NOTIFY tvPairingUiPhaseChanged) - /** Full-screen pairing QR camera under QML (mobile); drives translucent main window. */ Q_PROPERTY(bool embeddedPairingQrCameraActive READ embeddedPairingQrCameraActive WRITE setEmbeddedPairingQrCameraActive NOTIFY embeddedPairingQrCameraActiveChanged) - /** True only on iOS builds: use native UIWindow QR overlay (not Qt.platform.os, which can differ). */ Q_PROPERTY(bool iosNativePairingQrOverlayBuild READ iosNativePairingQrOverlayBuild CONSTANT) - /** True only on Android builds: full-screen CameraActivity pairing scanner; QML hides duplicate scan chrome. */ Q_PROPERTY(bool androidNativePairingQrOverlayBuild READ androidNativePairingQrOverlayBuild CONSTANT) - /** - * Epoch ms until which QML should not call openPairingQrScanner again (after native CameraActivity closes). - * Android pairing flow only; always 0 on other platforms. - */ Q_PROPERTY(qint64 androidPairingReaderCooldownUntilEpochMs READ androidPairingReaderCooldownUntilEpochMs NOTIFY androidPairingReaderCooldownUntilEpochMsChanged) @@ -72,23 +63,12 @@ public: bool iosNativePairingQrOverlayBuild() const; bool androidNativePairingQrOverlayBuild() const; Q_INVOKABLE void setEmbeddedPairingQrCameraActive(bool active); - /** iOS: native dim strip height uses safe bottom + extraPt (see PageSettingsApiQrPairingSend scanDimBleedBottom). No-op elsewhere. */ Q_INVOKABLE void syncIosEmbeddedPairingQrNativeBottomExtra(int extraPt); - /** - * iOS: reapply UIView transparency + safe-area dim strips when embedded pairing is already active. - * Needed after multitask resume: setEmbeddedPairingQrCameraActive(true) is a no-op if the flag stayed true, - * but QUIMetalView / hierarchy may have been rebuilt opaque so the camera only shows in the status bar band. - */ Q_INVOKABLE void refreshIosEmbeddedPairingQrChrome(); qint64 androidPairingReaderCooldownUntilEpochMs() const { return m_androidPairingReaderCooldownUntilEpochMs; } - /** Lengthens androidPairingReaderCooldownUntilEpochMs to at least now + ms (Android pairing; no-op elsewhere). */ Q_INVOKABLE void suppressAndroidNativePairingReaderStarts(int ms); - /** - * iOS: UIKit UIWindow QR scanner (see iosPairingQrOverlayWindow). Pass translated title/subtitle for native chrome. - * No-op on other platforms. - */ Q_INVOKABLE void presentIosPairingQrNativeOverlayScanner(const QString &title = QString(), const QString &subtitle = QString()); Q_INVOKABLE void dismissIosPairingQrNativeOverlayScanner(); @@ -96,36 +76,25 @@ public: #if defined(Q_OS_ANDROID) static bool tryConsumeAndroidQrScan(const QString &code); - /** JNI from CameraActivity onDestroy: avoid reopening native reader while camera HAL is still releasing. */ static void notifyAndroidPairingQrCameraClosed(); - /** JNI before CameraActivity finish when user pressed back — Qt should reopen native scan (QML shell has no preview). */ static void notifyAndroidPairingQrCameraUserDismissed(); #endif public slots: - /** Fast preflight before opening receive QR page; emits errorOccurred on failure. */ bool canOpenTvQrPairingPage(); void startTvQrSession(); void cancelTvQrSession(); - /** TV receive + phone send: call when leaving QR pairing (back / pop) so long-poll state does not stick. */ void cancelAllPairingActivity(); - /** Sends the current premium/free API config from \a serverIndex to the gateway for the given \a qrUuid. */ void submitPhonePairing(const QString &qrUuid, int serverIndex); - /** Android: system camera activity. iOS: toggle camera from QML. */ void openPairingQrScanner(); - /** Mobile: whether the app may use the camera for QR pairing (OS permission). Desktop: true. */ Q_INVOKABLE bool isPairingCameraAccessGranted() const; - /** Mobile: show rationale / system camera permission UI; emits pairingCameraAccessFinished. Desktop: emits granted. */ Q_INVOKABLE void requestPairingCameraAccess(); - /** Open system settings for this app (camera can be enabled there). No-op on desktop. */ Q_INVOKABLE void openPairingCameraAppSettings(); - /** Android: torch for embedded pairing camera. No-op elsewhere. */ Q_INVOKABLE void setPairingQrTorchEnabled(bool enabled); - /** If \a raw contains a session UUID (not vpn://), emits pairingUuidFromScan and returns true. */ bool applyScannedTextAsPairingUuid(const QString &raw); Q_INVOKABLE void clearPendingPhonePairingUuid(); @@ -146,15 +115,11 @@ signals: void pairingUuidFromScan(const QString &uuid); void tvPairingUiPhaseChanged(); - /** After requestPairingCameraAccess(): true if OS granted camera access. */ void pairingCameraAccessFinished(bool granted); void embeddedPairingQrCameraActiveChanged(); void androidPairingReaderCooldownUntilEpochMsChanged(); - /** iOS native overlay scanner: payload was not a pairing session UUID (toast in QML). */ void pairingSendQrScanRejectedInvalidPayload(); - /** Native overlay back chevron tapped — dismiss scanner and close page from QML. */ void pairingIosNativeQrOverlayBackRequested(); - /** Android CameraActivity: user pressed back — QML should exit pairing send (e.g. closePage), not reopen camera. */ void pairingAndroidNativeQrScannerUserDismissed(); private: diff --git a/client/ui/qml/Pages2/PageSettingsApiQrPairingSend.qml b/client/ui/qml/Pages2/PageSettingsApiQrPairingSend.qml index a5f65e443..aedbd7800 100644 --- a/client/ui/qml/Pages2/PageSettingsApiQrPairingSend.qml +++ b/client/ui/qml/Pages2/PageSettingsApiQrPairingSend.qml @@ -16,31 +16,24 @@ import "../Components" PageType { id: root - /** Loud dim colors when true (red/blue/cyan/orange regions). Sync with PageStart.pairingQrChromeDebug. */ property bool pairingQrChromeDebug: false - /** iOS (and any non-Android mobile): native QRCodeReader; Qt may not always report os === "ios". */ readonly property bool useIosStyleNativeQrReader: GC.isMobile() && Qt.platform.os !== "android" - /** iOS-only: full-screen UIKit UIWindow scanner (PairingUiController.iosNativePairingQrOverlayBuild — not Qt.platform.os). */ readonly property bool useIosNativePairingQrOverlay: PairingUiController.iosNativePairingQrOverlayBuild - /** Android: full-screen CameraActivity — Qt cannot reliably composite CameraX under QML on some OEMs (e.g. Samsung). */ readonly property bool useAndroidNativePairingQrScanner: GC.isMobile() && Qt.platform.os === "android" - /** Android: iOS-like flow — titles and camera preview only in CameraActivity; QML hides duplicate scan chrome. */ readonly property bool useAndroidNativePairingQrOverlay: PairingUiController.androidNativePairingQrOverlayBuild && GC.isMobile() && Qt.platform.os === "android" - /** Let dimming draw into window chrome (status bar + tab bar) when camera underlay is active. */ readonly property bool extendScanDimToScreenEdges: GC.isMobile() && pairingWizardStep === 0 && PairingUiController.embeddedPairingQrCameraActive && !root.useAndroidNativePairingQrOverlay clip: !extendScanDimToScreenEdges - /** QQuickWindow (not Item); do not type as Item — breaks binding on Qt 6. */ + /** QQuickWindow as var — typing as Item breaks bindings on Qt 6. */ readonly property var appWindow: Window.window - /** Pixels of window above this page (status bar / safe area gap). */ readonly property real scanDimBleedTop: { if (!extendScanDimToScreenEdges || !appWindow || !appWindow.contentItem) return 0 @@ -49,7 +42,6 @@ PageType { bleed = Math.max(bleed, PageController.safeAreaTopMargin) return bleed } - /** Pixels of window below this page (tab bar + home indicator). */ readonly property real scanDimBleedBottom: { if (!extendScanDimToScreenEdges || !appWindow || !appWindow.contentItem) return 0 @@ -63,15 +55,9 @@ PageType { return bleed } - /** - * Bottom bleed for dimLayer only. On iOS embedded native QR, keep dim inside the page — semi-opaque dim - * extended into the tab stack seam composites badly with opaque tab chrome (persistent hairline). - * Native bottom mask still uses scanDimBleedBottom via pushIosNativeBottomBleedSync(). - */ readonly property real scanDimBleedBottomForDimLayer: (root.useIosStyleNativeQrReader && PairingUiController.embeddedPairingQrCameraActive) ? 0 : scanDimBleedBottom - /** iOS: extend UIKit bottom dim under QML tab bar (see iosPairingCameraAccess + PairingUiController). */ function pushIosNativeBottomBleedSync() { if (!root.useIosStyleNativeQrReader || !PairingUiController.embeddedPairingQrCameraActive) { return @@ -85,61 +71,16 @@ PageType { } } - Timer { - id: pairingScanLayoutLogTimer - interval: 50 - repeat: false - onTriggered: root.logPairingScanLayout("timer") - } - Connections { target: PairingUiController function onEmbeddedPairingQrCameraActiveChanged() { if (PairingUiController.embeddedPairingQrCameraActive) { - pairingScanLayoutLogTimer.restart() Qt.callLater(root.pushIosNativeBottomBleedSync) } } } - function logPairingScanLayout(tag) { - const w = Window.window - const ci = w && w.contentItem ? w.contentItem : null - let m00 = null - let m0h = null - let scanBot = null - let dimTL = null - let dimBR = null - let dimHoleB = null - if (ci) { - m00 = root.mapToItem(ci, 0, 0) - m0h = root.mapToItem(ci, 0, root.height) - scanBot = scanStep.mapToItem(ci, 0, scanStep.height) - dimTL = dimLayer.mapToItem(ci, 0, 0) - dimBR = dimLayer.mapToItem(ci, dimLayer.width, dimLayer.height) - dimHoleB = dimLayer.holeBottom - } - console.warn("[PairingQrLayout]", tag, - "extend=", extendScanDimToScreenEdges, - "clip=", clip, - "root=", root.width, "x", root.height, - "win=", w ? w.width : -1, "x", w ? w.height : -1, - "contentItem=", ci ? ci.width : -1, "x", ci ? ci.height : -1, - "bleedT/B=", scanDimBleedTop, scanDimBleedBottom, - "dimLayerBleedB=", root.scanDimBleedBottomForDimLayer, - "safeT/B=", PageController.safeAreaTopMargin, PageController.safeAreaBottomMargin, - "map00=", m00 ? m00.x + "," + m00.y : "n/a", - "map0h=", m0h ? m0h.x + "," + m0h.y : "n/a", - "ci.scanStepBot=", scanBot ? scanBot.x.toFixed(1) + "," + scanBot.y.toFixed(1) : "n/a", - "ci.dimTL/BR=", dimTL ? dimTL.x.toFixed(1) + "," + dimTL.y.toFixed(1) : "n/a", - dimBR ? dimBR.x.toFixed(1) + "," + dimBR.y.toFixed(1) : "n/a", - "dimHoleB=", dimHoleB !== null ? dimHoleB.toFixed(1) : "n/a", - "win.screen=", w && w.screen ? w.screen.width + "x" + w.screen.height : "n/a", - "dimLayer wh=", dimLayer.width, "x", dimLayer.height) - } - - /** 0 = scan QR, 1 = confirm before sending subscription */ property int pairingWizardStep: 0 property bool keepPhonePairingInBackgroundOnClose: false @@ -188,11 +129,6 @@ PageType { if (addDeviceConfirmNavigationScheduled) { return } - console.warn("[PairingQrSend] startMobileScanner Qt.platform.os=", Qt.platform.os, - "iosNativePairingQrOverlayBuild=", PairingUiController.iosNativePairingQrOverlayBuild, - "useIosNativePairingQrOverlay=", root.useIosNativePairingQrOverlay, - "androidNativePairingQrOverlayBuild=", PairingUiController.androidNativePairingQrOverlayBuild, - "useAndroidNativePairingQrOverlay=", root.useAndroidNativePairingQrOverlay) if (!PairingUiController.isPairingCameraAccessGranted()) { awaitingCameraPermissionForScan = true PairingUiController.requestPairingCameraAccess() @@ -208,14 +144,10 @@ PageType { if (root.useAndroidNativePairingQrScanner) { const coolUntil = PairingUiController.androidPairingReaderCooldownUntilEpochMs if (Date.now() < coolUntil) { - console.warn("[PairingQrSend] startMobileScanner: skip (native camera cooldown), ms left=", - (coolUntil - Date.now())) return } const now = Date.now() if (now - _androidPairingReaderLastStartMs < 700) { - console.warn("[PairingQrSend] startMobileScanner: skip duplicate Android CameraActivity within", - (now - _androidPairingReaderLastStartMs), "ms") return } _androidPairingReaderLastStartMs = now @@ -314,14 +246,8 @@ PageType { } } - /** - * StackView often instantiates the page already visible — onVisibleChanged(true) may never run, so - * startMobileScanner (and native overlay present) would be skipped; only stop/dismiss runs. Same pattern as - * PageSetupWizardApiQrPairingReceive (Component.onCompleted + visible). - */ Component.onCompleted: { if (GC.isMobile() && root.visible && pairingWizardStep === 0) { - console.warn("[PairingQrSend] Component.onCompleted: schedule startMobileScanner (page created visible)") Qt.callLater(startMobileScanner) } } @@ -347,29 +273,21 @@ PageType { }) return } - /** - * No fixed ms delay: f1 reapply UIView transparency; f2 restart AVCapture; f3 refresh again — - * after restartPairingIosCamera, QUIMetalView / render thread often rebuilds opaque layers (same bug - * as status-bar-only camera) until underlay is reapplied once more. - */ Qt.callLater(function () { if (!root.visible || root.pairingWizardStep !== 0 || !GC.isMobile()) { return } - console.warn("[PairingQrResume] ApplicationActive f1 underlay") PairingUiController.embeddedPairingQrCameraActive = true PairingUiController.refreshIosEmbeddedPairingQrChrome() Qt.callLater(function () { if (!root.visible || root.pairingWizardStep !== 0) { return } - console.warn("[PairingQrResume] ApplicationActive f2 restart camera") root.restartPairingIosCamera() Qt.callLater(function () { if (!root.visible || root.pairingWizardStep !== 0) { return } - console.warn("[PairingQrResume] ApplicationActive f3 underlay post-camera") PairingUiController.refreshIosEmbeddedPairingQrChrome() }) }) diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 5887e42af..8ee1a87f7 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -19,87 +19,15 @@ PageType { property bool isControlsDisabled: false property bool isTabBarDisabled: false - /** Loud colors (tab bar base green, extra overlap) when true — pair with PageSettingsApiQrPairingSend.pairingQrChromeDebug. */ property bool pairingQrChromeDebug: false - /** Opaque extension of tab bar background upward (iOS embedded QR); see PairingTabChrome deltaY vs stack bottom. */ readonly property int tabBarChromeOverlapUp: (PairingUiController.embeddedPairingQrCameraActive && GC.isMobile() && Qt.platform.os !== "android") ? (root.pairingQrChromeDebug ? 24 : 18) : 0 - /** Pull stack under tab chrome so TabBar.background overlap fully covers stack bottom pixels. */ readonly property int tabStackPairingUnderlapDown: (PairingUiController.embeddedPairingQrCameraActive && GC.isMobile() && Qt.platform.os !== "android") ? 8 : 0 - readonly property bool pairingTabChromeLogActive: PairingUiController.embeddedPairingQrCameraActive && GC.isMobile() - && Qt.platform.os !== "android" - - function logPairingTabChromeLayout(tag) { - if (!root.pairingTabChromeLogActive) { - return - } - const w = Window.window - const ci = w && w.contentItem ? w.contentItem : null - let msg = "[PairingTabChrome] " + tag - msg += " PageStart=" + Math.round(root.width) + "x" + Math.round(root.height) - msg += " tabBar=" + Math.round(tabBar.width) + "x" + Math.round(tabBar.height) + " y=" + tabBar.y.toFixed(2) - msg += " imeBM=" + PageController.imeHeight - msg += " stack=" + Math.round(tabBarStackView.width) + "x" + Math.round(tabBarStackView.height) - msg += " overlapUp=" + tabBarChromeOverlapUp + " stackUnderlap=" + tabStackPairingUnderlapDown - msg += " tabBgRootH=" + (tabBarBackgroundRoot ? tabBarBackgroundRoot.height.toFixed(2) : "n/a") - if (ci) { - const tabOrigin = tabBar.mapToItem(ci, 0, 0) - const tabBandTop = tabBar.mapToItem(ci, 0, -tabBarChromeOverlapUp) - const stackOrigin = tabBarStackView.mapToItem(ci, 0, 0) - const stackBottomMid = tabBarStackView.mapToItem(ci, tabBarStackView.width * 0.5, tabBarStackView.height) - const bgTopLeft = tabBarBackgroundRoot.mapToItem(ci, 0, 0) - msg += " ci.tab(0,0)=" + tabOrigin.x.toFixed(1) + "," + tabOrigin.y.toFixed(1) - msg += " ci.tab(0,-ov)=" + tabBandTop.x.toFixed(1) + "," + tabBandTop.y.toFixed(1) - msg += " ci.stack(0,0)=" + stackOrigin.x.toFixed(1) + "," + stackOrigin.y.toFixed(1) - msg += " ci.stackMidBot=" + stackBottomMid.x.toFixed(1) + "," + stackBottomMid.y.toFixed(1) - msg += " ci.tabBgRoot(0,0)=" + bgTopLeft.x.toFixed(1) + "," + bgTopLeft.y.toFixed(1) - msg += " deltaY_tabBandTop_minus_stackMidBot=" + (tabBandTop.y - stackBottomMid.y).toFixed(2) - } else { - msg += " ci=missing" - } - console.warn(msg) - } - - Timer { - id: pairingTabChromeLogTimer50 - interval: 50 - repeat: false - onTriggered: root.logPairingTabChromeLayout("t50") - } - Timer { - id: pairingTabChromeLogTimer350 - interval: 350 - repeat: false - onTriggered: root.logPairingTabChromeLayout("t350") - } - - Connections { - target: PairingUiController - - function onEmbeddedPairingQrCameraActiveChanged() { - if (PairingUiController.embeddedPairingQrCameraActive && GC.isMobile() && Qt.platform.os !== "android") { - pairingTabChromeLogTimer50.restart() - pairingTabChromeLogTimer350.restart() - } - } - } - - onWidthChanged: { - if (root.pairingTabChromeLogActive) { - pairingTabChromeLogTimer50.restart() - } - } - onHeightChanged: { - if (root.pairingTabChromeLogActive) { - pairingTabChromeLogTimer50.restart() - } - } - Connections { objectName: "pageControllerConnection" @@ -413,12 +341,10 @@ PageType { anchors.bottom: parent.bottom height: parent.height + root.tabBarChromeOverlapUp - /** Opaque base: Shape alone can show the window-layer camera through anti-aliased edges when the window is clear (iOS QR pairing). */ Rectangle { anchors.fill: parent color: root.pairingQrChromeDebug ? "#00ff66" : AmneziaStyle.color.onyxBlack } - /** Stroke around tab row; hidden during iOS embedded QR overlap — top horizontal slateGray reads as a hairline “strip” above tabs. */ Shape { id: tabBarChromeShape objectName: "backgroundShape"