Create Free APK

JavaScript bridge API

QR and barcode scanner

Google’s scanner screen from one promise, AppMint.scanCode() - no camera permission, and the parsed URL, Wi-Fi, phone or contact for common QR codes.

AppMint.scanCode helper#

AppMint.scanCode(options)

Example

Opens Google's QR code and barcode scanner screen and gives you what was scanned. One call - no camera permission, no <video>, no UI of your own.

Returns: a Promise that always resolves: {ok: true, value, displayValue, format, type, …} or {ok: false, code, message}. Needs: nothing to switch on. The phone needs Google Play services.

Any code:

async function scan() {
  if (!(window.AppMint && AppMint.scanCode)) { alert('Open this page in the app to scan'); return; }
  var r = await AppMint.scanCode();
  if (!r.ok) {
    if (r.code === 'cancelled') return;                          // the user closed the scanner
    if (r.code === 'unavailable') alert('The scanner is not available on this phone');
    else if (r.code === 'busy') alert('A scan is already open');
    else alert('Scan failed: ' + r.message);                     // 'failed'
    return;
  }
  console.log(r.value, r.format, r.type);   // "TICKET-4821", "qr_code", "text"
}

Only some formats, and a "type it in" option for damaged labels:

var r = await AppMint.scanCode({
  formats: ['ean_13', 'ean_8', 'upc_a', 'upc_e'],   // shop barcodes
  manualInput: true,                                // lets the user type the code
  autoZoom: true                                    // default true
});
if (r.ok) lookUpProduct(r.value);

Ready-made parts - only the part that matches r.type is present:

var r = await AppMint.scanCode({ formats: ['qr_code'] });
if (r.ok) {
  if (r.type === 'url')     openLink(r.url.url);                      // url: { url, title }
  if (r.type === 'wifi')    showWifi(r.wifi.ssid, r.wifi.password, r.wifi.encryption); // 'wpa'|'wep'|'open'
  if (r.type === 'phone')   callNumber(r.phone);                      // a string
  if (r.type === 'email')   writeMail(r.email.address, r.email.subject, r.email.body);
  if (r.type === 'sms')     writeSms(r.sms.phoneNumber, r.sms.message);
  if (r.type === 'geo')     showMap(r.geo.lat, r.geo.lng);
  if (r.type === 'contact') addContact(r.contact.name, r.contact.organization, r.contact.phones, r.contact.emails);
}

Notes: Format names: qr_code, aztec, codabar, code_39, code_93, code_128, data_matrix, ean_8, ean_13, itf, pdf417, upc_a, upc_e (unknown names are ignored; none = all formats). type is one of url, wifi, text, phone, email, sms, geo, contact, calendar, isbn, product, driver_license, unknown. Error codes: cancelled (closed, including with Back), unavailable (no Play services, or the scanner could not be downloaded), busy (a scan is already open), failed. The first scan on a phone can take a few seconds while Play services downloads the scanner. AI-built apps use scanCode() from @/lib/appmintNative, which also scans on the web.

__ocrAvailable bridge#

window.WebToApk.__ocrAvailable(): Boolean

TextDetector / recognizeText(): Google Play services is here (the OCR module's home).

Example

Internal transport behind TextDetector: whether Google Play services (where the recognizer comes from) is on this phone. Pages use the standard API; never call this directly - the __ methods may change without notice.

Returns: true or false. When false, TextDetector is not installed at all. Needs: nothing.

Use the public API:

if ('TextDetector' in window) scanButton.hidden = false;

Notes: Implemented in the app shell (TextRecognitionBridge.kt).

__ocrRecognize bridge#

window.WebToApk.__ocrRecognize(requestId: String, base64: String)

Recognises the text in [base64] (PNG/JPEG); the answer arrives as `appmint:ocr {requestId, blocks|error}`.

Example

Internal transport behind TextDetector.detect(): recognises the text in a PNG/JPEG. Pages use the standard API; never call this directly - the __ methods may change without notice.

Returns: nothing; the answer arrives as the appmint:ocr event {requestId, blocks:[{text, box, corners, lines}]} or {requestId, error} (unavailable, bad-image, failed: …). Needs: nothing.

Use the public API:

const lines = await new TextDetector().detect(document.getElementById('receipt'));
console.log(lines.map((l) => l.rawValue));

Notes: Implemented in the app shell (TextRecognitionBridge.kt).

scanCode bridge#

window.WebToApk.scanCode(optionsJson: String, callbackId: String)

Opens Google's code scanner; [optionsJson] `{formats?, autoZoom?, manualInput?}`. Resolves the AppMint.scanCode promise [callbackId].

Example

Internal plumbing behind AppMint.scanCode() - pages use that Promise helper, which opens Google's QR/barcode scanner and resolves with the result.

Returns: nothing directly; the result resolves the AppMint.scanCode() promise. Needs: nothing to switch on (no camera permission). The phone needs Google Play services.

The public API:

async function scanTicket() {
  if (!(window.AppMint && AppMint.scanCode)) { alert('Open this page in the app to scan'); return; }
  var r = await AppMint.scanCode({ formats: ['qr_code'] });
  if (r.ok) checkTicket(r.value);                  // r.format, r.type, r.displayValue…
  else if (r.code !== 'cancelled') alert('Scan failed (' + r.code + ')');
}

Notes: The raw method takes optionsJson ({formats?, autoZoom?, manualInput?} as a JSON string) and a callbackId; the answer is delivered to the helper's internal reply table, so a page calling it directly gets no answer. Only one scan runs at a time - a second call answers code: 'busy'. See AppMint.scanCode for every result field and error code.

Generated from the app runtime and its example files on every docs build. Read it as Markdown · All families.

Checked against the shipped bridge on 2026-09-23.