J'ouvre cette issue pour démarrer une discussion sur comment va-t-on faire pour faire marcher les intents sur mobile. J'ai essayé de proposer quelques pistes et aimerait avoir vos retours là-dessus.
Cas d'usages d'intent
- Ouverture d'un fichier dans drive (pour le moment implémentée en redirigeant l'utilisateur sur une URL spécifique de Drive https://mycozy-drive/#files/{factureID})
- Récupération de l'URL de téléchargement d'un fichier (pour le moment fonctionnalité non implémentée car EDF n'est pas une application mobile native)
- Recherche
Intent via inAppBrowser au lieu d'une iframe
L'utilisation de inAppBrowser pose le problème de la communication entre l'application et le service. Sur mobile nous ne pouvons pas utiliser les iframe
s à cause des politiques de sécurité de Cordova.
En effet, pour pouvoir ouvrir une iframe il faut inscrire son domain dans la whitelist
et
étant donné que nous ne connaissons pas le domaine en avance, nous ne pouvons pas le faire.
Nous pouvons cependant utiliser inAppBrowser
qui utilise une véritable fenêtre pour ouvrir
la page demandée.
Via postMessage, executeScript
const w = window.open('[Adresse de l'intent]', '_blank')
La communication entre les deux fenêtres semble difficile (surtout de l'inappbrowser vers l'app).
J'ai essayé (comme préconisé sur https://www.telerik.com/blogs/cross-window-communication-with-cordova's-inappbrowser) :
w.executeScript({ code: 'localStorage.setItem("hello", "world")' })
w.executeScript({ code: 'localStorage.getItem("hello")' }, function (values) { console.log(values) })
La valeur dans localStorage est bien inscrite mais on ne peut pas la récupérer ☹️. Cela semble un
problème qu'on plusieurs personnes : https://www.google.fr/search?q=inappbrowser+callback+not+firing&oq=inappbrowser+callback+not+firing&aqs=chrome..69i57j69i60l3.4359j0j4&sourceid=chrome&ie=UTF-8. Avec les politiques
de CSP strictes que l'on a, je doute que cela ne marche.
✅ On peut cependant écouter sur l'évenement "exit" de la fenêtre.
w.addEventListener('exit', () => { checkIfTheUserHasCompletedTheIntent() })
Stocker valeur de retour dans l'intent
Stocker une valeur de retour dans l'intent (via un HTTP POST
par leur service et un HTTP GET
par l'application) permettrait sa lecture par l'application à la fermeture de l'inAppBrowser.
⚠️ Nécessite une route PUT
sur l'intent pour le modifier (seulement la valeur de retour doit être modifiable, juste une fois).
Websocket pour communication permanente
Idée pour communiquer des valeurs : passer par une websocket. Le service et l'application
recevrait de la stack un id + token ou ils pourraient communiquer. Inconvénients:
complexité chez la stack, latence, 2 connexions en plus par intent. Avantage : assez générique et
niveau de confiance plutôt élevé sur le fait que ca marche.
Abstraction du channel de communication app/service
Ces 2 possibilités d'ouverture/communication entre l'application et le service (via une iframe/postMessage, inAppBrowser/HTTP GET intents/id/returnValue
) nécessiterait une abstraction niveau cozy-intent
pour que l'utilisateur n'ait pas à se préoccuper de ces détails bas-niveaux, idéalement juste utiliser une option { mobile: true }
?
Possibles améliorations
Login automatique sur la stack
L'inAppBrowser ne partage pas ses cookies avec la webview Cordova, à l'ouverture de l'intent, l'utilisateur devrait donc se logguer. Pour améliorer l'expérience utilisateur, il pourrait être intéressant de le logguer directement dans l'inappbrowser, est ce que ce serait possible de demander à la stack un token d'authentification à n'utiliser qu'une fois et qui permettrait à l'utilisateur de ne pas avoir à retaper son mot de passe ?
w = window.open('https://mydomain.cozy.cloud/loginViaToken={token}&redirect={intentURL}', '_blank')
Nettoyage des intents
Le nettoyage des intents n'a pas été implémenté, l'idée étant de les supprimer juste après leur lecture par le service. Si l'on souhaitere stocker une valeur de retour dans l'intent pour lecture par l'application mobile, il faudrait le supprimer après lecture de la valeur de retour. Cela pourrait être configuré au lancement de l'intent avec une option spécifique { deleteAfter: 'readByService' }
, { deleteAfter: 'readByApp' }
. Autre possibilité : l'option {mobile:true}
évoquée plus haut pourrait abstraire ce détail.