# QR Scanner Full-screen camera-based QR scanner. Returns the decoded string to the caller via activity result. --- ## Activity — `QrScannerActivity` File: `ui/home/QrScannerActivity.kt`. Hosts a CameraX preview + analysis pipeline backed by `de.markusfisch.android.zxingcpp.ZxingCpp`. ### Reader Options ```kotlin ZxingCpp.ReaderOptions( tryHarder = true, tryRotate = true, tryInvert = true, tryDownscale = true, maxNumberOfSymbols = 1, textMode = ZxingCpp.TextMode.PLAIN, ) ``` ### Camera Pipeline `startCamera()` (`QrScannerActivity.kt:186-240`): 1. `ProcessCameraProvider.getInstance()` 2. Builds `Preview` and `ImageAnalysis` with `HIGHEST_AVAILABLE_STRATEGY` + `RATIO_16_9_FALLBACK_AUTO_STRATEGY` 3. Analyser receives `STRATEGY_KEEP_ONLY_LATEST` frames; reads the Y-plane via `ZxingCpp.readYBuffer(...)` with the image rotation applied 4. On first hit, `deliverResult(text)` sets `Activity.RESULT_OK` with `EXTRA_QR_CONTENT` and finishes ### Lock Guard `onCreate` (`QrScannerActivity.kt:101-106`) checks `CredentialStore.loadSecurityHash()` and `app.isUnlocked`; if a lock exists and the app is locked, it forwards to `LockActivity` and finishes — direct intent launches cannot bypass the lock screen. --- ## UI Controls | Control | Behaviour | |---|---| | Flashlight (`btnFlashlight`) | Toggles `camera.cameraControl.enableTorch()`; icon swaps between `ic_flashlight_to_on` / `ic_flashlight_to_off` with an `Animatable` transition | | Zoom slider (`zoomSlider`) | Linear zoom via `setLinearZoom()`; updated live from the camera's `zoomState` observer | | Pinch-to-zoom | `ScaleGestureDetector` on the preview view, mapped through `cameraControl.setZoomRatio()` clamped to the device's `[minZoomRatio, maxZoomRatio]` | | Pick image (`btnPickImage`) | `GetContent("image/*")`; decodes the picked bitmap with the same reader options | ### Image Decoder Fallback `Bitmap.decodeQr()` (`QrScannerActivity.kt:243-249`) mirrors BinaryEye: tries `Binarizer.LOCAL_AVERAGE` first, then falls back to `GLOBAL_HISTOGRAM` for tricky lighting. --- ## Callers Each caller registers an `ActivityResultContracts.StartActivityForResult` launcher and reads `EXTRA_QR_CONTENT` from the result intent. | Caller | Result handling | |---|---| | [TransferFragment](07-transfer.md) | `PaymvQrParser.bmlQrPayTarget()` first (URL, combined and POS QRs → BML QR pay), then M-Faisa numeric ids, then `PaymvQrParser.parse()` | | [PayMvQrFragment](11-paymv-qr-screen.md) | Generation only — does not call the scanner directly | | `CredentialsFragment` (login) | `OtpauthParser.parse(raw)` → fills `etOtpSeed` or shows a chooser for multi-entry QRs | | [DashboardFragment](21-dashboard.md) | BML QR (URL or POS) → BML QR pay; PayMV → pre-fill Transfer; otherwise toast | | [CardsFragment](22-cards.md) | Same routing as Dashboard, scoped to the active BML card | ### Share-to-Scan Fast Path When another app shares an image directly to the activity-alias `.ScanToPayActivity`, `MainActivity` decodes the QR from the image **before** routing (it holds the URI permission only at that point). The decoded string is then forwarded as the `share_qr_text` extra. This bypasses the camera entirely. ---   --- [← Notifications](24-notifications.md)     **Next →** [Circular Nav](26-circular-nav.md)