Upload
franck-simon
View
2.346
Download
2
Embed Size (px)
DESCRIPTION
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.
Citation preview
antislashn.org Android - Input Method Editor C - 1/44
Input Method Editor
antislashn.org Android - Input Method Editor C - 2/44
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 - 3/44
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 - 4/44
Introduction
● "pan and scan"
source : Google
antislashn.org Android - Input Method Editor C - 5/44
Introduction
● "fullscreen"
source : Google
antislashn.org Android - Input Method Editor C - 6/44
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 - 7/44
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 - 8/44
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 - 9/44
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 - 10/44
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 - 11/44
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 - 12/44
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 - 13/44
Créer son IME
● Cycle de vie de l'IME● méthodes callback de InputMethodService
antislashn.org Android - Input Method Editor C - 14/44
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 - 15/44
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 - 16/44
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 - 17/44
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=011bN: Name="BeeWi BBK300 Bluetooth Azerty Keyboard"P: Phys=4C:AA:16:8B:93:AAS: Sysfs=/devices/platform/tegra_uart.2/tty/ttyHS2/hci0/hci0:12/input8U: Uniq=00:24:94:C0:07:8CH: Handlers=event5 keychord B: PROP=0B: EV=12001fB: KEY=70000 10000 2008007 ff9f387a d941d7ff febeffdf ffefffff ffffffff fffffffeB: REL=3B: ABS=f00 0B: MSC=10B: LED=1f
antislashn.org Android - Input Method Editor C - 18/44
Créer son IME – exemple 1
● Extrait de Generic.klkey 1 ESCAPEkey 2 1key 3 2key 4 3key 5 4key 6 5key 7 6key 8 7key 9 8key 10 9key 11 0key 12 MINUSkey 13 EQUALSkey 14 DELkey 15 TABkey 16 Qkey 17 Wkey 18 Ekey 19 Rkey 20 T
antislashn.org Android - Input Method Editor C - 19/44
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 - 20/44
Créer son IME – exemple 1
● Extrait de Generic.kcm● de manière générale
● 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
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}
shift, capslock: 'A'ralt: '\u0105'
source : http://forum.xda-developers.com/showthread.php?t=1568760
antislashn.org Android - Input Method Editor C - 21/44
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 - 22/44
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
● soit en créant une application dédiée– cf. slides suivants
scancode en hexa – ici touche 'A'd'un clavier AZERTY BeeWi
touche appuyée
touche relâchée
antislashn.org Android - Input Method Editor C - 23/44
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 - 24/44
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("ALT\n");if((modifiers & KeyEvent.META_ALT_LEFT_ON) != 0)...
antislashn.org Android - Input Method Editor C - 25/44
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 - 26/44
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;A17;B18;C19;D20;E21;F22;G23;H24;I
antislashn.org Android - Input Method Editor C - 27/44
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 - 28/44
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 - 29/44
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 - 30/44
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 - 31/44
Créer son IME – exemple 1
● Classe spécialisant InputMethodServicepublic class ClavierImeService extends InputMethodService {
private static String tag = "ClavierImeService";private static SparseArray<Key> mapping;private static String fileName = "beewi.txt";
@Overridepublic void onCreate() {
...}
@Overridepublic boolean onEvaluateFullscreenMode() {
return false;}
@Overridepublic boolean onEvaluateInputViewShown() {
return false;}
...
private void log(String methodName, int keyCode, KeyEvent event) {...
}
}
car clavier physique
car clavier physique
antislashn.org Android - Input Method Editor C - 32/44
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 - 33/44
Créer son IME – exemple 1
● méthode remapKey() de ClavierImeService
private boolean remapKey(KeyEvent event) {boolean keySended = false;Key key = mapping.get(event.getScanCode());if (key != null) {
Log.d(tag, "+++ caractère remplacé");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;
}
création d'un nouvel événementavec les caractéristiques de l'anciensauf le code touche
si un champ est en saisie, on envoie lenouvel élément
antislashn.org Android - Input Method Editor C - 34/44
Créer son IME – exemple 1
● méthode onKeyUp() et onKeyDown() de ClavierImeService
public boolean onKeyUp(int keyCode, KeyEvent event) {log("onKeyUp", keyCode, event);if(remapKey(event))
return true;else
return super.onKeyUp(event.getKeyCode(), event);}
public boolean onKeyDown(int keyCode, KeyEvent event) {log("onKeyDown", keyCode, event);if(remapKey(event))
return true;else
return super.onKeyDown(event.getKeyCode(), event);}
si le nouvel événement a été envoyéon renvoie true pour indiquer auframework que l'événement a ététraité
sinon on repasse l'événement au framework
antislashn.org Android - Input Method Editor C - 35/44
Créer son IME – exemple 1
● méthode log() de ClavierImeServiceprivate void log(String methodName, int keyCode, KeyEvent event) {
Log.d(tag, ">>>>>>>>>>>>>>>> " + methodName + " <<<<<<<<<<<<<<<<<<<<<<");Log.d(tag, "=> keyCode : " + keyCode);Log.d(tag, "=> event.getKeyCode() : " + event.getKeyCode() + " [ "
+ Character.toString((char) event.getKeyCode()) + " ]");Log.d(tag, "=> event.getUnicodeChar() : " + event.getUnicodeChar()
+ " [ " + Character.toString((char) event.getUnicodeChar())+ " ]");
Log.d(tag, "=> event.scanCode() : " + event.getScanCode());Log.d(tag, "=> event.getUnicodeChar() : " + event.getUnicodeChar());Log.d(tag, "=> event.getDisplayLabel() : " + event.getDisplayLabel());Log.d(tag, "=> event.getAction() : " + event.getAction());
}
antislashn.org Android - Input Method Editor C - 36/44
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" /> </intent-filter> <meta-data android:name="android.view.im" android:resource="@xml/method" /> </service> </application>
permission nécessaire
<input-method xmlns:android="http://schemas.android.com/apk/res/android" />
publication d'éventuelles infossupplémentaires pour l'IME
antislashn.org Android - Input Method Editor C - 37/44
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 - 38/44
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 - 39/44
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 - 40/44
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 - 41/44
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 - 42/44
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 - 43/44
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);if(event.getAction()==KeyEvent.ACTION_UP){
String car = new String(new char[]{0x212B});inputConnection.commitText(car, 1);
}keySended = true;
}return keySended;
}
construction du caractère Ä
affichage du caractère par compositiondu texte dans le InputConnection
envoi uniquement sur latouche relachée
antislashn.org Android - Input Method Editor C - 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