Apache Adobe Flex TutorialTutoriaux Adobe Flex & AIR en Français

23mai/110

AIR Mobile – Afficher des pop-up natives depuis AIR (hack)

Le dernier tutorial présentait comment accéder aux contacts sous iOS sans passer par une API native:

AIR Mobile – Accéder aux contacts sous iOS (Address Book)

Ce n'était pas vraiment un "hack", juste un contournement. Ici, on va voir comment afficher des pop-up natives (qui ont donc le "chrome" de l'OS) depuis une application AIR Mobile. Attention, cette méthode est un vrai hack des familles et comme toute hack qui se respecte, a un point noir que j'explique à la fin du tutorial.

La technique : StageWebView et alert

Ce n'est pas moi qui ai trouvé cette technique mais quelqu'un l'a partagée sur le forum de la prerelease. Autant que tout le monde y ait accès :) . Si vous ne connaissez pas le composant StageWebView, celui-ci sert à embarquer un navigateur HTML natif là ou la classe HTMLLoader n'est pas supportée. Ce composant est supporté sur AIR Desktop et mobile (toutes plate-formes) mais pas sur AIR pour TV.

On peut donc ajouter un StageWebView et lui charger du contenu, dont du contenu JavaScript qui va être exécuté par le navigateur. C'est en exécutant du JavaScript (une sorte d'injection) que l'on va réaliser notre hack. En effet, lorsque vous faites un alert() dans du JavaScript sur une plate-forme mobile, vous avez une pop-up native qui va s'ouvrir et afficher le texte. Celle-ci respecte l'OS et les styles définis sur le portable de l'utilisateur.

Si vous avez un peu d'expérience en JavaScript, vous savez qu'il existe 3 méthodes différentes:

  • alert : qui affiche un simple message
  • confirm : qui demande à l'utilisateur Oui ou Non (boutons) + un message
  • prompt : qui demande à l'utilisateur d'entrer un texte + un message

Bon ce sont les méthodes classique un peu "old school" et la plupart vont utiliser des pop-ups jQuery pour faire le travail et styler cela.

Exécuter du code JavaScript dans une StageWebView

Pour exécuter du JS dans une StageWebView, c'est plutôt simple, il suffit de passer le code JS sous forme de String à la méthode loadString de StageWebView. Dans notre cas, on va faire:

_webView.loadString("<script>alert('Thanks for the click');</script>");

En faisant cela, une pop-up native va s'ouvrir. Cependant, pour les méthodes confirm et prompt, on va chercher à récupérer le résultat de la saisie utilisateur. En effet, ces 2 méthodes renvoient une valeur (Boolean pour confirm et String pour prompt) que l'on va devoir récupérer. Là on commence à rentrer un peu plus dans le hack puisqu'il n'est pas possible de base de faire communiquer JavaScript -> ActionScript avec StageWebView.

Réaliser une communication JavaScript -> ActionScript

La solution que certains ont trouvé  est d'utiliser la variable "location" indiquant l'URL de la page. En effet, depuis le code JavaScript, on peut modifier l'url de la page courante pour la navigation en modifiant window.location. Et lorsque l'utilisateur essaie de naviguer vers une autre page, StageWebView propage un évènement que l'on peut capter.

Là où ça devient malin, c'est que s'il on appelle la méthode preventDefault() sur cet évènement, on va saborder l'opération et rien de se passera, aucun changement de page. Mais entre temps, vous aurez récupéré l'URL de la page vers laquelle on a demandé la navigation. On peut utiliser cette URL pour faire transiter des variables au format String. Pour un format plus compact, vous pouvez réaliser un encodage / décodage en Base64.

Il ne faut pas oublier aussi qu'il est possible que dans l'utilisation classique d'une StageWebView, l'utilisateur a le droit de naviguer. On va donc préfixer notre url, avec "bridge___" par exemple pour indiquer que l'on est simplement en train de faire passer une variable.

Attention, n'utilisez pas un "pseudo-protocole" comme on peut le voir dans certains tutoriaux. Ne préfixez pas par bridge:// car iOS va vous dire qu'il ne connait pas le protocole et l'évènement ne sera jamais lancé. Cela marche cependant sous Android.

Le code de l'application

Voici le code de la mini-application que j'ai créé pour tester ce fonctionnement:

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">

  <fx:Script>
    <![CDATA[
      private static const BRIDGE_ID:String = "bridge___";

      private var _webView:StageWebView = null;
      protected function generateAlert(event:MouseEvent):void
      {
        var content:String = "alert('Thanks for the click')";
        loadContent(content);
      }
      protected function generateConfirm(event:MouseEvent):void
      {
        var content:String = "if(confirm('Are you sure?')){window.location = '"+BRIDGE_ID+"confirm YES';} else {window.location = '"+BRIDGE_ID+"confirm NO';}";
        loadContent(content);
      }
      protected function generatePrompt(event:MouseEvent):void
      {
        var content:String = "var pr = prompt('What is your name?');if (pr != null){window.location = '"+BRIDGE_ID+"prompt:'+pr}";
        loadContent(content);
      }

      private function loadContent(content:String):void
      {
        if (StageWebView.isSupported) {
          if (!_webView) {
            _webView = new StageWebView();
            _webView.addEventListener(LocationChangeEvent.LOCATION_CHANGING, onLocationChanging);
            _webView.stage = this.stage;
            _webView.viewPort = new Rectangle(0, 0, 0, 0);
          }
          _webView.loadString("<script>"+content+";</script>");
        } else {
          trace("StageWebView not supported!");
        }
      }

      protected function onLocationChanging(event:LocationChangeEvent):void{
        var location:String = event.location;
        if (location.indexOf(BRIDGE_ID) != -1){
          event.preventDefault();
          var value:String = location.substr(BRIDGE_ID.length);
          lbl.text = "Retour from SWV: " + value;
        }
      }

    ]]>
  </fx:Script>
  <s:layout>
    <s:VerticalLayout />
  </s:layout>
  <s:Button label="Alert" click="generateAlert(event)" />
  <s:Button label="Confirm" click="generateConfirm(event)" />
  <s:Button label="Prompt" click="generatePrompt(event)" />
  <s:Label id="lbl" width="100%" />
</s:View>

Et voilà quelques exemples de rendu sur Galaxy S et iPad:

photophoto (2)SC20110521-192502SC20110521-192452SC20110521-192432
photo (1)

Le gros problème de cette méthode

Cette méthode aurait pu être un hack bien cool mais quand vous regardez les screenshots, vous remarquez que le titre de la pop-up est totalement arbitraire et dépend des plate-formes. C'est en fait le même problème en web "classique", vous ne pouvez pas changer le titre d'une alert (il y a un hack en VB pour IE mais passons). C'est aussi pour cela que personne ne fait des alert mais préfère créer une pop-up à lui.

Dommage :)

Articles similaires

Commentaires (0) Trackbacks (0)

Aucun commentaire pour l'instant


Leave a comment

(required)

Aucun trackbacks pour l'instant