Scanning from another app
The intents
| Action | Filter in the manifest | What happens | Result |
|---|---|---|---|
com.google.zxing.client.android.SCAN | Yes, 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.SCAN | Yes. | 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.CARD | No 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.