📜 ⬆️ ⬇️

Gesturin - input gestures for Android


In this post I will tell you how to create an input method with gestures for Android. This input method can be made like handwriting input, but there is one limitation - you need to draw a letter (number, word) without removing your finger. The implementation includes an input method and a gesture customization utility, which is a reworked “Gesture Builder” from the examples for the Android SDK.


Creating a "keyboard"


Let's start by creating a keyboard class . All input methods are made on the basis of InputMethodService, and for gesture recognition we included OnGesturePerformedListener. To avoid problems with recognition, each language will have a separate file. To do this, we add two properties to the class: a map of languages ​​and the name of the current language.
// HashMap< ,  > private HashMap<String, GestureLibrary> mGestureLibMap; //   private String mCurrentGestureLib = null; 

The languages ​​load will be the loadLanguages ​​method, which will scan the application files directory and load all the contents as languages.
 private void loadLanguages() { //   "" File[] files = getFilesDir().listFiles(); mGestureLibMap = new HashMap<String, GestureLibrary>(); //  "" for(int i = 0; i < files.length; i++) { GestureLibrary gestureLib = GestureLibraries.fromFile(files[i]); if (!gestureLib.load()) { Toast.makeText(this, "Can't load gestures " + files[i].getName(), Toast.LENGTH_LONG).show(); continue; } //     mGestureLibMap.put(files[i].getName(), gestureLib); } if(mCurrentGestureLib == null || !mGestureLibMap.containsKey(mCurrentGestureLib)) { //       mCurrentGestureLib = mGestureLibMap.keySet().toArray(new String[0])[0]; if(mGestureLibMap.containsKey(mCurrentGestureLib.toLowerCase())) mCurrentGestureLib = mCurrentGestureLib.toLowerCase(); } } 

loadLanguages ​​will be called in the onStartInputView method, which is called each time the keyboard is called. This solution may cause brakes with a large number of languages, but so there will always be "fresh" gestures. It could have been contrived with the file of the latest update of gestures or the static property, but I didn’t cunning as I didn’t notice any brakes with the standard set (en, ru, 09).

Gestures are recognized in the onGesturePerformed method (more details about gestures can be found here ). If the name of the gesture begins with "@@", then we will assume that it is followed by a keyCode, which we will send to the application. If it starts with something else, then we will send it as text. The method itself looks like this:
 public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) { //   ArrayList<Prediction> predictions = mGestureLibMap.get(mCurrentGestureLib).recognize(gesture); if (predictions.size() > 0) { //     Prediction prediction = predictions.get(0); if(prediction.name.startsWith("@@")) { //     @@,        try { keyDownUp(Integer.valueOf(prediction.name.substring(2))); } catch(NumberFormatException e) { Log.w("GestureKeyboard", "NumberFormatException: " + e.getLocalizedMessage()); } } else { //      getCurrentInputConnection().commitText(editString(prediction.name), prediction.name.length()); } } } 

In this method, you can notice an unknown editString method. This method changes the register depending on the state of the “Upper” and “Lower” buttons.
 private String editString(String in) { //   lower,    LowerCase if(mLowerLock.isChecked()) return in.toLowerCase(); //  upper,   UpperCase if(mUpperLock.isChecked()) return in.toUpperCase(); //    return in; } 

And the keyDownUp method sends two events to the application - keyDown and keyUp. It was taken from the SoftKeyboard example and looks like this:
 private void keyDownUp(int keyEventCode) { //   keyDown  keyUp getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode)); getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode)); } 

We loaded various languages, so they can be changed. They change a bunch of two methods - onLangClick and setCurrentLanguage. When you click on the onLangClick button, we search for the list of languages ​​following the current one, or (if there is no such) change the language to the very first. When changing the language, the state of the “Caps Lock” button is also taken into account.
 public void onLangClick(View v) { //      //    String gestureNames[] = mGestureLibMap.keySet().toArray(new String[0]); boolean next = false; for(int i = 0; i < gestureNames.length; i++) { if(next) { //      if(!mCurrentGestureLib.toLowerCase().equals(gestureNames[i].toLowerCase())) { //        setCurrentLanguage(gestureNames, i); next = false; break; } continue; } if(mCurrentGestureLib.toLowerCase().equals(gestureNames[i].toLowerCase())) { //      next = true; } } if(next) setCurrentLanguage(gestureNames, 0); //   mLangSwither.setText(mCurrentGestureLib); } private void setCurrentLanguage(String gestureNames[], int index) { mCurrentGestureLib = gestureNames[index]; //      LowerCase,    if(mGestureLibMap.containsKey(gestureNames[index].toLowerCase())) { mCurrentGestureLib = gestureNames[index].toLowerCase(); } //   ,      UpperCase     if(mCapsLock.isChecked() && mGestureLibMap.containsKey(gestureNames[index].toUpperCase())) { mCurrentGestureLib = gestureNames[index].toUpperCase(); } } 

To change the register, use three buttons: Upper, Lower, Caps. Something interesting is just a caps. The language can be in two registers. The register can be written in the language name (example: en and en). And the caps button switches these registers, or if they are not present, changes the state of the “Upper” and “Lower” buttons.
 if(isChecked) { mLowerLock.setChecked(false); if(mGestureLibMap.containsKey(mCurrentGestureLib.toUpperCase())) { //    UpperCase mUpperLock.setChecked(false); mCurrentGestureLib = mCurrentGestureLib.toUpperCase(); } else { //     UpperCase,   UpperLock mUpperLock.setChecked(true); } } else { mUpperLock.setChecked(false); if(mGestureLibMap.containsKey(mCurrentGestureLib.toLowerCase())) { //    LowerCase mLowerLock.setChecked(false); mCurrentGestureLib = mCurrentGestureLib.toLowerCase(); } else { //   LowerLock mLowerLock.setChecked(true); } } //   mLangSwither.setText(mCurrentGestureLib); 

Having a little embellished UI, it turned out what is shown in the screenshot.
The input method class is ready, now you need something like this in AndroidManifest.xml :
')
 <service android:name="GestureKeyboard" 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> 

Having slightly embellished UI , it turned out that shown in the screenshot .

I will tell you about the settings in words. From the beginning, I took the source code of Gesture Builder from the Android SDK, and copied the necessary sources and resources into my project. Resources and source codes have been cleared from "superfluous". Next, I changed the GestureBuilderActivity class so that it opens the specified gestures (and not / sdcard / gestures). And made a list of languages with functions to add, delete and rename.

Result


The resulting “keyboard” doesn’t badly recognize my handwriting, but I didn’t use it on a regular basis - the keys are more convenient for me. All sources can be viewed on googlecode.com . In the same place download apk with the built-in English, Russian, and digital (for my handwriting).

Source: https://habr.com/ru/post/121153/


All Articles