lundi 2 mai 2016

Notre comportement sur la route au Maroc

J'aimerai user de mon droit d'expression en tant que citoyen marocain pour aborder le sujet inquiétant de notre comportement à nous, marocains, sur la route. Je ne voudrais pas aborder le côté juridique ni les polémiques du nouveau code de la route, seulement des remarques et des appels au bon sens qui siège à l'intérieur de chacun de nous.
Je ne suis pas parfait moi même et je suis ouvert à toutes les remarques et propositions pour améliorer notre quotidien sur la route. Je tiens à préciser que je ne pointe personne en particulier. C'est juste un effort pour relever ce qui ne va pas d'après mon expérience et proposer des solutions.
Afin de mieux clarifier mes idées, je les ai renforcé avec des exemples animés dans la présentation ci-dessous.

vendredi 16 décembre 2011

MoSync : Communication entre JavaScript et C++ dans MoSync


Ce document indique comment le pont entre JavaScript et C++ marche dans MoSync. C’est la suite du cours Création de projet HTML5 sur MoSync.

Comment invoquer du code C++ depuis JavaScript

Ce qui suit assume que voue avez créé un projet en utilisant la template HTML5/C++ Project. Le code utilise les classes WebAppMoblet et WebViewMessage dans la librairie Wormhole.
Ceci est la fonction JavaScript dans le fichier index.html qui invoque le code qui fait vibrer le mobile :
/**
 * Fait vibrer le mobile.
 */
function Vibrate()
{
      // C’est ainsi qu’un message est passé au C++ lorsqu’on utilise la librairie de pont (Bridge)
      bridge.messagehandler.send({messageName: "Vibrate"}, null);
}
Et ceci est le code C++ dans main.cpp qui gère le message:
/**
 * Cette fonction gère les messages envoyés depuis WebView
 * @param webView Le WebView qui a envoyé le message.
 * @param urlData L’objet de données qui contient le contenu du message.
 * Veuillez noter que l’objet de données sera valide uniquement lors de la durée de vie de l’appel de cette fonction, puis il sera libéré*/

void handleWebViewMessage(WebView* webView, MAHandle urlData)
{
      // Crée l’objet message. Ceci scanne le message.
      WebViewMessage message(webView, urlData);

      if (message.is("Vibrate"))
      {
            // Fait vibrer le message pendant une demi-seconde.
            // Dans iOS, la longueur de la vibration sera constante,
            // peut importe la valeur du paramètre time.
            maVibrate(500);
      }

      // Indique à WebView que le message a été traité
      // ainsi il peut envoyer le suivant.
      callJS("bridge.messagehandler.processedMessage()");
}
La dernière ligne, qui appelle la fonction JavaScript bridge.messagehandler.processedMessage(), est là pour indiquer au pont JavaScript qu’il peut envoyer le message suivant. Ceci aurait pu être appelé par code dans la superclasse WebAppMoblet, mais afin d’éviter de coupler WebAppMoblet à une librairie JavaScript particulière, ceci n’est pas fait automatiquement.
La fonction bridge.messagehandler.send() prend 2 paramètres :
bridge.messagehandler.send(message, callbackFunction)
Le paramètre ‘message’ est un dictionnaire, qui contient le nom du message et optionnellement d’autres paramètres du message. Le paramètre ‘callbackFunction’ est une fonction que vous pouvez appeler depuis C++ pour retourner une valeur à JavaScript. Le mécanisme de communication est asynchrone, c’est pourquoi une fonction de callback est utilisée pour faire passer le résultat à JavaScript.
Si vous voulez faire passer des paramètres à C++, vous ajouter le paramètre comme champs dans message, par exemple :
bridge.messagehandler.send(
          { messageName: "Vibrate",
            duration: "1000" },    null);
Dans le code C++, c’est ainsi que vous récupérez le paramètre du message:
void handleWebViewMessage(WebView* webView, MAHandle urlData)
{
      // Crée un objet message. Ceci scanne le message.
      WebViewMessage message(webView, urlData);

      if (message.is("Vibrate"))
      {
            // Récupère le paramètre "duration" comme une valeur int et vibre pendant ce nombre de millisecondes.
            int duration = message.getParamInt("duration");
            maVibrate(duration);
      }

      // Indique à WebView que nous avon traité le message, ainsi il peut envoyer le suivant.
      callJS("bridge.messagehandler.processedMessage()");
}
Si vous voulez récupérer la valeur String d’un paramètre, utilisez la fonction getParam  de l’objet message.

Comment invoquer le code JavaScript depuis C++

Pour invoquer du code JavaScript dans WebView depuis C++, vous pouvez utiliser la fonction WebAppMoblet::callJS(). Voici un exemple :
callJS("alert('Hello World')");
Ce qui suit illustre comment invoquer C++ depuis JavaScript et appeler une function de callback JavaScript. Voici la partie JavaScript :
bridge.messagehandler.send(
      { messageName: "GetScreenSize" },
      function(width, height) {
            alert("Screen size: " + width + " " + height);
      });
Voici la partie C++:
void handleWebViewMessage(WebView* webView, MAHandle urlData)
{
      WebViewMessage message(webView, urlData);
       
      if (message.is("GetScreenSize"))
      {
            // Appeler le handler de réponse JavaScript pour ce message.
            char script[512];
            sprintf(
                  script,
                  "bridge.messagehandler.reply(%s, %d, %d)",
                  message.getParam("callbackId").c_str(),
                  EXTENT_X(maGetScrSize()),
                  EXTENT_Y(maGetScrSize()));
            getWebView()->callJS(script);
      }
       
      callJS("bridge.messagehandler.processedMessage()");
}

MoSync : Création de projet HTML5

Vous pouvez utiliser MoSync pour créer une application en HTML5 et JavaScript sans avoir à écrire même pas une ligne en C/C++. Ce cours indique comment créer un projet où vous pouvez construire des applications mobiles en utilisant uniquement HTML5 et JavaScript, ou en ajoutant le pouvoir du C/C++.

Le support des applications HTML5 par MoSync

MoSync contient un système de widgets natif, l’un des types de widgets est le WebView. Ceci veut dire que que vous pouvez utiliser les technologies Web pour créer vos applications. Par contre, ces applications seront uniquement compatibles avec les mobiles supportant nativement les technologies Web à travers Wormhole ou autre.
Vous pouvez à partir de MoSync 2.7, créer des applications natives avecc HTML, CSS et javascript et les déployer dans les app-stores comme des applications standalone. Vous pouvez utiliser des standards web les librairies JavaScript telles ques jQuery et jQTouch, et réutiliser votre code Web existant.
Vous pouvez programmer votre application entière en HTML, CSS et JavaScript ou vous pouvez mixer les technologies Web avec le C++ qui vous donnera accès aux fonctionnalités non disponibles dans HTML5. Selon les langages et les librairies que vous voulez utiliser, vous pouvez écrire la grande partie de la logique de l’application en JavaScript, ou vous pouvez utiliser le C++ pour la logique de l’application et utiliser l’HTML et le CSS pour l’interface utilisateur.

Les templates HTML5/Javascript

Pour commencer, ouvrez l’IDE MoSync, sélectionnez File > New > Project, créez un projet HTML5/JavaScript, et sélectionnez HTML5 Project template comme présenté dans la capture d’écran suivante :

Ceci vous donnera un projet HTML5 qui est prêt pour la compilation et l’exécution. Vous n’avez pas besoin de n’écrire aucune ligne de C++ si vous choisissez cette option.
Si vous auriez besoin d’accéder au code écrit en C++ depuis JavaScript, vous devriez créer un projet en utilisant la template HTML5/C++ Project. La différence entre un HTML5 Project et un HTML5/C++ Project est que le dernier est configuré pour supporter les communications entre JavaScript et C++.
Les templates sont des applications à part entière. Voici comment l’application template s’affiche lorsqu’elle s’exécute. Dans les 2 applications le texte changera lorsque vous touchez l’écran. Dans une template d’application HTML5/C++ Project, le mobile va également vibrer.

Veuillez noter que vous pouvez manuellement ajouter le code nécessaire pour les interactions JavaScript/C++ dans un projet HTML5 Project, dans le cas où vous voulez ajouter des fonctionnalités qui nécessitent que programmiez en C++ (par exemple, pour accéder aux fonctionnalités natives du mobile qui ne sont pas implémentées par le widget navigateur WebView).

La structure du système de fichier

Un projet HTML5 a un répertoire nommé /LocalFiles. C’est là où vous devriez mettre vos fichiers média, HTML, CSS et JavaScript.
Voici les détails techniques. Le contenu de /LocalFiles est packagé dans une étape de compilation nommée ‘Bundle’, vers un fichier LocalFiles.bin dans le répertoire /Resources. Dans /Resources, il y’a un fichier nommé Resource.lst, qui inclut LocalFiles.bin. Lors du démarrage de l’application, ce fichier est décompressé et ses contenus sont copiés dans le système de fichier réservé pour l’application dans le mobile.
Ceci est le structure du système de fichier que vous aurez lorsque vous créez un projet template (Veuillez noter que LocalFiles.bin ne sera créé que lorsque vous compilez le projet).

La différence entre 2 projets template est que HTML5/C++ Project template contient un fichier nommé bridge.js, qui fournit un support de communication entre JavaScript et C++.
Le fichier main.cpp contient le code qui crée une WebAppMoblet. Une moblet est une classe qui gère les événements dans une application. La WebAppMoblet est une moblet conçue pour être utilisée dans les applications Web. Elle fait appel à une Widget WebView (i.e., un navigateur plein écran).
Dans le cas d’un HTML5 project, vous n’avez pas besoin de modifier quoi que ce soit dans main.cpp.
Si vous voulez fournir des fonctionnalités qui font appel à du code C++ depuis JavaScript, vous pouvez éditer main.cpp pour y inclure ces fonctionnalités. L’application template exemple contient du code qui fait vibrer le mobile quand l’écran est touché.

MoSync : Téléchargement de données depuis internet

Il y’a mille raisons pour lesquelles vous voudriez que votre application récupère des informations à partir d'Internet. Peut-être que vous voulez télécharger la bande dessinée Dilbert d’aujourd'hui, ou obtenir des données spécifiques pour l'utilisateur, ou de proposer des publicités, ou de se connecter à une interface web. Peut-être que vous souhaitez mettre en place un nouveau protocole, comme FTP ou Jabber. Dans ce tutoriel nous allons vous montrer comment obtenir des données par HTTP.

Modèles de connexion

MoSync fournit un large éventail d'objets et d’interfaces de connexion. Il y'a trois niveaux d'abstraction pour les connexions à Internet, et vous pouvez écrire du code de connexion par:
·         Utilisation de la méthode de l'API maConnect ()
·         Utilisation de la classe MAUtil::Connection
·         Utilisation des classes Downloader

Utilisation de la méthode de l’API maConnect()

Au niveau le plus bas, vous pouvez utiliser les fonctions de l'API MoSync directement. Vous pouvez utiliser la méthode maConnect () pour ouvrir une Socket pour se connecter à un port sur l'Internet. Vous pouvez implémenter des protocoles qui ne sont pas pris en charge dans MoSync à un niveau supérieur (comme HTTP et HTTPS) par le formatage d'une URL comme ceci:
socket://[:]
 de sorte que vous pouvez créer votre propre protocole pour le support d’IMAP et POP3 pour faire un client de messagerie par exemple.
Vous pouvez utiliser cette méthode pour vous connecter à des périphériques Bluetooth, avec le format :
btspp://

Nous allons nous concentrer sur les connexions HTTP dans ce tutoriel bien, et bien que l'API fournit des méthodes spécifiques de HTTP, nous allons nous concentrer sur les éléments de téléchargement fournis par MAUtil.

Utilisation de la classe MAUtil ::Connection

Le deuxième niveau d'abstraction est fournit par les objets MAUtil::Connection. Ce sont des objets C++ et sont beaucoup plus adaptés pour une utilisation dans une application Moblet.
MAUtil:: Connection vous permet un accès direct pour envoyer et recevoir des flux.
MAUtil:: HttpConnection construit connexions HTTP 1.0.
Avec les objets HttpConnection vous pouvez accéder au protocole HTTP 1.0 avec la possibilité de définir en-têtes HTTP avant d'envoyer la requête. Ils permettent également de créer des classes wrapper pour l’implémentation de nouvelles fonctions spécifiques HTTP. Par exemple, vous pouvez utiliser HttpConnection pour implémenter un système d'authentification en ligne, que vous pourrez ensuite réutiliser.
Les objets Connection envoient des messages aux ConnectionListeners. Lorsque vous travaillez avec Connection ou HttpConnection, vous aurez certainement besoin de créer une classe qui hérite de ConnectionListener ou HttpConnectionListener, et implémenter ses méthodes. Nous constatons que très souvent cette classe est un écran (Screen), que nous définissons comme:
class MyScreen : public Screen, ConnectionListener
Puis, on implémente les méthodes
void connectFinished(Connection* conn, int result);
void connRecvFinished(Connection* conn, int result);
void connWriteFinished(Connection* conn, int result);
void connReadFinished(Connection* conn, int result);
Ces méthodes de callback permettent d’implémenter le comportement de l’application par rapport aux événements de la connexion.

Utilisation de la classe Downloader

Au plus haut niveau se trouvent les classes Downloader. Ces classes enveloppent une grande partie de la mécanique obtention des données sur HTTP. Ce sont les classes que nous utilisons le plus souvent. Ce sont les classes qu’il faut si vous voulez juste de télécharger des XML ou un tableau par exemple.
La classe Downloader implémente elle-mêm HttpConnectionListener, et elle est très simple à utiliser. Il vous suffit d’indiquer l'URL duquel vous souhaitez obtenir des données ainsi que le MAHandle où charger les données.
Si vous voulez savoir quand votre téléchargement terminé, vous devez implémenter un Listener également. Pour ce faire, vous héritez et vous implémentez DownloadListener.
Un second objet de téléchargement est BuffDownloader. Il est un peu plus spécialisé, au lieu de télécharger à un MAHandler, il télécharge dans la mémoire Heap.
Il y’a deux autres objets de téléchargement à un niveau encore plus élevé: ImageDownloader et AudioDownloader. Ce sont deux implémentations de Downloader, ils créent les ressources appropriées dans la mémoire pour pourvoir les utiliser directement. Plus d'informations sur ces objets plus tard.
Enfin, vous pouvez créer vos propres Downloader. Vous pouvez hériter de Downloader, et effectuer certaines de vos propres tâches de téléchargement, tout comme vous pouvez implémenter Connection pour gérer votre propre protocole d'application.

Téléchargement de données depuis le Web

Selon toute probabilité, ce que vous voulez réellement faire c’est de télécharger des données à partir du Web. La meilleure façon de le faire est d'utiliser la classe Downloader et de créer un DownloadListener correspondant. Pour ce faire, nous pouvons écrire MyScreen qui demande le téléchargement de DownloadListener. Pour le faire d'une manière réutilisable, nous créons une classe DownloadScreen.

DownloadScreen.h

#ifndef _DOWNLOADSCREEN_H_
#define _DOWNLOADSCREEN_H_
#include
#include
#include
#include "IScreenCoordinator.h"
#include "..\Utilities/Util.h"
#include "..\Utilities\IDownloadScreenListener.h"
#include "..\Widgets\SoftKeyBar.h"
using namespace MAUI;
using namespace MAUtil;
class DownloadScreen : public Screen, public DownloadListener, public ISoftKeyBarListener
{
public:
    DownloadScreen(Screen* previous, IScreenCoordinator* mainScreen);
    ~DownloadScreen();
    void download(const char* url, const char* storeName);
    void keyPressEvent(int keyCode);
    void setDownloadListener(IDownloadScreenListener* dll);
    void notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes);
    void finishedDownloading(Downloader *dl, MAHandle data);
    void error(Downloader* dl, int code);
    void downloadCancelled(Downloader* dl);
    void softKeySelected(int buttonID);
private:
    Screen* previous;
    IScreenCoordinator* mainScreen;
    Layout* layout;
    Image* image;
    ListBox* listBox;
    Downloader* dl;
    IDownloadScreenListener* listener;
    MAHandle _store;
};
#endif //_DOWNLOADSCREEN_H_
Ce code va créer MyScreen, gérer les téléchargements, et informer son propre type de Listener (IDownloadScreenListener) lorsque les téléchargements sont terminés. (Il n'informe pas IDownloadScreenListener d'autre chose que la réussite, vu que l'interaction avec l'utilisateur pour un téléchargement échoué peut être traitée par cet écran.)

IDownloadListener.h

#ifndef _IDOWNLOADSCREENLISTENER_H_
#define _IDOWNLOADSCREENLISTENER_H_
//Interface
class IDownloadScreenListener
{
public:
    virtual void downloadComplete();
};
#endif //_IDOWNLOADSCREENLISTENER_H_
DownloadScreen.h est implémentée dans DownloadScreen.cpp:
#include  "DownloadScreen.h"
//Handles a download
const char* store;

DownloadScreen::DownloadScreen(Screen* previous, IScreenCoordinator* mainScreen)
 : previous(previous), mainScreen(mainScreen)
{
    dl = new Downloader();
    dl->addDownloadListener(this);

    image = (Image*)createMainLayout(BLANK, BACK_BUTTON, this);
    layout = (Layout*) image->getChildren()[0];
    listBox = (ListBox*) layout->getChildren()[1];
    Label* l = createLabel("Starting download", 32);
    listBox->add(l);
    this->setMain(image);
}

DownloadScreen::~DownloadScreen(void)
{
    if(dl->isDownloading())
    dl->cancelDownloading();    
    delete dl;
}

void DownloadScreen::setDownloadListener(IDownloadScreenListener* dll)
{
    listener = dll;
}

void DownloadScreen::download(const char *url, const char* storeName)
{
    if(dl->isDownloading())
    {
        //lprintfln("Busy.");
        //dl->cancelDownloading();
    }
    else
    {
        lprintfln("Downloading %s", url);
        store = storeName;
        dl->beginDownloading(url);
    }
}

void DownloadScreen::downloadCancelled(Downloader *dl)
{
    //lprintfln("Cancelled");
    listBox->add(createLabel("Download has been cancelled"));
}

void DownloadScreen::error(Downloader *dl, int code)
{
    lprintfln("Error: %d", code);
    listBox->add(createLabel("Sorry, an error has occured"));
}

void DownloadScreen::finishedDownloading(Downloader *dl, MAHandle data)
{
    //Save the store
    MAHandle h = maOpenStore(store, MAS_CREATE_IF_NECESSARY);
    maWriteStore(h, data);
    maCloseStore(h, 0);

    lprintfln("Finished download");
    if(listener != NULL)
    {
        //lprintfln("Calling listener");
        listener->downloadComplete();
    }

    //lprintfln("Showing calling screen");
    previous->show();
}

void DownloadScreen::notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes)
{
    Label* l = (Label*)listBox->getChildren()[0];
    char* cap = new char[255];
    sprintf(cap, "Downloaded %d of %d bytes", downloadedBytes, totalBytes);
    l->setCaption(cap);
    delete[] cap;
    //lprintfln("Downloaded %d of %d bytes", downloadedBytes, totalBytes);
}

void DownloadScreen::softKeySelected(int buttonID)
{
    switch(buttonID)
    {
    case SoftKeyBar::LSK:
        break;
    case SoftKeyBar::FIRE:
        break;
    case SoftKeyBar::RSK:
        previous->show();
        break;
    }
}

void DownloadScreen::keyPressEvent(int keyCode)
{
    switch(keyCode)
    {
    case MAK_2:
    case MAK_4:
    case MAK_UP:
        listBox->selectPreviousItem();
        break;
    case MAK_6:
    case MAK_8:
    case MAK_DOWN:
        listBox->selectNextItem();
        break;
    case MAK_0:
        if(dl->isDownloading())
        {
            dl->cancelDownloading();
        }
    }
}
Cet écran de téléchargement peut être utilisé depuis plusieurs autres écrans. Par exemple, si vous avez un écran qui affiche un calendrier où l'utilisateur peut naviguer sur différents jours pour voir les événements de chaque journée. Cet écran peut télécharger un paquet de données à partir d'Internet contenant les événements créés pour l'utilisateur. Lorsque vous voulez télécharger une mise à jour, myEventScreen appel DownloadScreen et lui transmet l'URL qu'il veut télécharger. Ce DownloadScreen s'occupe de toutes les opérations.
DownloadScreen contient un objet Downloader, et il implémente DownloadListener. Il crée un Downloader quand il est construit, et il le supprime lors de la destruction. Lorsque la méthode download de DownloadScreen est appelée, elle passe l'URL au Downloader et commence le téléchargement.
Comme le Downloader envoie des événements à DownloadScreen via l'interface DownloadListener, l'écran peut informer l'utilisateur sur leur téléchargement. Quand il a fini, il informe l'écran qu'il a terminé, de sorte que l'écran puisse continuer son exécution.
Pour l'utiliser, nous l’appelons à partir EventsScreen.cpp. Ce qui suit est une partie de son code :
#include "EventsScreen.h"
#include
EventsScreen::EventsScreen(Screen* previous, MainScreen* mainScreen)
: previous(previous), mainScreen(mainScreen)
{
            image =(Image*) createMainLayout(BLANK, BACK_BUTTON, this);
            layout = (Layout*) image->getChildren()[0];
            listBox = (ListBox*) layout->getChildren()[1];
            this->setMain(image);
            urlptr = NULL;
            dlscreen = new DownloadScreen(this, mainScreen);
            dlscreen->setDownloadListener(this);
}
EventsScreen::~EventsScreen()
{
            if(urlptr != NULL)
            delete [] urlptr;
            delete dlscreen;
}
void EventsScreen::downloadComplete()
{
            listBox->getChildren().clear();
            createEventScreen();
}
void EventsScreen::keyPressEvent(int keyCode)
{
            switch(keyCode)
            {
            case MAK_2:
            case MAK_4:
            case MAK_UP:
                        listBox->selectPreviousItem();
                        break;
            case MAK_6:
            case MAK_8:
            case MAK_DOWN:
                        listBox->selectNextItem();
                        break;
            case MAK_0:
                        dlscreen->download(formatUrl(), EVENTSTORAGE);
                        dlscreen->show();
                        break;
            }
}
Lorsque l'utilisateur appuie 0 sur son clavier, il envoie à DownloadScreen l'URL de téléchargement, et le nom du store pour y écrire les données qu’il a reçu.
Lorsque les données ont été complètement téléchargés, EventsScreen::DownloadComplete() est appelée, et l'écran peut se repeupler à partir des données téléchargées.

Réception des événements

Dans l’exemple ci-dessus, la classe DownloadScreen implémente DownloadListener. En particulier, elle implémente les fonctions :
void notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes);
void finishedDownloading(Downloader *dl, MAHandle data);
void error(Downloader* dl, int code);
void downloadCancelled(Downloader* dl);
En faisant cela, l’écran de téléchargement est informée à propos de ce qui arrive au downloader. Le code d’implémentation est comme ceci :
void DownloadScreen::downloadCancelled(Downloader *dl)
{
            //lprintfln("Cancelled");
            listBox->add(createLabel("Download has been cancelled"));
}
void DownloadScreen::error(Downloader *dl, int code)
{
            lprintfln("Error: %d", code);
            listBox->add(createLabel("Sorry, an error has occured"));
}
void DownloadScreen::finishedDownloading(Downloader *dl, MAHandle data)
{
            //Sauvegarder le store
            MAHandle h = maOpenStore(store, MAS_CREATE_IF_NECESSARY);
            maWriteStore(h, data);
            maCloseStore(h, 0);
            lprintfln("Finished download");
            if(listener != NULL)
            {
                        //lprintfln("Calling listener");
                        listener->downloadComplete();
            }
            //lprintfln("Showing calling screen");
            previous->show();
}
void DownloadScreen::notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes)
{
            Label* l = (Label*)listBox->getChildren()[0];
            char* cap = new char[255];
            sprintf(cap, "Downloaded %d of %d bytes", downloadedBytes, totalBytes);
            l->setCaption(cap);
            delete[] cap;
            //lprintfln("Downloaded %d of %d bytes", downloadedBytes, totalBytes);
}
Quand le Downloader informe l’écran d’une erreur, ou que le téléchargement a été annulé, ou qu’il a téléchargé un peu de donnée, vous pourrez informer l’utilisateur à son tour. Quand le téléchargement termine, vous pouvez appeler IDownloadScreenListener, et afficher l’écran précédent.

MoSync : Ecriture et lecture des données

Il y’a plusieurs méthodes pour écrire et lire des données depuis un appareil mobile. Lire et écrire des données internet peut être fait avec la classe Connection, mais ca reste difficile. La façon la plus simple d’écrire et lire des données, c’est avec le store MoSync.

Création des stores

Les stores sont enregistrés dans l’appareil mobile, et sont supportés par l’ensemble des mobiles compatibles MoSync.
Pour créer un store utilisez la fonction maOpenStore(). Cette fonction prend  paramètres, un const char* avec le nom du store, et un int représentant les options du store. Pour le moment, la seule option qui existe est MAS_CREATE_IF_NECESSARY et elle créera un store s’il n’existait pas au préalable.
La fonction maOpenStore() retourne un MAHandle, une référence au store que vous pouvez utiliser avec d’autres fonctions.
MAHandle myStore = maOpenStore("MyStore", MAS_CREATE_IF_NECESSARY);
Si le store n’existe pas dans le mobile, il sera créé et le MAHandle sera retourné. S’il existe déjà, le handle sera simplement retourné.
Si le fichier ne peut être créé (par exemple, il n’y a plus d’espace disponible ou une erreur du système) la valeur retournée sera l’une des valeurs de STERR.
Quand vous lancez votre application dans l’émulateur MoRE, vous remarquerez que les stores sont écrits dans un répertoire /stores dans votre répertoire output. Les stores ne peuvent pas être déployés avec votre application mais seulement créés à l’exécution.

Vérifier qu’un store existe

Si vous voulez vérifier si un store existe ou pas, appelez maOpenStore() avec les options à 0, la valeur STERR_NONEXISTENT sera retournées si le store n’existe pas.
MAHandle testStore = maOpenStore("MyStore", 0);
       
if(myStore == STERR_NONEXISTENT)
{
      // Store n’existe pas
}
else
{
       // Store existe avec comme handle testStore
}

Ecrire dans un store

Les stores n’ont pas un système sophistiqué d’écriture/lecture. Ils fournissent simplement une méthode pour écrire des données dans le mobile. A chaque fois que le store est écrit, il remplace le précédent. Vous ne pouvez pas ajouter ou modifier des données d’un store existent. Le store est écrit depuis une ressource de données. Cette ressource n’est pas redimensionnable, mais vous pouvez éditer son contenu, écrire et lire des variables dessus.
Vous pouvez créer des ressources de données à l’exécution, mais vous devez définir un MAHandle d’abord. Pour créer une ressource de données, utilisez la fonction maCreateData(). Elle retourne RES_OK s’il y’a assez de mémoire ou RES_OUT_OF_MEMORY sinon. Vous devez passer le MAHandle comme paramètre, en plus de la taille du store. Vous pouvez créer un nouveau MAHandle vide avec la fonction maCreatePlaceHolder().
String password = "p45sw0rd";
MAHandle myData = maCreatePlaceholder();
if(maCreateData(myData, password.length()) == RES_OK)
{

}
Vous pouvez écrire des données en utilisant la fonction maWriteData()
String password = "p45sw0rd";
MAHandle myData = maCreatePlaceholder();
if(maCreateData(myData, password.length()) == RES_OK)
{
    maWriteData(myData, password.c_str(), 0, password.length();
}
La fonction maWriteData() nécessite le MAHandle de la ressource de données, un pointeur vers l’objet que vous souhaitez écrire, le décallage depuis le début de la ressource de données et la taille des données que vous souhaitez écrire. Ce qui veut dire que vous pouvez écrire la variable là où vous voulez dans la ressource de données. Si vous écrivez plusieurs données (variables), vous devez ou bien garder trace de la position relative d’écriture, ou utiliser un DataHandler qui va le faire pour vous.
Pour écrire la ressource de données dans le store, vous devez utiliser la fonction maWriteStore(). Elle prend le MAHandle du store, et le MAHandle des données.
int result = maWriteStore(myStore, myData);
Tous ce qui était avant dans le store sera remplacé. Si maWriteStore() retourne une valeur >0, cela veut dire que les données ont été correctement écrits. Les autres valeurs devraient être mappées à STERR. Ce qu’il faut vérifier ici c’est STERR_FULL qui veut dire qu’il n’y a plus d’espace de stockage.
int result = maWriteStore(myStore, myData);

switch(result)
{
    case > 0:
        //Tout va bien, les données sont sauvegardées
        break;
    case STERR_FULL:
        //Echec, pas assez d’espace pour sauvegarder
        break;
    case STERR_NONEXISTENT:
        //Echec, le store n’existe pas
        break;
    case STERR_GENERIC:
        //erreur inconnue, probablement une faute du mobile
        break;
}

Une note sur la sécurité des stores

Même si dans l’exemple, on vous montre un mot de passe qui s’écrit dans le store, vous devez être conscients que ces données ne sont pas complètement privées. Différents systèmes offrent différentes mesures de sécurité. Dans un mobile J2ME, ces données sont pratiquement sécurisées. Un hacker devra savoir beaucoup de choses sur les données pour pouvoir y accéder. Dans les Symbian S60 par contre, l’utilisateur peut librement naviguer dans les stores en utilisant le FileManager. Des stores en format texte brut peuvent être ouverts et lus, et les images écrits dans les stores peuvent être affichées. Si les données que vous souhaitez écrire sont sensibles, il vaut mieux les crypter avant.

Fermer un store

Quand vous aurez fini d’écrire, vous devez fermer le store. Cela peut être fait avec la fonction maCloseStore().
maCloseStore(myStore, 0);
Le second paramètre (0 dans l’exemple), indique si le store doit être supprimé ou pas à la fermeture. Si c’est 0, vous voulez le garder, sinon, il sera détruit. C’est le seul moyen de supprimer des stores.
void deleteStore(const char* storeName)
{
    MAHandle store = maOpenStore(storeName, 0);
    if(store != STERR_NONEXISTENT)
    {
        maCloseStore(store, 1); //Supprimer le store
    }
}

Lire depuis un store

Tout comme vous écrivez dans les stores depuis des ressources de données, vous les lisez de la même façon. La fonction maReadStore() copie les données d’un store vers le une ressource de données, indiqué par un MAHandle.
MAHandle myData = maCreatePlaceholder();
MAHandle myStore = maOpenStore("MyStore", 0);
if(myStore != MAS_NONEXISTENT)
{
    //Le store existe, nous pouvons lire depuis
    int result = maReadStore(myStore, myData);
    if(result == RES_OUT_OF_MEMORY)
    {
        //Ce store est trop grand pour le lire dans la mémoire
    }
}
Une fois le store lu dans la ressource de données, les valeurs peuvent y être lues. Bien sûr, vous devez connaitre combien de données vous allez lire : vous pouvez récupérer la taille des données avec la fonction maGetDataSize(), puis lire les données en utilisant la fonction maReadData().
char password[maGetDataSize(myData)];
maReadData(myData, &password, 0, maGetDataSize(myData));
Pour utiliser maReadData(), vous devez savoir combien d’octets il faut lire depuis la ressource de données. Dans l’exemple précédent, le mot de passe est la seule donnée stockée, alors on peut facilement lire toutes les données.
Si le store contient un login et un mot de passe, ca devient plus compliqué. Vous n’utilisez probablement pas des tailles fixes pour les String, alors vous devez enregistrer des données complémentaires sur la taille de chaque String. Si vous insérez un octet contenant la taille du String (un int s’il est trop long) juste avant les données du String, vous pourrez le lire très facilement. C’est ce qu’on appelle des String Pascal ou P-String.
//Lire un p-string
byte len = 0;

//Vous devez garder la trace de votre position
int position = 0;

//Lire la taille
maReadData(myData, &len, position, 1);// Lire un octet depuis position

//Passer à l’octet suivant
position++;

//Lire le string
char password[len + 1];  //Le String peut ne pas se terminer avec un Null
maReadData(myData, &password, position, len);

//Ajouter un null à la fin
password[len+1] = '/0';

//Déplacer la position
position += len;
Si vous êtes entrain de lire plusieurs variables, vous devez garder l’indexe de votre position dans les données.
Une fois que vous aurez fini avec les données, vous pouvez les libérer de la mémoire.
maDestroyObject(myData);
Ceci supprimer la ressource de données de la mémoire, mais ne détruit pas le store.

Utilisation du DataHandler

Si vous voulez écrire beaucoup de variables dans la ressource de données, puis dans le store, vous pouvez utiliser un utilitaire nommé DataHandler. Ca aide à écrire plusieurs variables dans la ressource en gardant trace des décalages pour vous. Vous devez inclure le header MAUtil/DataHandler.h
#include "MAUtil/DataHandler.h"
using namespace MAUtil;

...

String username = "m0sync";
String password = "p45sw0rd";

//Sauvegarder les valeurs dans la ressource de données
MAHandle myData = maCreatePlaceholder();
int size = username.length() + 4 + password.length() + 4; //La taille des données dont nous aurons besoin, incluant 4 octets pour la longueur de chaque String

if(maCreateData(myData, size) == RES_OK)
{
    DataHandler* handler = new DataHandler(myData);

    int usernameLength =  username.length();
    handler->write(&usernameLength, 4);
    handler->write(username.c_str(), usernameLength);

    int passwordLength = password.length();
    handler->write(&passwordLength, 4);
    handler->write(password.c_str(), passwordLength);
}

int result = maWriteStore(myStore, myData);

if(result > 0)
{
    //Ecrit avec succès
    maCloseStore(myStore, 0);
}
else
{
    //Echoué, supprimer le store
    maCloseStore(myStore, 1);
}
Comme vous pouvez le voir dans l’exemple ci-dessus, la classe DataHandler enlève la complexité de lire et écrire dans les stores, en les traitant comme les classes Java StreamReader et StreamWriter. Contrairement à l’exemple ci-dessus où nous lisons la ressource en gardant trace de l’indexe de la position, le DataHandler le gérera pour nous, et permettra un accès en série.