Verdetto

Scanning from another app

Verdetto answers three intents and one share. Nothing here needs a permission, a library, or a key. The person keeps every safety check and every setting of the app; your app receives the text of the code the moment they confirm it.

The intents

ActionFilter in the manifestWhat happensResult
com.google.zxing.client.android.SCANYes, implicit intents work.Opens the scanner. The first code the person locks on is handed back, as the ZXing Barcode Scanner did it.RESULT_OK with SCAN_RESULT and SCAN_RESULT_FORMAT
app.scanner.action.SCANYes.Opens the scanner. Started for a result (a result launcher or startActivityForResult) it hands the code back the same way; started plainly it just opens the app on the scanner.RESULT_OK with the two extras when started for a result
app.scanner.action.CARDNo filter: an explicit intent with the package app.scanner.free only.Opens the person's own contact card editor, the code they show to share their details. Meant for the app's own widgets and shortcuts; another app may call it, but nothing comes back.None
android.intent.action.SEND with image/*Yes.Decodes a picture: the app shows the result sheet for the codes in it.None

The person can turn the hand-back off in Settings under "Hand results to other apps" ("When an app asks for a scan, the code goes back to it"), which is on by default. With it off, either scan action opens the scanner as a normal launch and your launcher receives RESULT_CANCELED when the person leaves. Back from the scanner is RESULT_CANCELED too.

No request extras are read today: not SCAN_MODE, SCAN_FORMATS, PROMPT_MESSAGE, SAVE_HISTORY, or any other. The scanner reads every symbology it knows on every call. A code the app's checks flag is still handed back; the person sees the warning first and decides.

The result

  • SCAN_RESULT (String): the code's content, exactly the bytes the code carried, decoded as text (UTF-8 where the symbology allows it, the symbology's own character set otherwise).
  • SCAN_RESULT_FORMAT (String): the symbology in upper case with underscores, the ZXing names where they exist. Delivered today: QR_CODE, MICRO_QR_CODE, RMQR_CODE, DATA_MATRIX, AZTEC, PDF417, MAXICODE, HAN_XIN, EAN_13, EAN_8, UPC_A, UPC_E, CODE_128, CODE_39, CODE_93, CODABAR, ITF, DATABAR, DATABAR_EXPANDED, DATABAR_LIMITED, DX_FILM_EDGE, and the other symbologies the app's own decoders add, in the same spelling (the name shown on the result sheet, upper-cased, spaces and hyphens as underscores).

Nothing else travels: no image, no location, no history.

Kotlin

private val scan = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val text = result.data?.getStringExtra("SCAN_RESULT")
        val format = result.data?.getStringExtra("SCAN_RESULT_FORMAT")
        // use text and format
    }
}

fun startScan() {
    val intent = Intent("app.scanner.action.SCAN")
    if (intent.resolveActivity(packageManager) != null) scan.launch(intent)
    else startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(GET_VERDETTO)))
}

// the store page, with the developers referrer
private const val GET_VERDETTO =
    "https://play.google.com/store/apps/details?id=app.scanner.free&referrer=utm_source%3Ddevelopers%26utm_medium%3Ddocs%26utm_campaign%3Ddevelopers"

On Android 11 and later, add the query to your manifest so resolveActivity can see the app:

<queries>
    <intent>
        <action android:name="app.scanner.action.SCAN" />
    </intent>
</queries>

Java

private final ActivityResultLauncher<Intent> scan = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(), result -> {
            if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
                String text = result.getData().getStringExtra("SCAN_RESULT");
                String format = result.getData().getStringExtra("SCAN_RESULT_FORMAT");
                // use text and format
            }
        });

void startScan() {
    Intent intent = new Intent("app.scanner.action.SCAN");
    if (intent.resolveActivity(getPackageManager()) != null) scan.launch(intent);
    else startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(GET_VERDETTO)));
}

The ZXing action

Code written for the ZXing Barcode Scanner keeps working: send com.google.zxing.client.android.SCAN the same way and read the same two extras. If more than one scanner on the phone answers it, the system asks the person which to use; sending the intent to the package app.scanner.free skips that.

What the person sees

The scanner opens as it always does, with its own checks. When a code locks, the app hands it back and closes; nothing of yours appears on the screen, and nothing of theirs (history, settings, the safety list) is touched by the call.

Testing on a phone without the app. The store page with the developers referrer, the same address the fallback in the samples opens: Get it on Google Play. The source of this page is the app's own INTENT.md; when the two differ, the app repository is right and this page is behind.