Simuler des évènements de touche sur Android

Dans cet article nous allons voir comment simuler des évènements de touche sur Android dans une application FireMonkey codée en C++. Pour faire cela nous allons utiliser la classe Instrumentation de l’API Android. Malheureusement cette classe n’est pas exposée dans la RTL. La bonne nouvelle est qu’il existe un outil pour générer les fichiers qui nous sont nécessaires. C’est à l’aide de Java2OP.exe que nous allons y arriver. Cette application par ligne de commande sert à générer des fichiers Delphi à partir de bibliothèques Java, d’où le nom Java To Object Pascal (Java2OP).

Donc avec cette commande nous obtiendrons le fichier Androidapi.Instrumentation.pas :

Java2OP.exe -classes android.app.Instrumentation -unit Androidapi.Instrumentation

La première fois que j’ai exécuté la commande, une erreur est survenue me disant que JDK était manquant. En cas de problème, je vous conseille de vous référer à l’aide où il y a une section Dépannage.

Pour commencer, on doit démarrer RAD Studio et créer un nouveau projet multi-périphériques C++Builder. Nous allons insérer un TMemo dans la Form. C’est dans ce contrôle que les touches de clavier simulées apparaitront. On doit bien sûr ajouter le fichier Androidapi.Instrumentation.pas au projet. À la compilation le fichier Androidapi.Instrumentation.hpp sera créé.

La simulation de touche de frappe doit se faire dans un thread, donc il faut aller dans le menu File / New / Other… et choisir Thread Object dans la section C++Builder Files. Vous pouvez donner le nom TInjectInput à la classe. Vous pouvez enregistrer le fichier et le nommer InjectInput.cpp. Dans ce fichier, il faut ajouter les en-têtes suivantes:

#include <Androidapi.JNI.GraphicsContentViewText.hpp>
#include "Androidapi.Instrumentation.hpp" // Fichier généré par Java2OP.exe

Dans l’évènement Execute on ajoute le code suivant:

    while(Terminated == false)
    {
        try
        {
            _di_JInstrumentation Instrumentation = TJInstrumentation::JavaClass->init();

            Graphicscontentviewtext::_di_JKeyEvent KeyEventDown = TJKeyEvent::JavaClass->init(
                TJKeyEvent::JavaClass->ACTION_DOWN, TJKeyEvent::JavaClass->KEYCODE_A);
            Instrumentation->sendKeySync(KeyEventDown);

            Graphicscontentviewtext::_di_JKeyEvent KeyEventUp = TJKeyEvent::JavaClass->init(
                TJKeyEvent::JavaClass->ACTION_UP, TJKeyEvent::JavaClass->KEYCODE_A);
            Instrumentation->sendKeySync(KeyEventUp);
        }
        catch(const Exception &)
        {
        }

        Sleep(5000);
    }

Cela aura comme effet de générer un événement de frappe avec la touche a enfoncée et ensuite un évènement avec la même touche relâchée. La méthode injectKeyEvent aurait aussi pu être meilleure, mais je voulais utiliser KeyEvent. Il est important de mettre le code dans un try/catch car lorsqu’on quitte l’application l’exception suivante est lancée:

java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission

Il faut savoir que même si la permission INJECT_EVENTS est accordée, l’exception sera quand même lancée si l’application n’est pas signée.

Dans le fichier .h de la Form on ajoute le code suivant:

//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <FMX.Controls.hpp>
#include <FMX.Forms.hpp>
#include <FMX.Controls.Presentation.hpp>
#include <FMX.Memo.hpp>
#include <FMX.ScrollBox.hpp>
#include <FMX.Types.hpp>
#include "InjectInput.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components
    TMemo *Memo1;
private:	// User declarations
    TInjectInput* FInjectInput;
public:		// User declarations
    __fastcall TForm1(TComponent* Owner);
    virtual __fastcall ~TForm1();
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

À la ligne 12 on ajoute l’en-tête. À la ligne 19 on ajoute une propriété privée. Finalement à la ligne 22 c’est le destructeur de Form.

Dans le fichier .cpp de la Form on ajoute le code suivant:

//---------------------------------------------------------------------------
#include <fmx.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    FInjectInput = new TInjectInput(true);
    FInjectInput->Start();
}
//---------------------------------------------------------------------------

__fastcall TForm1::~TForm1()
{
    delete FInjectInput;
}
//---------------------------------------------------------------------------

Dans le constructeur on va créer le thread et le démarrer. On ajoute le destructeur pour libérer la mémoire.

Maintenant, il suffit de choisir le Target Android, compiler et déployer le code. On doit cliquer sur le contrôle TMemo et des a devraient apparaître à toutes les 5 secondes.

Déboguer sur CyanogenMod avec RAD Studio

Dans un article précédant, datant de novembre 2014, j’expliquais comment déboguer sur la OUYA avec RAD Studio. Plusieurs choses ont changé depuis ce temps. Le plus important étant la vente de OUYA a Razer et le fait que le matériel cessera d’être supporté. Je croyais bien que ma petite boîte grise allait rester pour toujours à l’intérieur de l’un de mes tiroirs, jusqu’à ce que je tombe sur un article expliquant comment faire rouler CyanogenMod sur ma OUYA. CyanogenMod version 11 (CM11) est basé sur Android KitKat 4.4.x. Donc, présentement ma OUYA fonctionne comme Android 4.4.4. C’est ce que l’on peut voir sur la capture d’écran qui suit.

About tablet
Information sur la OUYA et le système d’exploitation

Ma motivation première pour transformer ma OUYA en appareil Android standard n’était pas nécessairement le développement d’applications. Comme beaucoup de gens, c’était de faire fonctionner Kodi version 16.1 (Jarvis). Le reste de l’article va se concentrer par contre sur ma seconde raison.

Pour utiliser RAD Studio avec CM11, il s’agit de faire les étapes habituelles que l’on ferait sur Android. La première étant de faire afficher la section avec les options pour développeur. Pour faire cela on doit aller dans les Settings dans la section About tablet. Ensuite on clique sept fois sur Build number.

Maintenant que la section Developer options des Settings est disponible, il faut s’assurer que les options Android debugging et ADB over network sont activées:

ADB over network
Activation du débogage par le réseau

L’adresse IP et le numéro de port qui se trouve juste en dessous de l’option l’option ADB over network vous seront utiles bientôt. Il faut d’abord se connecter à la OUYA. Pour cela l’application Android Debug Bridge (adb) située dans le dossier /<sdk>/platform-tools sera utilisée. Dans le SDK Manager pour Android on trouve ce chemin dans la section Adb location. Vous devez aller dans ce dossier et taper la ligne de commande suivante:
adb connect 192.168.1.115:5555
Si tout fonctionne le résultat devrait être: connected to 192.168.1.115:5555
This ingredient performs well in the human body to prevent cheap levitra canada testosterone generation. Depression is another common issue that causes penile weakness appalachianmagazine.com purchase levitra due to imbalance in the production of nitric oxide to increase. This article aims to provide an explanation of the possible matter behind click for source buy cialis without prescription snoring. The MarketWatch report causes it to be practically obvious as water, regardless of tadalafil online no prescription‘s powerful maintain on the marketplace, Bayer recorded a 16% advance in sales of its erectile dysfunction drug, to 73 million euros this year.The figures clearly display that levitra won’t await prolonged to defeat its competition.
Vous pouvez aussi taper cette commande pour voir la liste des dispositifs connectés:
adb devices
Si la liste est vide, c’est que la commande de connexion à échouer.

Ensuite, ouvrez ou créez un nouveau projet de type Multi-Device Application dans RAD Studio. Sélectionnez le Target Android. Dans la section Target de celui-ci, OUYA devrait apparaitre. Si ce n’est pas le cas il faut faire un Refresh.

OUYA Target
Le Target OUYA est sélectionné

Il est maintenant possible de compiler le fichier APK et de tester l’application directement sur la console. Une fois fermée, elle devrait être disponible avec les autres applications.

Si vous voulez simplement installer l’application, vous pouvez utiliser cette ligne de commande:
adb install "C:\Projets\Application\Android\Release\application\bin\application.apk"

Utilisation de TWebBrowser avec JavaScript

Dans cet article, je vais interagir avec du contenu HTML à l’intérieur d’un composant TWebBrowser. Le but est de valider une page web avec l’API de Nu Html Checker (v.Nu) et d’afficher le résultat. On aurait pu utiliser les composants REST inclut dans RAD Studio, mais cette fois-ci, j’ai décidé d’utiliser la bibliothèque jQuery. Donc, il va y avoir plus de code JavaScript que de code C++. Cet article sert à démontrer certaines fonctionnalités de FireMonkey et non d’avoir une application parfaitement codée et prête à mettre sur un magasin en ligne.

Tout d’abord, il faut créer une application multi-périphériques dans laquelle il faut ajouter quelques composants. Un TPanel aligné à Top dans lequel on insère un TEdit pour l’entrée du URL et un TSpeedButton pour déclencher le chargement du résultat. On ajoute un TWebBrowser aligné à Top et un TSplitter lui aussi avec le même alignement. Par la suite, on ajoute un TMemo et on met Align à Client. Il va contenir le code HTML et JavaScript. Pour cette raison, le code suivant sera ajouté à la propriété Lines:

<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js" type="text/javascript"></script>
        <script type="text/javascript">
            function go(urlToValidate) {
                $.ajax({
                    type: "GET",
                    url: 'http://validator.w3.org/nu/?out=json&doc=' + urlToValidate,
                    dataType: "jsonp",
                    cache: false,
                    crossDomain: true,
                    success: function (data) {
                        $("#out").append('<ul>');
                        $.each(data.messages, function() {
                            $.each(this, function(k, v) {
                                if(k == 'message') {
                                    $("#out").append('<li>' + v + '</li>');
                                }
                            });
                        });
                        $("#out").append('</ul>');
                    },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                        $("#out").html("Erreur!");
                    }
                });
            }
        </script>
    </head>
    <body>
        <div id="out"></div>
    </body>
</html>

Pour ceux qui on déjà utilisé jQuery, ce code n’a rien d’impressionnant. Il ne fait que faire une requête AJAX de type GET à l’API v.Nu. Si l’appel fonctionne, une liste de message sera alors affichée. Si elle échoue, le mot Erreur sera présenté. La fonction JavaScript go ne semble pas appeler. Effectivement, elle sera invoquée par du code C++.

Alors, nous allons débuter la partie C++ du projet. L’encodage du URL aurait pu se faire en JavaScript mais je voulais tester TURLEncoding. Pour cela, il faut ajouter le fichier d’en-tête suivant

#include <System.NetEncoding.hpp>

Dans le constructeur, on ajoute ce code pour définir l’URL par défaut et charger le code HTML dans le navigateur web.

    Edit1->Text = "http://www.google.com";
    WebBrowser1->LoadFromStrings(Memo1->Text, "");

Dans l’évènement OnClick du bouton on ajoute ceci:

    String LUrl = System::Netencoding::TURLEncoding::URL->Encode(Edit1->Text);
    WebBrowser1->EvaluateJavaScript("go('" + LUrl + "');");

Ici on encode l’URL et on l’utilise comme paramètre lors de l’appel à la fonction go JavaScript.

Si tout fonctionne bien, vous devriez obtenir un résultat similaire à cette capture d’écran:
Nu Html Checker

Couleur d’un pixel dans un TBitmap

Dans cet article, nous allons voir comment changer la couleur des pixels dans un Fmx::Graphics::TBitmap.

Pour cela, on va utiliser une image PNG qui possède seulement deux couleurs: du blanc et du noir.
Cœur

Tout d’abord, il faut créer un projet FireMonkey dans lequel il faut ajouter un composant TImage.

Ensuite, il faut ajouter le fichier d’en-tête suivant pour utiliser TAlphaColorArray:

#include <FMX.Utils.hpp>

Par la suite, vous pouvez ajouter ce code dans le constructeur de la Form.

Fmx::Graphics::TBitmap* LBitmap = NULL;
try
{
    LBitmap = new Fmx::Graphics::TBitmap("C:\\images\\heart.png");
 
    TBitmapData LBitmapData;
    if(LBitmap->Map(TMapAccess::ReadWrite, LBitmapData))
    {
        try
        {
            TAlphaColorArray* LColorArray;
            for(int y = 0; y < LBitmapData.Height; ++y)
            {
                LColorArray = static_cast<TAlphaColorArray*>
                    (LBitmapData.GetScanline(y));
                for(int x = 0; x < LBitmapData.Width; ++x)
                {
                    void* LPixel = &LColorArray->data[x];
                    const System::Uitypes::TAlphaColor LColor =
                        PixelToAlphaColor(LPixel, LBitmapData.PixelFormat);
                    if(LColor == TAlphaColorRec::White)
                    {   // Change le blanc en rouge
                        AlphaColorToPixel(TAlphaColorRec::Red,
                            LPixel, LBitmapData.PixelFormat);
                    }
                    else if(LColor == TAlphaColorRec::Black)
                    {   // Change le noir en violet
                        AlphaColorToPixel(TAlphaColorRec::Violet,
                            LPixel, LBitmapData.PixelFormat);
                    }
                }
            }
        }
        __finally
        {
            LBitmap->Unmap(LBitmapData);
        }
    }
 
    Image1->Bitmap->Assign(LBitmap);
}
__finally
{
    delete LBitmap;
}

Voilà, maintenant vous devriez obtenir ceci à l’exécution:

Résultat changement de couleur

L’API PageSpeed v2

Dans cet article je vais revisiter l’article que j’avais écrit en mai 2012 sur l’utilisation de l’API PageSpeed de Google. Cette fois-ci c’est C++Builder XE7 qui sera utilisé avec la version 2 de l’API. Tout comme la première fois, les résultats seront présentés dans un diagramme circulaire à l’aide du contrôle TChart. En plus du code qui sera légèrement différent, un composant TImage sera utilisé pour y afficher une capture d’écran du site web.

La première étape est de créer un nouveau projet FireMonkey. Dans la Form il faut insérer un TRESTClient, un TRESTRequest, un TRESTResponse, un TChart, un TImage et un TLayout dans lequel il y aura un TEdit un TButton. On aligne le contrôle TLayout à Bottom et vous pouvez donner comme texte à votre bouton le mot « Analyser ».

Voici ce à quoi l’interface devrait ressembler:
Analyseur de site web

Dans votre fichier cpp voici la liste de fichier d’en-tête à utiliser ainsi que la macro qui va contenir votre clef d’API. N’essayez pas d’utiliser celle-ci, il s’agit de caractères écrits de façon aléatoire.

#include <System.JSON.hpp>
#include <FMXTee.Series.hpp>
#include <System.NetEncoding.hpp>
#define GOOGLEAPIKEY "dskk1j3sW4WBYdkjds8sSDSD" // Clef d'API

Voici le code à ajouter dans votre constructeur:

    Chart1->Title->Text->Text = "Statistique de la page";
    Chart1->Legend->Title->Text->Text = "Ressource en octets";
    Chart1->Legend->Alignment = TLegendAlignment::laBottom;
    Chart1->BevelOuter = TPanelBevel::bvNone;
    Series::TPieSeries *Series = new Series::TPieSeries(this);
    Series->Marks->Visible = false;
    Chart1->AddSeries(Series);

Étant donné que nous accéderons à un site web qui utilise SSL (https), les fichiers ssleay32.dll et libeay32.dll devront être distribués avec votre application Windows. Une version Win32 et Win64 des fichiers est disponible sur le site web suivant: http://indy.fulgan.com/SSL.

La prochaine étape est d’ajouter le code dans l’événement OnClick du bouton.

    Chart1->Series[0]->Clear(); // Efface le contenu du diagramme

    RESTClient1->BaseURL =
        "https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=" +
        Edit1->Text + "&key=" GOOGLEAPIKEY + "&screenshot=true";
    RESTRequest1->Execute();

    TJSONObject* Obj = static_cast<TJSONObject*>(RESTResponse1->JSONValue);
    TJSONPair* Pair = Obj->Get("pageStats");
    if(Pair)
    {
        TJSONNumber* Answer;
        TJSONObject* PageStats = static_cast<TJSONObject*>(Pair->JsonValue);
        if((Pair = PageStats->Get("htmlResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "HTML", TAlphaColor(claGreen));
        }
        if((Pair = PageStats->Get("cssResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "CSS", TAlphaColor(claOrange));
        }
        if((Pair = PageStats->Get("imageResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "Image", TAlphaColor(claYellow));
        }
        if((Pair = PageStats->Get("javascriptResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "JavaScript", TAlphaColor(claRed));
        }
        if((Pair = PageStats->Get("otherResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "Autre", TAlphaColor(claBlue));
        }
    }
    Pair = Obj->Get("screenshot");
    if(Pair)
    {
        TJSONObject* Screenshot = static_cast<TJSONObject*>(Pair->JsonValue);
        if((Pair = Screenshot->Get("data")) != NULL)
        {
            TJSONString* Answer = static_cast<TJSONString*>(Pair->JsonValue);
            String Value = Answer->Value();
            // On change de base64url à base64
            Value = ReplaceStr(ReplaceStr(Value, "_", "/"), "-", "+");
            // On transforme de base64 vers stream
            TBytes LDAta = TNetEncoding::Base64->DecodeStringToBytes(Value);
            TBytesStream* LStream = new TBytesStream(LDAta);
            try
            {
                Image1->Bitmap->LoadFromStream(LStream);
            }
            __finally
            {
                delete LStream;
            }
        }
    }

Un petit mot sur la ligne 49. La documentation de l’API PageSpeed à propos des données de l’image dit:

Base64-encoded screenshot of the page that was analyzed.

Les données peuvent comporter des caractères moins (-) et souligné (_), donc la documentation est erronée si on se fie à RFC 4648 section 5:

This encoding may be referred to as « base64url ». This encoding should not be regarded as the same as the « base64 » encoding and should not be referred to as only « base64 ». Unless clarified otherwise, « base64 » refers to the base 64 in the previous section.

This encoding is technically identical to the previous one, except for the 62:nd and 63:rd alphabet character, as indicated in Table 2.

Google n’aurait donc pas dû dire qu’il s’agit de base64. Il aurait dû dire que c’est du base64url. C’est pour cette raison que je remplace les deux caractères.

Pourtant, dans d’autres API comme celui de Gmail, Google met la bonne information:

The entire email message in an RFC 2822 formatted and base64url encoded string.

En terminant, je vous rappelle qu’un nouvel outil dans RAD Studio XE7 vous permet de tester les API REST. Il se trouve Tools / Rest Debugger. Voici une capture d’écran du logiciel:
RESTDebugger

Comment déboguer sur la OUYA avec RAD Studio

La OUYA est une console de jeu vidéo qui fonctionne sous le système d’exploitation Android. Il est donc possible grâce à FireMonkey de créer des applications pour cette plateforme. Bien sûr, les déboguer à partir de l’IDE serait un grand avantage. C’est ce que le logiciel adb nous permet de faire. Tout d’abord il faut démarrer la OUYA et aller dans le menu MANAGE / SYSTEM / DEVELOPPEMENT. Il faut activer ADB et ADB OVER NETWORK comme démontré dans la capture d’écran suivante:
OUYA ADB ON
L’adresse IP et le numéro de port vous seront utiles bientôt. Il faut d’abord se connecter à la OUYA. Pour cela l’application Android Debug Bridge (adb) située dans le dossier /<sdk>/platform-tools sera utilisée. Dans le SDK Manager pour Android on trouve ce chemin dans la section Adb location. Vous devez aller dans ce dossier et taper la ligne de commande suivante:
adb connect 192.168.1.104:5555
Si tout fonctionne le résultat devrait être: connected to 192.168.1.104:5555

Ensuite, ouvrez ou créez un nouveau projet de type Multi-Device Application dans RAD Studio. Sélectionnez le Target Android. Dans la section Target de celui-ci OUYA Console devrait apparaitre. Si ce n’est pas le cas il faut faire un Refresh.

OUYA Console Target

Il est maintenant possible de compiler le fichier APK et de tester l’application directement sur la console. Une fois fermée, elle devrait être disponible dans la section MAKE / SOFTWARE.

Si vous voulez simplement installer l’application, il est possible d’aller dans le menu MAKE / UPLOAD afin d’activer le téléchargement vers la OUYA.
OUYA Upload APK
Il vous suffit simplement d’ouvrir le lien donné dans votre navigateur web préféré et de déposer le fichier APK dans la zone appropriée.
Upload APK

Il est possible de faire d’autres tâches intéressantes avec adb. Par exemple, j’ai enregistré une capture d’écran sur la OUYA avec la ligne de commande suivante:
adb shell screencap -p /sdcard/screencap.png
Ensuite j’ai transferé l’image vers mon disque local C: avec cette ligne de commande:
adb pull /sdcard/screencap.png c:\

Server-Sent Events avec C++Builder

Les Server-Sent Events (SSE) servent à pousser des informations à partir d’un serveur HTTP vers un navigateur web. Habituellement c’est le client qui doit demander l’information au serveur, mais avec cette méthode c’est le contraire.

Le principe est simple: le client fait une requête à une adresse web dans le but de recevoir des évènements DOM. Pour effectuer cela, c’est EventSource de l’API JavaScript qui sera utilisé. Il est important de dire que les Server-Sent Events ne sont pas encore supportés dans Internet Explorer.

La première étape est de créer un nouveau projet C++Builder. Dans la Form il faut insérer un TIdHTTPServer. C’est ce contrôle de Indy qui sera utilisé pour le serveur. On commence d’abord par démarrer le serveur avec ligne de code dans le constructeur:

    IdHTTPServer1->Active = true;

Dans l’évènement OnCommandGet du TIdHTTPServer on ajoute ce code:

    if(ARequestInfo->URI == "/demo")
    {
        TIdTCPConnection* Connection = AContext->Connection;
        while(Connection->Connected())
        {
            System::Word Hour, Min, Sec, MSec;
            Now().DecodeTime(&Hour, &Min, &Sec, &MSec);
            String Message = "Voici l'heure sur le serveur: " +
                Format("%.2d:%.2d:%.2d.%.3d", ARRAYOFCONST((Hour, Min, Sec, MSec)));

            Connection->IOHandler->WriteBufferOpen();
            Connection->IOHandler->WriteLn("HTTP/1.1 200 OK");
            Connection->IOHandler->WriteLn("Content-Type: text/event-stream; charset=UTF-8");
            Connection->IOHandler->WriteLn("Cache-Control: no-cache");
            Connection->IOHandler->Write("data: " + Message + "\n\n");
            Connection->IOHandler->WriteBufferClose();

            Sleep(100); // Une petite pause
        }
    }
    else
    {
        AResponseInfo->ContentText =
            "<!doctype html>"
            "<html>"
            "    <head>"
            "        <title>Server-Sent Events</title>"
            "        <script type = \"text/javascript\">"
            ""
            "        var source = new EventSource(\"demo\");"
            "        source.onmessage = function(event)"
            "        {"
            "            document.getElementById(\"result\").innerHTML = event.data;"
            "        };"
            ""
            "        </script>"
            "    </head>"
            "    <body>"
            "        <p id=\"result\"></p>"
            "    </body>"
            "</html>";
    }

Voilà, maintenant il suffit d’ouvrir votre navigateur web à l’adresse 127.0.0.1 vous devriez voir l’heure de votre PC qui s’actualise à tous les 100ms.

Utiliser l’API Graph de Facebook avec C++Builder

Facebook se passe sans doute de présentation, par contre son API est peut-être moins connu. Dans cet article nous irons chercher les informations publiques d’un utilisateur qui ne nécessitent aucune autorisation.

La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form il faut insérer un TIdHTTP, un TIdSSLIOHandlerSocketOpenSSL, un TStringGrid, un TImage, un contrôle TEdit, un TLabel et finalement un TButton. Vous pouvez donner comme texte à votre bouton le mot « Rechercher » et pour le TLabel vous pouvez y inscrire « Nom d’utilisateur: ». Je vous propose de placer les composants dans la fenêtre de la manière suivante:
Pour ceux qui se le demande j’ai utilisé le style Air.Style. Ça change un peu des fenêtres Windows que l’on voit tout le temps.

Dans votre fichier cpp voici le fichier d’en-tête à ajouter:

#include <Data.DBXJSON.hpp>

Voici le code à ajouter dans votre constructeur:

    IdHTTP1->IOHandler = IdSSLIOHandlerSocketOpenSSL1;

    // Ceci est nécessaire pour les redirections
    IdHTTP1->HandleRedirects = true;

    // Propriété par défaut pour le contrôle grille
    StringGrid1->ShowSelectedCell = false;
    StringGrid1->ReadOnly = true;
    StringGrid1->RowCount = 0;

    // Ajout de la première colonne
    StringGrid1->AddObject(new TStringColumn(this));
    StringGrid1->Columns[0]->Header = L"Nom";
    StringGrid1->Columns[0]->Width = 150;

    // Ajout de la deuxième colonne
    StringGrid1->AddObject(new TStringColumn(this));
    StringGrid1->Columns[1]->Header = L"Valeur";
    StringGrid1->Columns[1]->Width = 150;

Étant donné que nous accéderons à un site web qui utilise SSL (https), la première ligne de code est critique. Sans elle, une exception dans la classe EIdIOHandlerPropInvalid produira le message « IOHandler value is not valid ». Parce que nous utilisons OpenSSL, les fichiers ssleay32.dll et libeay32.dll devront être distribués avec votre application.

La prochaine étape est d’ajouter le code dans l’événement OnClick du bouton.

    System::Classes::TMemoryStream* ResponseContent = new System::Classes::TMemoryStream;

    try
    {
        // On vide la liste avant d'ajouter les valeurs
        StringGrid1->RowCount = 0;
        Image1->Bitmap = NULL;

        String URL = "https://graph.facebook.com/" + Edit1->Text;

        String Response = IdHTTP1->Get(URL);

        TJSONObject* Obj = static_cast<TJSONObject*>(TJSONObject::ParseJSONValue(Response));
        TJSONPair* Pair;
        TJSONString* Answer;

        if((Pair = Obj->Get("id")) != NULL)
        {   // ID Facebook
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "ID";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }
        if((Pair = Obj->Get("name")) != NULL)
        {   // Nom complet
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "Nom";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }
        if((Pair = Obj->Get("first_name")) != NULL)
        {   // Prénom
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "Prénom";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }
        if((Pair = Obj->Get("last_name")) != NULL)
        {   // Nom de famille
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "Nom de famille";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }
        if((Pair = Obj->Get("gender")) != NULL)
        {   // Sexe (female ou male)
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "Sexe";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }

        // Téléchargement de l'image
        IdHTTP1->Get(URL + "/picture", ResponseContent);

        Image1->Bitmap = new Fmx::Types::TBitmap(ResponseContent);
    }
    catch(...)
    {
    }

    delete ResponseContent;

Dans le code on insère dans la liste seulement quelques informations, mais il en existe plusieurs autres qui sont disponibles.

À présent, vous connaissez le minimum requis pour commencer à vous amuser avec cette interface API .

Utiliser l’API Google Street View Image

L’API Google Street View Image permet de télécharger une image statique par l’envoi d’une requête HTTP standard.

La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form il faut insérer un TIdHTTP, un TImage, un TEdit, un TTrackBar et quatre contrôles TButton. Je vous propose de placer les composants dans la fenêtre de la manière suivante:

Google Street View Image API

Le TEdit servira à entrer les coordonnées de latitude et longitude. La barre graduée permet d’agrandir et de réduire l’affichage de l’image. Un nombre plus petit signifie un plus grand niveau de zoom. Les boutons servent à déplacer la caméra dans différentes directions. Le résultat sera évidemment affiché dans le composant TImage.

Voici les attributs et la méthode à ajouter à votre fichier .h:

int FHeading;
int FFieldOfView;
int FPitch;

void __fastcall UpdateImage();

Voici tout le code qui sera nécessaire pour l’application:

//---------------------------------------------------------------------------
#include <fmx.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
    // On fait croire à Google que l'on est Firefox 16.0
    IdHTTP1->Request->UserAgent =
        "Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0";

    // Valeur par défaut
    FHeading = 0;
    FFieldOfView = 90;
    FPitch = 0;

    TrackBar1->Tracking = false;
    TrackBar1->Value = FFieldOfView;
    TrackBar1->Min = 10;
    TrackBar1->Max = 120;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::UpdateImage()
{
    System::Classes::TMemoryStream* ResponseContent = new System::Classes::TMemoryStream;

    try
    {
        String URL = Format("http://maps.googleapis.com/maps/api/streetview?size=%dx%d&location=%s&sensor=false&heading=%d&fov=%d&pitch=%d",
            ARRAYOFCONST((
            (int)Image1->Width, // Image Width
            (int)Image1->Height, // Image Height
            EditLocation->Text, // Location
            FHeading, // Heading (0 to 360)
            FFieldOfView, // Field of view
            FPitch // Pitch
            )));

        IdHTTP1->Get(URL, ResponseContent); // Téléchargement de l'image

        Image1->Bitmap = new Fmx::Types::TBitmap(ResponseContent);
    }
    catch(...)
    {
    }

    delete ResponseContent;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::EditLocationKeyUp(TObject *Sender, WORD &Key,
          System::WideChar &KeyChar, TShiftState Shift)
{
    if(Key == vkReturn)
    {   // La touche Entrée a été appuyée, on met à jour l'image
        UpdateImage();
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
    FFieldOfView = TrackBar1->Value;
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonLeftClick(TObject *Sender)
{
    // Déplacement de la caméra d'un angle de 20° vers la gauche
    FHeading = (FHeading - 20) % 360;
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonRightClick(TObject *Sender)
{
    // Déplacement de la caméra d'un angle de 20° vers la droite
    FHeading = (FHeading + 20) % 360;
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonTopClick(TObject *Sender)
{
    if((FPitch += 10) > 90)
    {   // La caméra est complètement vers le haut
        FPitch = 90;
    }
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonBottomClick(TObject *Sender)
{
    if((FPitch -= 10) < -90)
    {   // La caméra est complètement vers le bas
        FPitch = -90;
    }
    UpdateImage();
}
//---------------------------------------------------------------------------

Le code qui est le plus important se trouve dans la méthode UpdateImage. Tous les évènements servent uniquement à modifier l’un des paramètres du URL.

J’aurais bien aimé utiliser le composant TLocationSensor disponible dans C++Builder XE3 pour aller chercher mes coordonnées de latitude et longitude, mais je n’ai pas le matériel nécessaire pour le tester. C’est dommage car ça me semble facile à utiliser.

Utiliser l’API de VirusTotal avec C++Builder

VirusTotal est un service gratuit qui permet la détection de virus, vers, chevaux de Troie ou tout autre type de programme malveillant. En visitant le site web de cette application, il est possible de choisir un fichier sur votre disque dur et de le soumettre à une analyse. Par la suite, plusieurs antivirus vérifieront si les données comportent des partie suspectes. Le résultat sera ensuite disponible en ligne.

Cet outil web est vraiment génial, mais ce qui l’est encore plus c’est qu’il existe une interface API publique que l’on peut utiliser avec des requêtes POST par HTTP. Les réponses seront en format JSON (JavaScript Object Notation).

Avant de commencer, il faudra vous inscrire pour obtenir une clef d’API. Elle sera affichée dans la section API de votre profil. Il est à noter qu’avec cette clef vous ne pourrez effectuer que quatre requêtes par minute.

Maintenant, on peut commencer à coder. La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form, il faut insérer un TIdHTTP, un TIdSSLIOHandlerSocketOpenSSL, un TOpenDialog, un contrôle TEdit deux TButton et un TMemo.

Je vous propose de placer les composants dans la fenêtre de la manière suivante:
VirusTotal API

Vous pourrez aussi utiliser des textes similaires à ceux de l’image pour les différents contrôles.

Dans votre fichier cpp voici la liste de fichiers d’en-tête à utiliser ainsi que la macro qui va contenir votre clef d’API. N’essayez pas d’utiliser celle-ci, il s’agit de caractères écrits de façon aléatoire.

#define APIKEY "g9898984d0909534e1230958567f098090928712a8034cb0809786201854f678"
#include <Data.DBXJSON.hpp>
#include <Idmultipartformdata.hpp>

Voici le code à ajouter dans votre constructeur:

    IdHTTP1->IOHandler = IdSSLIOHandlerSocketOpenSSL1;

    OpenDialog1->Filter = "Application (*.exe)|*.EXE|Tous les fichiers|*.*";
    OpenDialog1->Options = System::Uitypes::TOpenOptions()
        << TOpenOption::ofHideReadOnly << TOpenOption::ofEnableSizing <<
        TOpenOption::ofFileMustExist;

Étant donné que nous accéderons à un site web qui utilise SSL (https), la première ligne de code est critique. Parce que nous utilisons OpenSSL, les fichiers ssleay32.dll et libeay32.dll devront être distribués avec votre application.

La prochaine étape est d’ajouter le code dans l’événement Button1Click.

    if(OpenDialog1->Execute() == false)
    {
        return;
    }

    Idmultipartformdata::TIdMultiPartFormDataStream* Params =
        new Idmultipartformdata::TIdMultiPartFormDataStream();
    Params->AddFormField("apikey", APIKEY);
    Params->AddFile("file", OpenDialog1->FileName, "application/octet-stream");

    System::UnicodeString Response = IdHTTP1->Post(
        "https://www.virustotal.com/vtapi/v2/file/scan", Params);

    TJSONObject* Obj = static_cast<TJSONObject*>
        (TJSONObject::ParseJSONValue(Response));
    if(Obj)
    {
        TJSONPair* Pair;
        TJSONString* Answer;
        if((Pair = Obj->Get("response_code")) != NULL)
        {
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            if(Answer->ToString() == "1")
            {
                if((Pair = Obj->Get("resource")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Edit1->Text = AnsiDequotedStr(Answer->ToString(), '\"');
                }
            }
            else
            {
                Edit1->Text = "Erreur";
            }
        }
    }

    delete Params;

Ce code est utilisé pour la soumission du fichier choisi avec le TOpenDialog vers le serveur de VirusTotal. Si le code de la réponse est 1, ce qui signifie donc que la requête à réussi, la ressource sera affichée dans le TEdit.

Finalement, il faut ajouter ce code dans l’événement Button2Click.

    Idmultipartformdata::TIdMultiPartFormDataStream* Params =
        new Idmultipartformdata::TIdMultiPartFormDataStream();
    Params->AddFormField("apikey", APIKEY);
    Params->AddFormField("resource", Edit1->Text);

    System::UnicodeString Response = IdHTTP1->Post(
        "https://www.virustotal.com/vtapi/v2/file/report", Params);

    TJSONObject* Obj = static_cast<TJSONObject*>
        (TJSONObject::ParseJSONValue(Response));
    if(Obj)
    {
        TJSONPair* Pair = Obj->Get("scans");;
        if(Pair)
        {
            TJSONObject* Scans = static_cast<TJSONObject*>(Pair->JsonValue);
            TJSONPairEnumerator* Enumerator = Scans->GetEnumerator();
            while(Enumerator->MoveNext())
            {
                String AntivirusName =
                    AnsiDequotedStr(Enumerator->Current->JsonString->ToString(), '\"');
                Memo1->Lines->Add(AntivirusName);

                TJSONObject* AntivirusData =
                    static_cast<TJSONObject*>(Enumerator->Current->JsonValue);

                String Value;
                TJSONString* Answer;
                if((Pair = AntivirusData->Get("detected")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Detected: " + Value);
                }
                if((Pair = AntivirusData->Get("version")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Version: " + Value);
                }
                if((Pair = AntivirusData->Get("result")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Result: " + Value);
                }
                if((Pair = AntivirusData->Get("update")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Update: " + Value);
                }
            }
        }
    }
    delete Params;

Cette méthode servira à aller chercher le résultat d’une analyse pour la ressource entrée dans le TEdit. Le tout sera affiché dans le contrôle TMemo.

Il est important de savoir que les fichiers soumis par l’API ont la priorité d’analyse la plus basse. Dépendant de la charge de travail de VirusTotal, il pourrait être possible d’attendre plusieurs heures avant que le fichier soit analysé. Il est donc important d’appeler ce code à intervalle régulier pour s’assurer que tous les résultats sont disponibles.

La réponse de la requête POST ressemble à ceci:

{
   "scans":{
      "AVG":{
         "detected":false,
         "version":"10.0.0.1190",
         "result":null,
         "update":"20120826"
      },
      "Panda":{
         "detected":false,
         "version":"10.0.3.5",
         "result":null,
         "update":"20120826"
      }
   },
   "scan_id":"51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813-1346036118",
   "sha1":"189f6287cd36c2f49bc828a556a70d960b7547b3",
   "resource":"51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813",
   "response_code":1,
   "scan_date":"2012-08-27 02:55:18",
   "permalink":"https://www.virustotal.com/file/51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813/analysis/1346036118/",
   "verbose_msg":"Scan finished, scan information embedded in this object",
   "total":2,
   "positives":0,
   "sha256":"51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813",
   "md5":"463b0fe60365c7cc84325646aabeb907"
}

Pour plus de détail sur l’API vous pouvez consulter la page web suivante: https://www.virustotal.com/documentation/public-api