44
antislashn.org Android - Input Method Editor C - 1/44 Input Method Editor

Android Input Method Editor

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

Page 1: Android   Input Method Editor

antislashn.org Android - Input Method Editor C - 1/44

Input Method Editor

Page 2: Android   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– …

Page 3: Android   Input Method Editor

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

Page 4: Android   Input Method Editor

antislashn.org Android - Input Method Editor C - 4/44

Introduction

● "pan and scan"

source : Google

Page 5: Android   Input Method Editor

antislashn.org Android - Input Method Editor C - 5/44

Introduction

● "fullscreen"

source : Google

Page 6: Android   Input Method Editor

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"/>

Page 7: Android   Input Method Editor

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>

Page 8: Android   Input Method Editor

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

Page 9: Android   Input Method Editor

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● ...

Page 10: Android   Input Method Editor

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

Page 11: Android   Input Method Editor

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

Page 12: Android   Input Method Editor

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

Page 13: Android   Input Method Editor

antislashn.org Android - Input Method Editor C - 13/44

Créer son IME

● Cycle de vie de l'IME● méthodes callback de InputMethodService

Page 14: Android   Input Method Editor

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()● ...

Page 15: Android   Input Method Editor

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, ...

Page 16: Android   Input Method Editor

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)

Page 17: Android   Input Method Editor

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

Page 18: Android   Input Method Editor

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

Page 19: Android   Input Method Editor

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}

Page 20: Android   Input Method Editor

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

Page 21: Android   Input Method Editor

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

Page 22: Android   Input Method Editor

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

Page 23: Android   Input Method Editor

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, ...

Page 24: Android   Input Method Editor

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)...

Page 25: Android   Input Method Editor

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

Page 26: Android   Input Method Editor

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

Page 27: Android   Input Method Editor

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;

}}

Page 28: Android   Input Method Editor

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 {

...

}}

Page 29: Android   Input Method Editor

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;

}

Page 30: Android   Input Method Editor

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;}

}

Page 31: Android   Input Method Editor

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

Page 32: Android   Input Method Editor

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);}

}

Page 33: Android   Input Method Editor

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

Page 34: Android   Input Method Editor

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

Page 35: Android   Input Method Editor

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());

}

Page 36: Android   Input Method Editor

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

Page 37: Android   Input Method Editor

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

Page 38: Android   Input Method Editor

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)

Page 39: Android   Input Method Editor

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

Page 40: Android   Input Method Editor

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

Page 41: Android   Input Method Editor

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

Page 42: Android   Input Method Editor

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 à Ä

Page 43: Android   Input Method Editor

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

Page 44: Android   Input Method Editor

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