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

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"

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:\