Files
fksar/docs/thijooree/25-qr-scanner.md
T
2026-06-13 21:30:12 +05:00

77 lines
3.2 KiB
Markdown

# 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) | Routes PayMV / BML URL via `PaymvQrParser` + `extractBmlGatewayUrl` |
| [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 URL → 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)