PostMessage pour TWA

Sayed El-Abady
Sayed El-Abady

Depuis Chrome 115, les activités Web fiables (TWA) peuvent envoyer des messages à l'aide de postMessages. Cet article décrit la configuration nécessaire à la communication entre votre application et le Web.

À la fin de ce guide, vous saurez : - Comprendre le fonctionnement du client et de la validation du contenu Web - Savoir initialiser le canal de communication entre le client et le contenu Web - Savoir envoyer et recevoir des messages à partir de contenus Web

Pour suivre ce guide, vous aurez besoin des éléments suivants:

  • Pour ajouter la dernière bibliothèque androidx.browser (version min. 1.6.0-alpha02) à votre fichier build.gradle.
  • Chrome version 115.0.5790.13 ou ultérieure pour TWA.

La méthode window.postMessage() permet en toute sécurité la communication multi-origine entre les objets Window. (par exemple, entre une page et un pop-up qu'elle a générés, ou entre une page et un iFrame intégré).

En règle générale, les scripts présents sur des pages différentes ne sont autorisés à accéder les uns aux autres que si elles proviennent de la même origine, et partagent le même protocole, le même numéro de port et le même hôte (également appelé règle d'origine identique). La méthode window.postMessage() fournit un mécanisme contrôlé pour communiquer de manière sécurisée entre différentes origines. Cela peut être utile pour implémenter des applications de chat, des outils collaboratifs, etc. Par exemple, une application de chat peut utiliser postMessage pour envoyer des messages entre des utilisateurs qui se trouvent sur différents sites Web. L'utilisation de postMessage dans les activités Web fiables (TWA) peut s'avérer un peu délicate. Ce guide vous explique comment utiliser postMessage dans le client TWA pour envoyer et recevoir des messages depuis la page Web.

Ajouter l'application à la validation Web

Pour que le postMessage fonctionne, il faut établir une relation valide entre un site Web et l'application Trusted Web Activity qui lance ce site. Pour ce faire, utilisez Digital Asset Links (DAL) en ajoutant le nom du package de l'application dans votre fichier assetlinks.json avec la relation use_as_origin, comme suit:

[{
  "relation": ["delegate_permission/common.use_as_origin"],
  "target" : { "namespace": "android_app", "package_name": "com.example.app", "sha256_cert_fingerprints": [""] }
}]

Notez que si vous configurez la propriété sur l'origine associée à la TWA, vous devez indiquer une origine dans le champ MessageEvent.origin. Toutefois, postMessage peut être utilisé pour communiquer avec d'autres sites qui n'incluent pas Digital Assets Link. Par exemple, si vous êtes propriétaire de www.example.com, vous devrez le prouver via DAL, mais vous pouvez communiquer avec d'autres sites Web, comme www.wikipedia.org.

Ajouter le service PostMessageService à votre fichier manifeste

Pour recevoir des communications postMessage, vous devez configurer le service en ajoutant PostMessageService à votre fichier manifeste Android:

<service android:name="androidx.browser.customtabs.PostMessageService"
android:exported="true"/>

Obtenir une instance CustomTabsSession

Après avoir ajouté le service au fichier manifeste, utilisez la classe CustomTabsClient pour le lier. Une fois connecté, vous pouvez utiliser le client fourni pour créer une session comme suit. CustomTabsSession est la classe principale permettant de gérer l'API postMessage. Le code suivant montre qu'une fois le service connecté, le client est utilisé pour créer une session, cette session est utilisée pour postMessage:

private CustomTabsClient mClient;
private CustomTabsSession mSession;

// We use this helper method to return the preferred package to use for
// Custom Tabs.
String packageName = CustomTabsClient.getPackageName(this, null);

// Binding the service to (packageName).
CustomTabsClient.bindCustomTabsService(this, packageName, new CustomTabsServiceConnection() {
 @Override
 public void onCustomTabsServiceConnected(@NonNull ComponentName name,
     @NonNull CustomTabsClient client) {
   mClient = client;

   // Note: validateRelationship requires warmup to have been called.
   client.warmup(0L);

   mSession = mClient.newSession(customTabsCallback);
 }

 @Override
 public void onServiceDisconnected(ComponentName componentName) {
   mClient = null;
 }
});

Vous vous demandez maintenant à quoi correspond cette instance customTabsCallback ? Nous le créerons dans la section suivante.

Créer un rappel CustomTabsCallback

CustomTabsCallback est une classe de rappel permettant au CustomTabsClient de recevoir des messages concernant des événements dans ses onglets personnalisés. L'un de ces événements est onPostMessage. Il est appelé lorsque l'application reçoit un message du Web. Ajoutez le rappel au client pour initialiser le canal postMessage afin de lancer la communication, comme indiqué dans le code suivant.

private final String TAG = "TWA/CCT-PostMessageDemo";
private Uri SOURCE_ORIGIN = Uri.parse("my-app-origin-uri");
private Uri TARGET_ORIGIN = Uri.parse("website-you-are-communicating-with");

// It stores the validation result so you can check on it before requesting postMessage channel, since without successful validation it is not posible to use postMessage.
boolean mValidated;

CustomTabsCallback customTabsCallback = new CustomTabsCallback() {

// Listens for the validation result, you can use this for any kind of
// logging purposes.
 @Override
 public void onRelationshipValidationResult(int relation, @NonNull Uri requestedOrigin,
     boolean result, @Nullable Bundle extras) {
   // If this fails:
   // - Have you called warmup?
   // - Have you set up Digital Asset Links correctly?
   // - Double check what browser you're using.
   Log.d(TAG, "Relationship result: " + result);
   mValidated = result;
 }

// Listens for any navigation happens, it waits until the navigation finishes
// then requests post message channel using
// CustomTabsSession#requestPostMessageChannel(sourceUri, targetUri, extrasBundle)

// The targetOrigin in requestPostMessageChannel means that you can be certain their messages are delivered only to the website you expect.
 @Override
 public void onNavigationEvent(int navigationEvent, @Nullable Bundle extras) {
   if (navigationEvent != NAVIGATION_FINISHED) {
     return;
   }

   if (!mValidated) {
     Log.d(TAG, "Not starting PostMessage as validation didn't succeed.");
   }

   // If this fails:
   // - Have you included PostMessageService in your AndroidManifest.xml ?
boolean result = mSession.requestPostMessageChannel(SOURCE_ORIGIN, TARGET_ORIGIN, new Bundle());
   Log.d(TAG, "Requested Post Message Channel: " + result);
 }

// This gets called when the channel we requested is ready for sending/receiving messages.
 @Override
 public void onMessageChannelReady(@Nullable Bundle extras) {
   Log.d(TAG, "Message channel ready.");

   int result = mSession.postMessage("First message", null);
   Log.d(TAG, "postMessage returned: " + result);
 }

// Listens for upcoming messages from Web.
 @Override
 public void onPostMessage(@NonNull String message, @Nullable Bundle extras) {
   super.onPostMessage(message, extras);
// Handle the received message.

 }
};

Communiquer depuis le Web

Nous pouvons maintenant envoyer et recevoir des messages depuis notre application hôte. Comment faire de même depuis le Web ? La communication doit commencer par l'application hôte, puis la page Web doit obtenir le port à partir du premier message. Ce port est utilisé pour communiquer avec vous. Votre fichier JavaScript doit ressembler à l'exemple suivant:

window.addEventListener("message", function (event) {
  // We are receiveing messages from any origin, you can check of the origin by
  // using event.origin

  // get the port then use it for communication.
  var port = event.ports[0];
  if (typeof port === 'undefined') return;

  // Post message on this port.
  port.postMessage("Test")

  // Receive upcoming messages on this port.
  port.onmessage = function(event) {
    console.log("[PostMessage1] Got message" + event.data);
  };
});

Un exemple complet est disponible sur cette page.

Photo de Joanna Kosinska, publiée sur Unsplash