Scan de QR Code¶
Ce document explique comment le scan de QR code est implémenté et exposé à la couche WebView. Il couvre :
- L'API de pont utilisée par JavaScript pour démarrer un scan
- Comment les permissions sont gérées
- Comment l'activité Huawei ScanKit est lancée
- Comment le résultat du scan est renvoyé à JavaScript
La fonctionnalité de scan QR est implémentée dans BaseWebViewActivity et exposée à WebView via bridgeWebView.
Vue d'ensemble¶
Le flux de scan QR est :
┌──────────┐
│ START │
└────┬─────┘
│
▼
┌─────────────────────────┐
│ Check Permissions │
│ - CAMERA │
│ - READ_MEDIA │
└────┬────────────────────┘
│
▼
┌─────────────────────────┐
│ Register Callback │
│ scanQrcodeResultCallback│
└────┬────────────────────┘
│
▼
┌─────────────────────────┐
│ startQrCodeScan(999) │
└────┬────────────────────┘
│
▼
┌─────────────────────────┐
│ Open Camera Scanner │
│ │
│ ┌─────────────────┐ │
│ │ [QR Frame] │ │
│ │ │ │
│ │ Point camera │ │
│ │ at QR code │ │
│ └─────────────────┘ │
└────┬────────────────────┘
│
│ (User scans or cancels)
│
├────────────┬─────────────┐
▼ ▼ ▼
Success Cancel Error
│
▼
┌─────────────────────────┐
│ Decode QR Data │
│ Extract value │
└────┬────────────────────┘
│
▼
┌─────────────────────────┐
│ scanQrcodeResultCallback│
│ { │
│ value: "QR_CONTENT", │
│ requestCode: 999 │
│ } │
└────┬────────────────────┘
│
▼
┌─────────────────────────┐
│ Process QR Data │
│ in JavaScript │
│ - URL redirect │
│ - Parse JSON │
│ - etc. │
└─────────────────────────┘
│
▼
┌──────────┐
│ END │
└──────────┘
- JavaScript appelle la méthode de pont natif startQrCodeScan.
- Le code natif vérifie et demande les permissions nécessaires (caméra, etc.).
- Si les permissions sont accordées, le natif configure Huawei ScanKit et lance ScanKitActivity.
- L'utilisateur scanne un QR code (ou annule).
- BaseWebViewActivity.onActivityResult() reçoit le résultat, extrait la valeur scannée et la renvoie à JavaScript via scanQrcodeResultCallBack.
Le flux est entièrement piloté par événements : WebView déclenche le scan et reçoit le résultat de manière asynchrone via un rappel.
API de Pont Native–WebView¶
startQrCodeScan (JS → Native)
Nom du gestionnaire de pont : startQrCodeScan
Défini dans : registerMethod() à l'intérieur de BaseWebViewActivity
Signature côté natif :
bridgeWebView.registerHandler("startQrCodeScan", new BridgeHandler() {
@Override
public void handler(String requestCode, CallBackFunction function) {
// ...
}
});
Paramètres¶
requestCode (String)
- Optionnel, envoyé depuis JavaScript.
- Si vide, REQUEST_CODE_SCAN_ONE prend la valeur par défaut 0.
- Si non vide, il est analysé comme entier et stocké dans REQUEST_CODE_SCAN_ONE.
- Cela permet au côté JS de distinguer plusieurs requêtes de scan si nécessaire.
Exemple de logique native :
REQUEST_CODE_SCAN_ONE = TextUtils.isEmpty(requestCode)
? 0
: Integer.valueOf(requestCode);
Comportement Natif Immédiat¶
À l'intérieur de handler(...), le code natif :
- Demande les permissions d'exécution en utilisant
XXPermissions(avec un PermissionInterceptor personnalisé). - Si toutes les permissions requises ne sont pas accordées, il rappelle immédiatement JS avec une erreur de permission :
function.onCallBack(gson.toJson(Result.fail(PERMISSION_ERROR, false))); return; - Si les permissions sont accordées, il construit une configuration Huawei ScanKit et lance l'activité de scan.
Gestion des Permissions¶
Les permissions sont demandées via XXPermissions :
XXPermissions.with(BaseWebViewActivity.this)
// (Camera and any related permissions are configured here)
.interceptor(new PermissionInterceptor())
.request(new OnPermissionCallback() {
@Override
public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
if (!allGranted) {
function.onCallBack(gson.toJson(Result.fail(PERMISSION_ERROR, false)));
return;
}
// Permissions granted → proceed to launch scanner
// ...
}
});
Points clés :¶
- Les permissions sont demandées avant le lancement de toute IU de scan.
- Si l'utilisateur refuse une permission requise, WebView reçoit une réponse avec
Result.fail(PERMISSION_ERROR, false)et aucune activité de scan n'est démarrée. - La liste réelle des permissions (caméra, stockage, etc.) est configurée dans le code natif et peut être ajustée selon les besoins.
Lancement de l'IU de Scan (Huawei ScanKit)¶
Une fois les permissions accordées, le code construit un HmsScanAnalyzerOptions et démarre l'activité Huawei ScanKit :
HmsScanAnalyzerOptions.Creator creator = new HmsScanAnalyzerOptions.Creator();
// Accept all types of barcodes / QR codes
creator.setHmsScanTypes(HmsScan.ALL_SCAN_TYPE);
// Show the built-in scan guide overlay
creator.setShowGuide(true);
// Allow scanning from gallery (photo mode)
creator.setPhotoMode(true);
HmsScanAnalyzerOptions hmsScanAnalyzerOptions = creator.create();
// Launch ScanKit activity
Intent intent = new Intent(BaseWebViewActivity.this, ScanKitActivity.class);
if (intent != null) {
intent.putExtra("ScanFormatValue", hmsScanAnalyzerOptions.mode);
}
startActivityForResult(intent, REQUEST_CODE_SCAN_ONE);
Notes :
HmsScan.ALL_SCAN_TYPEactive la reconnaissance de plusieurs formats de codes-barres, pas seulement les QR codes.setShowGuide(true)affiche un guide au-dessus de l'aperçu de la caméra pour aider à l'alignement de l'utilisateur.setPhotoMode(true)permet à l'utilisateur de sélectionner une image existante dans la galerie et de la scanner.REQUEST_CODE_SCAN_ONEest utilisé ultérieurement dansonActivityResultpour identifier le résultat.
Si une exception se produit lors de la construction des options ou du démarrage de l'activité, elle est capturée et journalisée :
}
catch (Exception e) {
e.printStackTrace();
}
Réception du Résultat du Scan (Native)¶
Le résultat du scan est géré dans onActivityResult :
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_SCAN_ONE && resultCode == RESULT_OK) {
HmsScan obj = data.getParcelableExtra(ScanUtil.RESULT);
Toaster.show(obj.originalValue);
if (obj != null) {
Map<String, Object> map = new HashMap<>();
map.put("value", obj.originalValue);
map.put("requestCode", REQUEST_CODE_SCAN_ONE);
bridgeWebView.callHandler("scanQrcodeResultCallBack",
gson.toJson(Result.ok(map)),
new CallBackFunction() {
@Override
public void onCallBack(String data) {
}
});
} else {
bridgeWebView.callHandler("scanQrcodeResultCallBack",
gson.toJson(Result.fail()),
new CallBackFunction() {
@Override
public void onCallBack(String data) {
}
});
}
}
// ... (other onActivityResult branches: picture choose, OCR, etc.)
}
Détails importants¶
- Uniquement quand
requestCode == REQUEST_CODE_SCAN_ONEetresultCode == RESULT_OKle résultat est traité comme un résultat de scan valide. HmsScan obj = data.getParcelableExtra(ScanUtil.RESULT);extrait l'objet résultatHmsScande l'intent.obj.originalValuecontient la valeur chaîne encodée dans le QR code / code-barres.- Un petit toast (
Toaster.show(obj.originalValue);) est affiché pour le débogage / retour rapide. -
Si
objest non-null :- Une
mapest créée avec :"value"→ la chaîne scannée (obj.originalValue)"requestCode"→ le code de requête utilisé (pour que JS puisse corréler la réponse)
- La map est encapsulée avec
Result.ok(map)et renvoyée à JS viascanQrcodeResultCallBack.
- Une
-
Si
objest null :- Le natif appelle scanQrcodeResultCallBack avec
Result.fail().
- Le natif appelle scanQrcodeResultCallBack avec
Gestion de l'annulation :
- Si l'utilisateur annule l'écran ScanKit, `resultCode` ne sera pas `RESULT_OK`, donc la branche QR est ignorée.
- Dans ce cas, aucune charge utile de succès n'est envoyée ; le côté JS doit gérer cela en traitant « aucun résultat reçu » comme une opération annulée ou en ajoutant un mécanisme séparé si nécessaire.
Utilisation côté JavaScript¶
1. Enregistrer le rappel de résultat¶
Dans votre JavaScript WebView, vous devez enregistrer un gestionnaire pour recevoir les résultats du scan
bridgeWebView.registerHandler("scanQrcodeResultCallBack", function (data) {
const res = JSON.parse(data);
// Depending on your Result implementation, this may look like:
// { success: true/false, code: ..., data: { value, requestCode } }
if (res.success) {
const value = res.data.value;
const reqCode = res.data.requestCode;
console.log("QR scan success:", value, "(req:", reqCode, ")");
// TODO: handle the scanned value (navigate, fill form, etc.)
} else {
console.warn("QR scan failed", res);
// TODO: show error message or retry option
}
});
2. Démarrer un scan QR depuis JS¶
Lorsque vous souhaitez démarrer le scan :
function startQrScan() {
const REQUEST_CODE_QR = 1001; // any integer you want to track this call
bridgeWebView.callHandler(
"startQrCodeScan",
String(REQUEST_CODE_QR), // this becomes requestCode in native
function (response) {
// Initial response from native (permission / immediate errors)
const res = JSON.parse(response || "{}");
if (res.success === false) {
console.error("Failed to start QR scan:", res);
// Probably permission denied or some immediate error.
} else {
// In most cases, you simply wait for scanQrcodeResultCallBack.
console.log("QR scan started; waiting for result...");
}
}
);
}
Flux du point de vue JS :
- Appeler
startQrCodeScan. - Inspecter optionnellement la réponse immédiate (pour les erreurs de permission).
- Attendre
scanQrcodeResultCallBackavec le résultat final.
Structure de la Charge Utile de Résultat¶
Toutes les réponses sont encapsulées dans la classe Result interne avant d'être envoyées à JS, par exemple :
-
Succès (forme conceptuelle) :
{ "success": true, "code": 0, "data": { "value": "scanned-qr-content", "requestCode": 1001 } } -
En cas d'échec
{ "success": false, "code": "<error-code>", "message": "<human-readable message>" }
Les clés exactes (code, message, etc.) dépendent de votre implémentation de Result, mais l'important est que data.value contient la chaîne scannée et data.requestCode vous permet de mapper un résultat à une requête de scan spécifique.
Gestion des Erreurs et Cas Limites¶
-
Permissions refusées
startQrCodeScanretourne immédiatement avecResult.fail(PERMISSION_ERROR, false).- L'IU de scan n'est jamais ouverte.
- JS doit afficher un message demandant à l'utilisateur d'accorder la permission caméra.
-
Le scan retourne un résultat null (
obj == null)scanQrcodeResultCallBackest appelé avecResult.fail().- JS doit traiter cela comme « scan échoué » et permettre la nouvelle tentative.
-
L'utilisateur annule ScanKit
resultCoden'est pasRESULT_OK.- La branche QR est ignorée dans
onActivityResult. - Aucune charge utile de succès n'est envoyée ; JS doit gérer cela comme « aucun résultat » (ex. par délai d'expiration, ou par flux utilisateur).
-
Exceptions inattendues lors du lancement de ScanKit
- Actuellement journalisées uniquement via
e.printStackTrace(). - Vous pouvez étendre cela pour envoyer un
Resultd'échec à JS pour une meilleure expérience utilisateur.
- Actuellement journalisées uniquement via
Résumé¶
-
L'implémentation du scan QR fournit une API propre et de haut niveau à WebView :
- Point d'entrée JS :
startQrCodeScan - Scanner natif : Huawei ScanKit via
ScanKitActivity - Rappel de résultat vers JS :
scanQrcodeResultCallBackavec une charge utile encapsulée dans Result contenant la valeur scannée et le requestCode.
- Point d'entrée JS :
Cela maintient tous les détails de la caméra et de ScanKit dans la couche native, tout en gardant l'intégration WebView simple et prévisible.