Aller au contenu

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    │
└──────────┘
  1. JavaScript appelle la méthode de pont natif startQrCodeScan.
  2. Le code natif vérifie et demande les permissions nécessaires (caméra, etc.).
  3. Si les permissions sont accordées, le natif configure Huawei ScanKit et lance ScanKitActivity.
  4. L'utilisateur scanne un QR code (ou annule).
  5. 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 :

  1. Demande les permissions d'exécution en utilisant XXPermissions (avec un PermissionInterceptor personnalisé).
  2. 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;
    
  3. 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_TYPE active 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_ONE est utilisé ultérieurement dans onActivityResult pour 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_ONE et resultCode == RESULT_OK le résultat est traité comme un résultat de scan valide.
  • HmsScan obj = data.getParcelableExtra(ScanUtil.RESULT); extrait l'objet résultat HmsScan de l'intent.
  • obj.originalValue contient 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 obj est non-null :

    • Une map est 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 via scanQrcodeResultCallBack.
  • Si obj est null :

    • Le natif appelle scanQrcodeResultCallBack avec Result.fail().

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 :

  1. Appeler startQrCodeScan.
  2. Inspecter optionnellement la réponse immédiate (pour les erreurs de permission).
  3. Attendre scanQrcodeResultCallBack avec 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

    • startQrCodeScan retourne immédiatement avec Result.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)

    • scanQrcodeResultCallBack est appelé avec Result.fail().
    • JS doit traiter cela comme « scan échoué » et permettre la nouvelle tentative.
  • L'utilisateur annule ScanKit

    • resultCode n'est pas RESULT_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 Result d'échec à JS pour une meilleure expérience utilisateur.

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 : scanQrcodeResultCallBack avec une charge utile encapsulée dans Result contenant la valeur scannée et le requestCode.

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.