Four Kotlin files appeared in both apps. Diffed with the product names normalised, EnrollmentScanActivity was 31 lines each differing in six -- all of them comments -- and PinnedCert was 58 against 59 with identical TrustManager logic. That is the same evidence that moved the Rust half. Two parameters, both per-app, and they fail differently. A wrong URI scheme means a scanned QR is ignored, which is visible at once. A wrong Keystore alias means the app cannot unseal the token it already stored, so an enrolled phone reads as not enrolled and nothing says why -- so both existing values are recorded in the README rather than left to be rediscovered. The certificate alias went the other way and became a constant: it names an entry in a KeyStore that exists only in memory for the length of one lazy block, so nothing ever reads it back and having two of them said nothing. The drift was in both directions, as the evidence predicted: ai-app had gained localNetworkAllowed -- which matters because Android 17's ACCESS_LOCAL_NETWORK denial is invisible at the socket, and without it a blocked app and an unreachable server produce the same timeout -- and had moved to the KTX `edit` block. dev-updater had neither, and gets both. No Compose, deliberately. Nothing here draws anything; the screens are each app's own because that is where the two products actually differ. The PinnedCaCertificate generator did not move, and the README says why: all three things it varies are per-app, so sharing it means a composite build neither project has. Its one historical bug is already fixed identically in both copies.
31 lines
1.5 KiB
Kotlin
31 lines
1.5 KiB
Kotlin
package com.example.wgapplink
|
|
|
|
import android.view.View
|
|
import com.journeyapps.barcodescanner.CaptureActivity
|
|
import com.journeyapps.barcodescanner.DecoratedBarcodeView
|
|
|
|
/**
|
|
* The screen behind an app's "Scan QR code" button: zxing-android-embedded's capture activity with
|
|
* two of its defaults taken off, both because they put work on the person holding the phone.
|
|
*
|
|
* - **It decodes only the framing rectangle**, which `CameraPreview.marginFraction` insets by 10%
|
|
* from every edge and `DecoderThread` crops each frame to before the decoder sees it. A code that
|
|
* fills the viewfinder keeps decoding -- measured, not assumed -- but it does so having spent its
|
|
* quiet zone and margin for error on the crop, and anything further out is simply not looked at.
|
|
* Decoding the whole preview costs nothing and means the framing is never the user's problem.
|
|
* - **It decorates the preview** with a red laser line and the dots the detector scatters wherever
|
|
* it finds a candidate pattern. That is the library's house style; a plain preview is ours.
|
|
*
|
|
* Orientation is left to the sensor rather than pinned to landscape as the library's own manifest
|
|
* entry pins it, so the phone can be held whichever way the code is in front of it.
|
|
*/
|
|
class EnrollmentScanActivity : CaptureActivity() {
|
|
override fun initializeContent(): DecoratedBarcodeView {
|
|
val view = super.initializeContent()
|
|
view.barcodeView.marginFraction = 0.0
|
|
view.viewFinder.visibility = View.GONE
|
|
view.statusView.visibility = View.GONE
|
|
return view
|
|
}
|
|
}
|