From 0522123a33fde3a90312cad72b764b784320c629 Mon Sep 17 00:00:00 2001 From: iris <2+iris@noreply.localhost> Date: Tue, 25 Aug 2026 20:36:12 -0400 Subject: [PATCH] Scan the enrollment QR in either polarity ai-server prints the QR as block characters, which take the terminal's foreground colour. On a dark-themed terminal that comes out as a photographic negative, and ZXing looks only for a dark code on a light ground, so the in-app scanner silently never matched it -- while the phone's own camera app, which tries both, did. Fixing it in the scanner rather than by having the server force its colours means however the code gets displayed stops mattering: a light terminal, a dark one, a screenshot someone inverted, a printout. MIXED_SCAN alternates normal and inverted frames, so the cost is half the frame rate at each polarity. Checked against the library's own MixedDecoder off-device: the plain decoder reads the normal image and returns nothing for the inverted one, and the mixed decoder reads both within two frames. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01QQz6R4kBQcWSHBNZMgBnjL --- AGENTS.md | 7 +++++++ .../kotlin/com/example/aiapp/SettingsScreen.kt | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 79aced0..c2750ec 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -152,6 +152,13 @@ Established 2026-08-25, and it decides more than it looks like: `"""\n-----BEGIN CERTIFICATE-----` costs Android's `CertificateFactory` its preamble sniff, so it tries DER instead and fails at runtime with `ASN.1 ... DECODE_ERROR` — nowhere near the code that produced it. +- **ZXing only looks for a dark code on a light ground.** The enrollment + QR is block characters in the terminal's foreground colour, so a + dark-themed terminal renders it as a negative and the in-app scanner + silently never matches — while the phone's own camera app, which tries + both, does. The scanner asks for `Intents.Scan.MIXED_SCAN`, which + alternates normal and inverted frames; keep it that way rather than + making the server dictate the colours. - **AGP 9 refuses `Provider`s in the source-set API**: generated sources go through `androidComponents.onVariants { it.sources.java?.addGenerated SourceDirectory(task, Task::outputDir) }`, which also carries the task diff --git a/app/androidApp/src/main/kotlin/com/example/aiapp/SettingsScreen.kt b/app/androidApp/src/main/kotlin/com/example/aiapp/SettingsScreen.kt index 0c0b4b1..af406b9 100644 --- a/app/androidApp/src/main/kotlin/com/example/aiapp/SettingsScreen.kt +++ b/app/androidApp/src/main/kotlin/com/example/aiapp/SettingsScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.activity.compose.rememberLauncherForActivityResult +import com.google.zxing.client.android.Intents import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanIntentResult import com.journeyapps.barcodescanner.ScanOptions @@ -82,7 +83,22 @@ fun SettingsScreen( Spacer(Modifier.height(16.dp)) OutlinedButton( - onClick = { scanLauncher.launch(ScanOptions().setDesiredBarcodeFormats(ScanOptions.QR_CODE)) }, + onClick = { + scanLauncher.launch( + ScanOptions() + .setDesiredBarcodeFormats(ScanOptions.QR_CODE) + // Decode both polarities: ZXing otherwise looks only for a + // dark code on a light ground, and ai-server's QR is block + // characters in the terminal's foreground colour -- so on a + // dark-themed terminal it comes out as a photographic + // negative that the scanner silently never matches. Which + // way round it renders is the terminal's business, not + // something this app should depend on. MIXED_SCAN alternates + // normal and inverted frames, so it costs half the frame + // rate at each polarity and nothing else. + .addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN) + ) + }, modifier = Modifier.fillMaxWidth(), ) { Text("Scan QR code") } Spacer(Modifier.height(16.dp))