Android Input Method Editor

3,020 views

Published on

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.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
3,020
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
33
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Android Input Method Editor

  1. 1. Input Method Editorantislashn.org Android - Input Method Editor C - 1/44
  2. 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. 3. Introduction ● La saisie est effectuée via un IME ● Input Method Editor ● IMF supporte une grand nombre dIME ● En général lutilisateur accède à un IME software particulier de manière transparente ● lors de la saisie dun champ – texte, date, heure, mot de passe, … ● Android arrange automatiquement le bureau – "pan and scan" qui permet le défilement de lécran de lapplication afin que la zone de saisie soit toujours visible – "fullscrenn" qui est utilisé si lIME est trop large pour partager lespace avec lécran de lapplicationantislashn.org Android - Input Method Editor C - 3/44
  4. 4. Introduction ● "pan and scan" source : Googleantislashn.org Android - Input Method Editor C - 4/44
  5. 5. Introduction ● "fullscreen" source : Googleantislashn.org Android - Input Method Editor C - 5/44
  6. 6. Contrôle de lIME ● Les attributs XML des zones dédition permettent de contrôle le type dIME 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. 7. Contrôle de lIME ● 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. 8. Contrôle de lIME ● Comportement des boutons ● la touche Enter dun clavier virtuel passe au champ suivant – si le champ en cours de saisie nest pas multilingues ● en mode "fullscreen", lIME 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éfautantislashn.org Android - Input Method Editor C - 8/44
  9. 9. Créer son IME ● La création dun IME permet de ● créer un nouveau clavier virtuel – ABCDEF au lieu du classique AZERTY ● ajouter des options lors de lappui 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. 10. Créer son IME ● Lajout dun IME passe par la spécialisation de la classe InputMethodService ● lIME apparaît lors dans les paramètres de saisie – il peut avoir ses propres écrans de paramétrage ● LIME 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, suggestionantislashn.org Android - Input Method Editor C - 10/44
  11. 11. Créer son IME ● Les types de saisie peuvent être différents – email, texte, numéro de télephone, … ● lIME 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 : Googleantislashn.org Android - Input Method Editor C - 11/44
  12. 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 lappui dune toucheantislashn.org Android - Input Method Editor C - 12/44
  13. 13. Créer son IME ● Cycle de vie de lIME ● méthodes callback de InputMethodServiceantislashn.org Android - Input Method Editor C - 13/44
  14. 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. 15. Créer son IME – exemple 1 ● Cet exemple montre comment redéfinir le mapping dun 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. 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. 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é dajouter 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=1fantislashn.org Android - Input Method Editor C - 17/44
  18. 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 Tantislashn.org Android - Input Method Editor C - 18/44
  19. 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. 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=1568760antislashn.org Android - Input Method Editor C - 20/44
  21. 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=1568760antislashn.org Android - Input Method Editor C - 21/44
  22. 22. Créer son IME – exemple 1 ● Afin de refaire le mapping dun 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 dun clavier AZERTY BeeWi touche appuyée touche relâchée ● soit en créant une application dédiée – cf. slides suivantsantislashn.org Android - Input Method Editor C - 22/44
  23. 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 dAndroid, et non pas de lactivité – HOME, SEARCH, ...antislashn.org Android - Input Method Editor C - 23/44
  24. 24. Créer son IME – exemple 1 ● Extrait du code de lactivité 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. 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 – lobjectif est de mapper les touches alphanumérique dans lordre ABCDE au lieu de AZERTY ● création de notre classe InputMethodService ● déclaration dans le fichier manifeste ● … puis testantislashn.org Android - Input Method Editor C - 25/44
  26. 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;Iantislashn.org Android - Input Method Editor C - 26/44
  27. 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. 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. 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. 30. Créer son IME – exemple 1 ● Détail de la méthode getAndroidCode() ● utilisation de la réflexivité, ce qui permet de saffranchir des tests sur les différents niveaux de SDKprivate 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. 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. 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. 33. Créer son IME – exemple 1 ● méthode remapKey() de ClavierImeService private boolean remapKey(KeyEvent event) { boolean keySended = false; création dun nouvel événement Key key = mapping.get(event.getScanCode()); avec les caractéristiques de lancien 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émentantislashn.org Android - Input Method Editor C - 33/44
  34. 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
  35. 35. Créer son IME – exemple 1 ● méthode log() de ClavierImeService private 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 - 35/44
  36. 36. Créer son IME – exemple 1 ● Déclaration de lInputMethodService 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 lIME </application> <input-method xmlns:android="http://schemas.android.com/apk/res/android" />antislashn.org Android - Input Method Editor C - 36/44
  37. 37. Prise en compte des touches decontrôle ● Lensemble des touches de contrôle dun 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) ● Lappui sur une touche déclenche un KeyEvent qui comprends le code de la touche appuyée et les touches muettes associéesantislashn.org Android - Input Method Editor C - 37/44
  38. 38. Prise en compte des touches decontrôle ● Exemple ● appui sur Ctrl et A ● laffichage de lévénement donnera – keycode : 29 (constante KEYCODE_A) – valeur des métas : 0x3000 ● obtenu par la méthode getMetaState ● qui est composé dun 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. 39. Prise en compte des touches decontrô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 coursantislashn.org Android - Input Method Editor C - 39/44
  40. 40. Prise en compte des touches decontrô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étasantislashn.org Android - Input Method Editor C - 40/44
  41. 41. Prise en compte des touches decontrô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 à inhiberantislashn.org Android - Input Method Editor C - 41/44
  42. 42. Envoi dun code Unicode ● Un caractère unicode na pas de constante spécifique ● pas de constante KEYCODE_ ● Il faut donc directement utiliser les fonctions dédition du InputConnection ● dans lexemple suivant, nous enverrons le caractère 0x212B correspondant à Äantislashn.org Android - Input Method Editor C - 42/44
  43. 43. Envoi dun 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 InputConnectionantislashn.org Android - Input Method Editor C - 43/44
  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=1568760antislashn.org Android - Input Method Editor C - 44/44

×