Aller au contenu

Demander à la doc

Les réponses viennent uniquement de cette documentation, sources à l’appui. Pour les sujets propres à votre compte, la doc vous oriente vers le bon interlocuteur.

Que cherchez-vous ?

Vous discutez avec un assistant IA. Il peut se tromper, pensez à vérifier les informations importantes.

Application embarquée sur Android

Sur Android, votre application conseiller charge l’application embarquée dans une WebView plutôt que dans une iframe. L’application échange avec votre code Kotlin par un pont que vous injectez, avec les mêmes messages que sur le web, plus ce qu’un téléphone demande : le bouton retour du système, les liens à ouvrir hors de l’application, et un scanner que votre application lance quand la WebView ne sait pas scanner.

Le reste ne change pas : la configuration dans le tableau de bord, les clés de signature et le token conseiller que signe votre backend.

Votre application Android (WebView) Dialog
loadUrl ────────────────────────────────▶ application embarquée
◀── dialog:ready
──▶ dialog:auth { token } (signé par votre backend)
──▶ dialog:shopper { email }
──▶ dialog:back (bouton retour du système)
◀── dialog:back-result { handled }
◀── dialog:scan-requested (la WebView ne sait pas scanner)
──▶ dialog:open-product { productId }
◀── dialog:open-url { url } (le lien de paiement)
◀── dialog:pre-cart-sent

L’application se charge au premier niveau de la WebView, pas dans une iframe : sur Android, il n’y a pas d’origine autorisée à déclarer dans le tableau de bord. C’est votre application qui réserve le pont à l’origine de Dialog.

  • androidx.webkit 1.3 ou plus récent, pour WebViewCompat.addWebMessageListener. Vérifiez WebViewFeature.WEB_MESSAGE_LISTENER à l’exécution.
  • androidx.browser, pour ouvrir le lien de paiement dans un Custom Tab.
  • La permission CAMERA dans votre manifeste, demandée à l’exécution, si le conseiller scanne dans l’application.
private const val DIALOG_ORIGIN = "https://assistant-app.askdialog.com"
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.settings.mediaPlaybackRequiresUserGesture = false
// Sans le pont, l'application ne reçoit pas son token : prévenez au lieu de la charger.
if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
showWebViewUpdateRequired()
return
}
// Avant loadUrl : le pont est injecté au chargement de la page.
WebViewCompat.addWebMessageListener(webView, "DialogHost", setOf(DIALOG_ORIGIN)) {
_, message, _, _, replyProxy ->
dialogHost = replyProxy
message.data?.let { onDialogMessage(JSONObject(it)) }
}
webView.loadUrl("$DIALOG_ORIGIN/?apiKey=VOTRE_CLE_API_PUBLIQUE&locale=fr-FR&scan=native")

Le pont doit s’appeler DialogHost, et sa règle d’origine doit être exactement l’origine de Dialog : le pont n’existe alors que dans la page de Dialog, jamais dans une page vers laquelle un lien mènerait. Les messages sont du texte JSON dans les deux sens.

Paramètre Obligatoire Description
apiKey oui Votre clé API publique, comme sur le web.
locale non Une locale BCP 47 (fr-FR, en-GB). Par défaut, celle de l’appareil.
countryCode non Pays sur deux lettres, quand le marché diffère de la région de la locale.
scan non native quand votre application sait scanner les codes elle-même. Voir Scanner.

Votre application ne peut répondre qu’après le premier message de l’application : le replyProxy arrive avec dialog:ready.

private var dialogHost: JavaScriptReplyProxy? = null
private fun send(type: String, vararg fields: Pair<String, Any>) {
val message = JSONObject().put("type", type)
fields.forEach { (key, value) -> message.put(key, value) }
dialogHost?.postMessage(message.toString())
}
private fun onDialogMessage(message: JSONObject) {
when (message.getString("type")) {
"dialog:ready", "dialog:token-expiring" -> lifecycleScope.launch {
send("dialog:auth", "token" to backend.fetchDialogToken())
}
"dialog:back-result" -> if (!message.getBoolean("handled")) finish()
"dialog:open-url" -> CustomTabsIntent.Builder().build()
.launchUrl(this, Uri.parse(message.getString("url")))
"dialog:scan-requested" -> scanner.scan { code ->
send("dialog:open-product", "productId" to code)
}
"dialog:pre-cart-sent" -> {
// draftOrderName, invoiceUrl, invoiceSent
}
}
}
Message Contenu Quand
dialog:ready aucun L’application est chargée. Répondez par dialog:auth, puis dialog:shopper si vous le connaissez.
dialog:token-expiring aucun Environ 60 secondes avant l’expiration du token, ou après un refus de Dialog. Répondez par un nouveau dialog:auth.
dialog:back-result { handled } La réponse à dialog:back. false sur l’écran d’accueil de l’application : fermez l’écran vous-même.
dialog:open-url { url } Un lien que l’application ne peut pas ouvrir : une WebView le chargerait à la place de l’application. Aujourd’hui, le lien de paiement d’un pré-panier envoyé. Ouvrez-le dans un Custom Tab.
dialog:scan-requested aucun Le conseiller veut scanner et la WebView ne sait pas le faire. Scannez, puis répondez par dialog:open-product.
dialog:pre-cart-sent { draftOrderName, invoiceUrl, invoiceSent } Comme sur le web.
Message Contenu Rôle
dialog:auth { token } Le token conseiller, comme sur le web.
dialog:shopper { email?, customerId? } Le client que sert le conseiller, comme sur le web.
dialog:back aucun Le bouton retour du système. L’application ferme son panneau ouvert ou revient d’un écran, puis répond par dialog:back-result.
dialog:open-product { productId } Ouvre l’assistant sur un produit : le code lu par votre scanner, ou un produit choisi par votre application.

Transmettez chaque appui sur retour à l’application, et ne quittez l’écran que quand elle répond n’avoir rien sur quoi revenir.

onBackPressedDispatcher.addCallback(this) {
if (dialogHost == null) finish() else send("dialog:back")
}

L’application scanne les QR codes avec la caméra quand la WebView le permet. Toutes les WebView Android ne le permettent pas, selon l’appareil. Avec scan=native dans l’URL, l’application passe par votre scanner quand elle ne sait pas scanner, ou quand le conseiller a refusé la caméra : elle envoie dialog:scan-requested, et vous répondez par dialog:open-product. N’importe quel scanner convient, par exemple la lecture de codes-barres de ML Kit.

Le QR code contient l’identifiant du produit, comme sur le web. Le conseiller peut toujours saisir le code à la place.

Pour le scanner de la WebView, accordez la caméra à la page de Dialog une fois que votre application a la permission CAMERA :

webView.webChromeClient = object : WebChromeClient() {
override fun onPermissionRequest(request: PermissionRequest) {
val fromDialog = request.origin.toString().trimEnd('/') == DIALOG_ORIGIN
if (fromDialog && PermissionRequest.RESOURCE_VIDEO_CAPTURE in request.resources) {
request.grant(arrayOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE))
} else {
request.deny()
}
}
}