Files
amethyst/commonsUI
Claude 8f42e4cec2 feat(qr): rebuild the QR reader on CameraX + zxing-cpp
The old scanner handed the whole job to zxing-android-embedded's CaptureActivity,
which is a Camera1 app: autofocus is a 2s timer rather than continuous, there is
no zoom and no discoverable torch, the decode is cropped to the viewfinder rect,
and each frame gets exactly one un-retried attempt. We also passed MIXED_SCAN,
which inverts alternate frames and so threw away half of every scan of an
ordinary dark-on-light code.

Replace it with an in-app Compose scanner on CameraX (already a declared but
unused dependency) behind a BarcodeDecoder interface, with zxing-cpp as the
engine:

- Continuous autofocus, tap-to-focus, pinch zoom and a torch button; the
  analysis frame's Y plane gives mean luminance for free, so the torch is
  offered exactly when the scene is dark.
- Auto-zoom sweeps 1x-2.5x while nothing is decoding, and stops for good once
  the user pinches. A code too small in frame is the one failure no amount of
  decoder tuning can fix.
- tryInvert/tryRotate/tryDownscale on every frame and tryHarder/tryDenoise on
  every fifth, so a hard code gets several expensive attempts a second without
  the frame rate collapsing for easy ones.
- The whole frame is decoded, not a centred box, and the preview and analysis
  use cases share one ViewPort so the overlay can highlight what we read.
- Several codes in frame become tappable choices instead of a coin flip.
- Structured Append (multi-part) codes are reassembled; we could not read them.

Decoding a code we cannot route no longer looks identical to not reading one
(issue #417): payloads are classified, and an unsupported one keeps the camera
running and explains itself. Key material and pairing secrets are never echoed
into that sheet -- a QR code is scanned in public by definition. A bare 64-char
hex pubkey is re-encoded as an npub rather than rejected.

Codes can now also be read from the gallery or the clipboard, which is how most
of them actually arrive.

On the display side, ShowQRScreen now pins brightness and keeps the screen awake
(extracted from ShareNoteAsQrScreen, which already did), and the finder
patterns' corner rounding scales with module size instead of a fixed 20px that
deformed small codes.

Licensing: io.github.zxing-cpp:android is Apache-2.0 (verified against its
published POM) -- permissive, no linking-exception question. It adds a prebuilt
.so per ABI, ~0.8 MB compressed on arm64 after the existing ABI split, partly
returned by dropping zxing-android-embedded. Its AAR ships a consumer ProGuard
keep rule, so R8 does not break the JNI boundary.

Not yet verified: the plan's phase-0 decode corpus and the manual scan matrix
both need a camera, so every camera-facing path here is compile-verified and
reasoned-through only. Tracked in the plan's new section 7.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0134jvyriixNTHST4WRbbqbX
2026-09-15 02:27:40 +00:00
..