Annexe de mon support de formation sur Android. Un exemple d'IME pour un clavier physique est expliqué. Cette annexe sera complétée avec d'autres exemples.
2. Introduction
● IMF : Input Method Framework
● apparu avec le SDK 1.5
● permet de développer des moyens de saisie
– clavier software
– clavier physique
– voix
– reconnaissance d'écriture
– …
antislashn.org Android - Input Method Editor C - 2/44
3. Introduction
● La saisie est effectuée via un IME
● Input Method Editor
● IMF supporte une grand nombre d'IME
● En général l'utilisateur accède à un IME software
particulier de manière transparente
● lors de la saisie d'un champ
– texte, date, heure, mot de passe, …
● Android arrange automatiquement le bureau
– "pan and scan" qui permet le défilement de l'écran de
l'application afin que la zone de saisie soit toujours visible
– "fullscrenn" qui est utilisé si l'IME est trop large pour partager
l'espace avec l'écran de l'application
antislashn.org Android - Input Method Editor C - 3/44
4. Introduction
● "pan and scan"
source : Google
antislashn.org Android - Input Method Editor C - 4/44
5. Introduction
● "fullscreen"
source : Google
antislashn.org Android - Input Method Editor C - 5/44
6. Contrôle de l'IME
● Les attributs XML des zones d'édition permettent
de contrôle le type d'IME qui sera utilisé
● dans le fichier XML du layout
● android:inputType qui peut prendre les valeurs
– android:password, android:numeric,
android:phoneNumber, …
<EditText android:id="@+id/edtInput"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textShortMessage|textAutoCorrect|textCapSentences|textMultiLine"
android:imeOptions="actionSend|flagNoEnterAction"
android:maxLines="4"
android:maxLength="2000"
android:hint="@string/compose_hint"/>
antislashn.org Android - Input Method Editor C - 6/44
7. Contrôle de l'IME
● Permettre le redimensionnement
● dans le fichier AndroidManifest.xml
– attribut android:windowSoftInput de l'élément
<activity>
● adjustResize, adjustPan, stateVisible
<activity name="EditContactActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
...
</activity>
antislashn.org Android - Input Method Editor C - 7/44
8. Contrôle de l'IME
● Comportement des boutons
● la touche Enter d'un clavier virtuel passe au champ
suivant
– si le champ en cours de saisie n'est pas multilingues
● en mode "fullscreen", l'IME peut ajouter un bouton à la
droite du champ en saisie
● L'élément <TextView> possède un attribut
android:imeOption qui peut prendre les valeurs
● actionGo, actionSearch,actionNone,
actionSend,actionNext,actionDone
– actionDone ou actionNext par défaut
antislashn.org Android - Input Method Editor C - 8/44
9. Créer son IME
● La création d'un IME permet de
● créer un nouveau clavier virtuel
– ABCDEF au lieu du classique AZERTY
● ajouter des options lors de l'appui long sur un caractère
● créer un mapping personnalisé pour un clavier réel
● changer le mode de saisie : voix, reconnaissance
écriture
● changer le mode de prédilection de la saisie
● ...
antislashn.org Android - Input Method Editor C - 9/44
10. Créer son IME
● L'ajout d'un IME passe par la spécialisation de la
classe InputMethodService
● l'IME apparaît lors dans les paramètres de saisie
– il peut avoir ses propres écrans de paramétrage
● L'IME représente un moyen de saisie
● il possède un cycle de vie
● il peut inter-agir avec le champs en cours de saisie
● il peut posséder des vues secondaires ("candidates
views")
– correction, suggestion
antislashn.org Android - Input Method Editor C - 10/44
11. Créer son IME
● Les types de saisie peuvent être différents
– email, texte, numéro de télephone, …
● l'IME peut détecter le type de saisie en cours
– auprès de la classe EditorInfo
● paramètre de la méthode onStartInputView()
● champ inputType
– masque de constantes
● TYPE_CLASS_TEXT, TYPE_CLASS_PHONE,...
source : Google
antislashn.org Android - Input Method Editor C - 11/44
12. Créer son IME
● Classes principales
● InputMethodService
– classe à redéfinir pour créer son IME et sa prise en charge
par Android
● BaseInputConnection
– canal de communication entre le champ en cours de saisie et
InputMethodService
● peut être null si aucune saisie en cours
● KeyboardView
– vue du clavier virtuel, définit en général dans un fichier XML
● KeyEvent
– événements générés lors de l'appui d'une touche
antislashn.org Android - Input Method Editor C - 12/44
13. Créer son IME
● Cycle de vie de l'IME
● méthodes callback de
InputMethodService
antislashn.org Android - Input Method Editor C - 13/44
14. Créer son IME
● La classe BaseInputConnection possède des
méthodes de gestion du texte en cours de saisie
● getTextBeforCursor()
● getTextAfterCursor()
● deleteSurroundingText()
● commitText()
● sendKeyEvent()
● ...
antislashn.org Android - Input Method Editor C - 14/44
15. Créer son IME – exemple 1
● Cet exemple montre comment redéfinir le mapping
d'un clavier physique
● Android prend en charge pas défaut le QWERTY
● cet exemple est volontairement très simple
● Un clavier envoi au système un scancode
● correspondant à la position physique des touches
– peut dépendre des claviers
● le scancode est ensuite transformé en un code de
touche
● puis le comportement de la touche est adapté en
fonction des autres touches Shift, Atl, Ctrl, ...
antislashn.org Android - Input Method Editor C - 15/44
16. Créer son IME – exemple 1
● Pour effectuer les transformations entre scancode,
code de la touche et comportement, Android utilise
deux fichiers de base
● system/usr/keylayout/Generic.kl
– scancode vers touche
● les code touches Android sont des constantes de KeyEvent
● system/usr/keychars/Generic.kcm
– description du comportement de la touche
● Des fichiers tiers peuvent être présents
● exemple : Vendor_xxxx_Product_yyyy.kl
– xxxx est le VID (Vendor ID)
– yyyy est le PID (Product ID)
antislashn.org Android - Input Method Editor C - 16/44
17. Créer son IME – exemple 1
● Si votre téléphone est rooté, ou si vous créez votre
propre ROM il est donc aisé d'ajouter un fichier
spécifique.
● sous Android voir le fichier /proc/bus/input/devices
I: Bus=0005 Vendor=0a5c Product=8502 Version=011b
N: Name="BeeWi BBK300 Bluetooth Azerty Keyboard"
P: Phys=4C:AA:16:8B:93:AA
S: Sysfs=/devices/platform/tegra_uart.2/tty/ttyHS2/hci0/hci0:12/input8
U: Uniq=00:24:94:C0:07:8C
H: Handlers=event5 keychord
B: PROP=0
B: EV=12001f
B: KEY=70000 10000 2008007 ff9f387a d941d7ff febeffdf ffefffff ffffffff fffffffe
B: REL=3
B: ABS=f00 0
B: MSC=10
B: LED=1f
antislashn.org Android - Input Method Editor C - 17/44
18. Créer son IME – exemple 1
● Extrait de Generic.kl
key 1 ESCAPE
key 2 1
key 3 2
key 4 3
key 5 4
key 6 5
key 7 6
key 8 7
key 9 8
key 10 9
key 11 0
key 12 MINUS
key 13 EQUALS
key 14 DEL
key 15 TAB
key 16 Q
key 17 W
key 18 E
key 19 R
key 20 T
antislashn.org Android - Input Method Editor C - 18/44
19. Créer son IME – exemple 1
● Extrait de Generic.kcm
key A {
label: 'A'
base: 'a'
shift, capslock: 'A'
ctrl, alt, meta: none
}
key B {
label: 'B'
base: 'b'
shift, capslock: 'B'
ctrl, alt, meta: none
}
antislashn.org Android - Input Method Editor C - 19/44
20. Créer son IME – exemple 1
● Extrait de Generic.kcm
● de manière générale
key [keycode] {
label: '[label]'
base: '[key without any modifiers]'
[modifier]: '[key with modifier]'
[modifier]+[modifier]: '[key with both modifiers]'
[modifier],[modifier]: '[key with any of listed modifiers]'
[modifier]: fallback [magic key] # read below
[modifier],[modifier]: none
}
● où le modificateur peut être : alt, ralt, lalt, shift, rshift,
lshift, ctrl, rctrl, lctrl, capslock, meta, rmeta, lmeta
– le code de touche peut aussi être sous format unicode
shift, capslock: 'A'
ralt: 'u0105'
source : http://forum.xda-developers.com/showthread.php?t=1568760
antislashn.org Android - Input Method Editor C - 20/44
21. Créer son IME – exemple 1
● Extrait de Generic.kcm
● le fallback magic key correspond à certaines
commandes comme HOM, SEARCH, MENU, ...
key D {
label: 'D'
base: 'd'
shift, capslock: 'D'
meta: fallback HOME # show desktop
alt: none
}
source : http://forum.xda-developers.com/showthread.php?t=1568760
antislashn.org Android - Input Method Editor C - 21/44
22. Créer son IME – exemple 1
● Afin de refaire le mapping d'un clavier, il est
nécessaire de connaître les scancodes qui sont
générés
● soit en écoutant les événements sur adb
– commande adb shell getevent scancode en hexa – ici touche 'A'
d'un clavier AZERTY BeeWi
touche appuyée
touche relâchée
● soit en créant une application dédiée
– cf. slides suivants
antislashn.org Android - Input Method Editor C - 22/44
23. Créer son IME – exemple 1
● Code permettant de récupérer le scancode
● ne fonctionne pas pour toutes les touches, car certaines
sont prisent en compte au niveau d'Android, et non pas
de l'activité
– HOME, SEARCH, ...
antislashn.org Android - Input Method Editor C - 23/44
24. Créer son IME – exemple 1
● Extrait du code de l'activité
public boolean onKeyDown(int keyCode, KeyEvent event){
StringBuilder builder = new StringBuilder();
builder.append("KeyCode : ").append(keyCode).append(" [").append(String.format("0x%04X", keyCode)).append("]n");
builder.append("event.getScanCode() : ").append(event.getScanCode()).append(" [").append(String.format("0x%04X",
event.getScanCode())).append("]n");
builder.append("event.getAction() : ").append(event.getAction()).append('n');
builder.append("event.getUnicodeChar() : ").append(event.getUnicodeChar()).append(" [").
append(String.format("0x%04X", event.getUnicodeChar())).append("]n");
builder.append("character : ").append(Character.toString((char)event.getUnicodeChar())).append('n');
builder.append("event.getScanCode() : ").append(event.getScanCode()).append('n');
builder.append("event.getDisplayLabel() : ").append(event.getDisplayLabel()).append('n');
builder.append("event.getNumber() : ").append(event.getNumber()).append('n');
int modifiers = event.getMetaState();
if((modifiers & KeyEvent.META_ALT_ON) != 0)
builder.append("ALTn");
if((modifiers & KeyEvent.META_ALT_LEFT_ON) != 0)
...
antislashn.org Android - Input Method Editor C - 24/44
25. Créer son IME – exemple 1
● Nous pouvons passer à notre mapping du clavier
physique
– clavier bluetooth dans notre exemple
● création du fichier de mapping
– l'objectif est de mapper les touches alphanumérique dans
l'ordre ABCDE au lieu de AZERTY
● création de notre classe InputMethodService
● déclaration dans le fichier manifeste
● … puis test
antislashn.org Android - Input Method Editor C - 25/44
26. Créer son IME – exemple 1
● Le fichier de mapping est placé dans le répertoire
assets du projet Eclipse
# format
# scancode;code android;code android Shift;
# le code android est calqué sur les constantes de la classe android.view.KeyCode
# exemple : le scancode décimal 16 est associé à la touche A dans le clavier AZERTY
# le code android est KEYCODE_A, le code dans le fichier est A
# le séparateur est le caractère ;
16;A
17;B
18;C
19;D
20;E
21;F
22;G
23;H
24;I
antislashn.org Android - Input Method Editor C - 26/44
27. Créer son IME – exemple 1
● Une classe utilitaire Key encapsule le scancode et
le code Android
package org.antislashn.android.clavier;
public class Key {
int scancode;
int androidKeyCode;
public Key(int scanCode, int androidKeyCode) {
this.scancode = scanCode;
this.androidKeyCode = androidKeyCode;
}
}
antislashn.org Android - Input Method Editor C - 27/44
28. Créer son IME – exemple 1
● Une classe utilitaire FileParser lit le fichier et
crée une collection de Key
public class FileParser {
static private String separator = ";";
static private String prefix = "KEYCODE_";
static private String tag = "FileParser";
public static SparseArray<Key> parse(Context context,String fileName) throws IOException{
...
return mapping;
}
private static int getAndroidCode(String code) throws SecurityException,
NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
...
}
}
antislashn.org Android - Input Method Editor C - 28/44
29. Créer son IME – exemple 1
● Détail de la méthode parse()
public static SparseArray<Key> parse(Context context,String fileName) throws IOException{
BufferedReader file = new BufferedReader(new InputStreamReader(context.getAssets().open(fileName)));
String line = null;
SparseArray<Key> mapping = new SparseArray<Key>();
while((line=file.readLine())!=null){
line = line.trim();
if(line.charAt(0) == '#' || line.length() == 0)
continue;
String[] fields = line.split(separator);
if(fields.length<2)
continue;
int scanCode = Integer.parseInt(fields[0]);
int androidCode;
try {
androidCode = getAndroidCode(fields[1]);
Key key = new Key(scanCode,androidCode);
mapping.append(scanCode, key);
} catch (Exception e) {
Log.e(tag,"=> Erreur sur la rechecher de "+fields[1],e);
}
}
file.close();
file = null;
return mapping;
}
antislashn.org Android - Input Method Editor C - 29/44
30. Créer son IME – exemple 1
● Détail de la méthode getAndroidCode()
● utilisation de la réflexivité, ce qui permet de s'affranchir
des tests sur les différents niveaux de SDK
private static int getAndroidCode(String code) throws
SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
String fieldName = prefix + code.toUpperCase(Locale.US);
Field attribut = KeyEvent.class.getDeclaredField(fieldName);
if(attribut != null)
return attribut.getInt(null);
return 0;
}
}
antislashn.org Android - Input Method Editor C - 30/44
31. Créer son IME – exemple 1
● Classe spécialisant InputMethodService
public class ClavierImeService extends InputMethodService {
private static String tag = "ClavierImeService";
private static SparseArray<Key> mapping;
private static String fileName = "beewi.txt";
@Override
public void onCreate() { car clavier physique
...
}
@Override
public boolean onEvaluateFullscreenMode() {
car clavier physique
return false;
}
@Override
public boolean onEvaluateInputViewShown() {
return false;
}
...
private void log(String methodName, int keyCode, KeyEvent event) {
...
}
}
antislashn.org Android - Input Method Editor C - 31/44
32. Créer son IME – exemple 1
● méthode onCreate() de ClavierImeService
public void onCreate() {
super.onCreate();
Log.d(tag, ">>>>>>>>>>>> onCreate");
try {
mapping = FileParser.parse(this, fileName);
} catch (Exception e) {
Log.e(tag, ">>> onCreate ERROR :", e);
} finally {
Log.d(tag, ">>> onCreate - chargement du fichier " + fileName);
}
}
antislashn.org Android - Input Method Editor C - 32/44
33. Créer son IME – exemple 1
● méthode remapKey() de ClavierImeService
private boolean remapKey(KeyEvent event) {
boolean keySended = false; création d'un nouvel événement
Key key = mapping.get(event.getScanCode()); avec les caractéristiques de l'ancien
if (key != null) {
Log.d(tag, "+++ caractère remplacé");
sauf le code touche
event = new KeyEvent(event.getDownTime(), event.getEventTime(),
event.getAction(), key.androidKeyCode,
event.getRepeatCount(), event.getMetaState(),
event.getDeviceId(), event.getScanCode(),
event.getFlags());
Log.d(tag, "+++ event.getUnicodeChar() : " + event.getUnicodeChar()
+ " [" + Character.toString((char) event.getUnicodeChar())
+ "]");
InputConnection inputConnection = getCurrentInputConnection();
if (inputConnection != null) {
Log.d(tag, "+++ caractère envoyé");
inputConnection.sendKeyEvent(event);
keySended = true;
}
}
return keySended; si un champ est en saisie, on envoie le
}
nouvel élément
antislashn.org Android - Input Method Editor C - 33/44
34. Créer son IME – exemple 1
● méthode onKeyUp() et onKeyDown() de
ClavierImeService
public boolean onKeyUp(int keyCode, KeyEvent event) { si le nouvel événement a été envoyé
log("onKeyUp", keyCode, event); on renvoie true pour indiquer au
if(remapKey(event)) framework que l'événement a été
return true; traité
else
return super.onKeyUp(event.getKeyCode(), event);
}
public boolean onKeyDown(int keyCode, KeyEvent event) { sinon on repasse l'événement au
log("onKeyDown", keyCode, event); framework
if(remapKey(event))
return true;
else
return super.onKeyDown(event.getKeyCode(), event);
}
antislashn.org Android - Input Method Editor C - 34/44
36. Créer son IME – exemple 1
● Déclaration de l'InputMethodService dans le
fichier manifeste
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<service android:name="org.antislashn.android.clavier.ClavierImeService"
android:permission="android.permission.BIND_INPUT_METHOD" >
<intent-filter>
<action android:name="android.view.InputMethod" /> permission nécessaire
</intent-filter>
<meta-data
android:name="android.view.im"
android:resource="@xml/method" />
publication d'éventuelles infos
</service> supplémentaires pour l'IME
</application>
<input-method xmlns:android="http://schemas.android.com/apk/res/android" />
antislashn.org Android - Input Method Editor C - 36/44
37. Prise en compte des touches de
contrôle
● L'ensemble des touches de contrôle d'un clavier
sont référencées
● avec la constante type KEYCODE_
– qui associe un sancode et une touche
– exemple scancode de 15 et KEYCODE_TAB
● avec la constante type META
– qui caractérise une touche et les touches de modification de
comportement (modifier key)
● L'appui sur une touche déclenche un KeyEvent qui
comprends le code de la touche appuyée et les
touches muettes associées
antislashn.org Android - Input Method Editor C - 37/44
38. Prise en compte des touches de
contrôle
● Exemple
● appui sur Ctrl et A
● l'affichage de l'événement donnera
– keycode : 29 (constante KEYCODE_A)
– valeur des métas : 0x3000
● obtenu par la méthode getMetaState
● qui est composé d'un OU logique des maques suivants
– META_CTRL_ON (valeur : 0x00001000)
– META_CTRL_LEFT_ON (valeur : 0x00002000)
antislashn.org Android - Input Method Editor C - 38/44
39. Prise en compte des touches de
contrôle
● Tout un ensemble de méthodes de KeyEvent
permet de connaître les touches appuyées
● isShiftPressed(), isAltPressed(), …
● Pour changer le comportement par défaut des
combinaisons de touches il faut :
● ne pas transmettre l'état des métas à l'événement qui
sera recréé
● agir sur les métas du InputConnection de la saisie
en cours
antislashn.org Android - Input Method Editor C - 39/44
40. Prise en compte des touches de
contrôle
● Par exemple nous souhaitons afficher la lettre C
avec la combinaison Ctrl + A
● suppression des métas dans l'événement recréé
private KeyEvent changeKeyEvent(KeyEvent event) {
return new KeyEvent(event.getDownTime(),event.getEventTime(),
event.getAction(), androidKeyCode,
event.getRepeatCount(), 0,
event.getDeviceId(),event.getScanCode(),
event.getFlags());
}
mise à zéro de l'état des métas
antislashn.org Android - Input Method Editor C - 40/44
41. Prise en compte des touches de
contrôle
● puis masque avec la valeur des métas à ne pas prendre
en compte sur InputConnection
private boolean sendKeyEvent(KeyEvent event){
boolean keySended = false;
InputConnection inputConnection = imeService.getCurrentInputConnection();
if (inputConnection != null) {
Log.d(tag, "+++ caractère envoyé");
inputConnection.clearMetaKeyStates(maskMeta);
inputConnection.sendKeyEvent(event);
keySended = true;
}
return keySended;
}
mise à zéro des métas – le masque appliqué
dépend des métas à inhiber
antislashn.org Android - Input Method Editor C - 41/44
42. Envoi d'un code Unicode
● Un caractère unicode n'a pas de constante
spécifique
● pas de constante KEYCODE_
● Il faut donc directement utiliser les fonctions
d'édition du InputConnection
● dans l'exemple suivant, nous enverrons le caractère
0x212B correspondant à Ä
antislashn.org Android - Input Method Editor C - 42/44
43. Envoi d'un caractère Unicode
● exemple de code
private boolean sendKeyEvent(KeyEvent event){
boolean keySended = false;
InputConnection inputConnection = imeService.getCurrentInputConnection();
if (inputConnection != null) {
Log.d(tag, "+++ caractère envoyé");
inputConnection.clearMetaKeyStates(maskMeta); envoi uniquement sur la
if(event.getAction()==KeyEvent.ACTION_UP){ touche relachée
String car = new String(new char[]{0x212B});
inputConnection.commitText(car, 1);
}
keySended = true; construction du caractère Ä
}
return keySended;
}
affichage du caractère par composition
du texte dans le InputConnection
antislashn.org Android - Input Method Editor C - 43/44
44. Ressources
● Web
● http://developer.android.com/guide/topics/text/creating-input-method.html
● http://android-developers.blogspot.fr/2009/04/updating-applications-for-on-screen.html
● http://forum.xda-developers.com/showthread.php?t=1568760
antislashn.org Android - Input Method Editor C - 44/44