• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008-2009 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.example.android.softkeyboard;
18 
19 import android.inputmethodservice.InputMethodService;
20 import android.inputmethodservice.Keyboard;
21 import android.inputmethodservice.KeyboardView;
22 import android.text.method.MetaKeyKeyListener;
23 import android.util.Log;
24 import android.view.KeyCharacterMap;
25 import android.view.KeyEvent;
26 import android.view.View;
27 import android.view.inputmethod.CompletionInfo;
28 import android.view.inputmethod.EditorInfo;
29 import android.view.inputmethod.InputConnection;
30 import android.view.inputmethod.InputMethodManager;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 /**
36  * Example of writing an input method for a soft keyboard.  This code is
37  * focused on simplicity over completeness, so it should in no way be considered
38  * to be a complete soft keyboard implementation.  Its purpose is to provide
39  * a basic example for how you would get started writing an input method, to
40  * be fleshed out as appropriate.
41  */
42 public class SoftKeyboard extends InputMethodService
43         implements KeyboardView.OnKeyboardActionListener {
44     static final boolean DEBUG = false;
45 
46     /**
47      * This boolean indicates the optional example code for performing
48      * processing of hard keys in addition to regular text generation
49      * from on-screen interaction.  It would be used for input methods that
50      * perform language translations (such as converting text entered on
51      * a QWERTY keyboard to Chinese), but may not be used for input methods
52      * that are primarily intended to be used for on-screen text entry.
53      */
54     static final boolean PROCESS_HARD_KEYS = true;
55 
56     private KeyboardView mInputView;
57     private CandidateView mCandidateView;
58     private CompletionInfo[] mCompletions;
59 
60     private StringBuilder mComposing = new StringBuilder();
61     private boolean mPredictionOn;
62     private boolean mCompletionOn;
63     private int mLastDisplayWidth;
64     private boolean mCapsLock;
65     private long mLastShiftTime;
66     private long mMetaState;
67 
68     private LatinKeyboard mSymbolsKeyboard;
69     private LatinKeyboard mSymbolsShiftedKeyboard;
70     private LatinKeyboard mQwertyKeyboard;
71 
72     private LatinKeyboard mCurKeyboard;
73 
74     private String mWordSeparators;
75 
76     /**
77      * Main initialization of the input method component.  Be sure to call
78      * to super class.
79      */
onCreate()80     @Override public void onCreate() {
81         super.onCreate();
82         mWordSeparators = getResources().getString(R.string.word_separators);
83     }
84 
85     /**
86      * This is the point where you can do all of your UI initialization.  It
87      * is called after creation and any configuration change.
88      */
onInitializeInterface()89     @Override public void onInitializeInterface() {
90         if (mQwertyKeyboard != null) {
91             // Configuration changes can happen after the keyboard gets recreated,
92             // so we need to be able to re-build the keyboards if the available
93             // space has changed.
94             int displayWidth = getMaxWidth();
95             if (displayWidth == mLastDisplayWidth) return;
96             mLastDisplayWidth = displayWidth;
97         }
98         mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty);
99         mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols);
100         mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift);
101     }
102 
103     /**
104      * Called by the framework when your view for creating input needs to
105      * be generated.  This will be called the first time your input method
106      * is displayed, and every time it needs to be re-created such as due to
107      * a configuration change.
108      */
onCreateInputView()109     @Override public View onCreateInputView() {
110         mInputView = (KeyboardView) getLayoutInflater().inflate(
111                 R.layout.input, null);
112         mInputView.setOnKeyboardActionListener(this);
113         mInputView.setKeyboard(mQwertyKeyboard);
114         return mInputView;
115     }
116 
117     /**
118      * Called by the framework when your view for showing candidates needs to
119      * be generated, like {@link #onCreateInputView}.
120      */
onCreateCandidatesView()121     @Override public View onCreateCandidatesView() {
122         mCandidateView = new CandidateView(this);
123         mCandidateView.setService(this);
124         return mCandidateView;
125     }
126 
127     /**
128      * This is the main point where we do our initialization of the input method
129      * to begin operating on an application.  At this point we have been
130      * bound to the client, and are now receiving all of the detailed information
131      * about the target of our edits.
132      */
onStartInput(EditorInfo attribute, boolean restarting)133     @Override public void onStartInput(EditorInfo attribute, boolean restarting) {
134         super.onStartInput(attribute, restarting);
135 
136         // Reset our state.  We want to do this even if restarting, because
137         // the underlying state of the text editor could have changed in any way.
138         mComposing.setLength(0);
139         updateCandidates();
140 
141         if (!restarting) {
142             // Clear shift states.
143             mMetaState = 0;
144         }
145 
146         mPredictionOn = false;
147         mCompletionOn = false;
148         mCompletions = null;
149 
150         // We are now going to initialize our state based on the type of
151         // text being edited.
152         switch (attribute.inputType&EditorInfo.TYPE_MASK_CLASS) {
153             case EditorInfo.TYPE_CLASS_NUMBER:
154             case EditorInfo.TYPE_CLASS_DATETIME:
155                 // Numbers and dates default to the symbols keyboard, with
156                 // no extra features.
157                 mCurKeyboard = mSymbolsKeyboard;
158                 break;
159 
160             case EditorInfo.TYPE_CLASS_PHONE:
161                 // Phones will also default to the symbols keyboard, though
162                 // often you will want to have a dedicated phone keyboard.
163                 mCurKeyboard = mSymbolsKeyboard;
164                 break;
165 
166             case EditorInfo.TYPE_CLASS_TEXT:
167                 // This is general text editing.  We will default to the
168                 // normal alphabetic keyboard, and assume that we should
169                 // be doing predictive text (showing candidates as the
170                 // user types).
171                 mCurKeyboard = mQwertyKeyboard;
172                 mPredictionOn = true;
173 
174                 // We now look for a few special variations of text that will
175                 // modify our behavior.
176                 int variation = attribute.inputType &  EditorInfo.TYPE_MASK_VARIATION;
177                 if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
178                         variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
179                     // Do not display predictions / what the user is typing
180                     // when they are entering a password.
181                     mPredictionOn = false;
182                 }
183 
184                 if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
185                         || variation == EditorInfo.TYPE_TEXT_VARIATION_URI
186                         || variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
187                     // Our predictions are not useful for e-mail addresses
188                     // or URIs.
189                     mPredictionOn = false;
190                 }
191 
192                 if ((attribute.inputType&EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
193                     // If this is an auto-complete text view, then our predictions
194                     // will not be shown and instead we will allow the editor
195                     // to supply their own.  We only show the editor's
196                     // candidates when in fullscreen mode, otherwise relying
197                     // own it displaying its own UI.
198                     mPredictionOn = false;
199                     mCompletionOn = isFullscreenMode();
200                 }
201 
202                 // We also want to look at the current state of the editor
203                 // to decide whether our alphabetic keyboard should start out
204                 // shifted.
205                 updateShiftKeyState(attribute);
206                 break;
207 
208             default:
209                 // For all unknown input types, default to the alphabetic
210                 // keyboard with no special features.
211                 mCurKeyboard = mQwertyKeyboard;
212                 updateShiftKeyState(attribute);
213         }
214 
215         // Update the label on the enter key, depending on what the application
216         // says it will do.
217         mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions);
218     }
219 
220     /**
221      * This is called when the user is done editing a field.  We can use
222      * this to reset our state.
223      */
onFinishInput()224     @Override public void onFinishInput() {
225         super.onFinishInput();
226 
227         // Clear current composing text and candidates.
228         mComposing.setLength(0);
229         updateCandidates();
230 
231         // We only hide the candidates window when finishing input on
232         // a particular editor, to avoid popping the underlying application
233         // up and down if the user is entering text into the bottom of
234         // its window.
235         setCandidatesViewShown(false);
236 
237         mCurKeyboard = mQwertyKeyboard;
238         if (mInputView != null) {
239             mInputView.closing();
240         }
241     }
242 
onStartInputView(EditorInfo attribute, boolean restarting)243     @Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
244         super.onStartInputView(attribute, restarting);
245         // Apply the selected keyboard to the input view.
246         mInputView.setKeyboard(mCurKeyboard);
247         mInputView.closing();
248     }
249 
250     /**
251      * Deal with the editor reporting movement of its cursor.
252      */
onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)253     @Override public void onUpdateSelection(int oldSelStart, int oldSelEnd,
254             int newSelStart, int newSelEnd,
255             int candidatesStart, int candidatesEnd) {
256         super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
257                 candidatesStart, candidatesEnd);
258 
259         // If the current selection in the text view changes, we should
260         // clear whatever candidate text we have.
261         if (mComposing.length() > 0 && (newSelStart != candidatesEnd
262                 || newSelEnd != candidatesEnd)) {
263             mComposing.setLength(0);
264             updateCandidates();
265             InputConnection ic = getCurrentInputConnection();
266             if (ic != null) {
267                 ic.finishComposingText();
268             }
269         }
270     }
271 
272     /**
273      * This tells us about completions that the editor has determined based
274      * on the current text in it.  We want to use this in fullscreen mode
275      * to show the completions ourself, since the editor can not be seen
276      * in that situation.
277      */
onDisplayCompletions(CompletionInfo[] completions)278     @Override public void onDisplayCompletions(CompletionInfo[] completions) {
279         if (mCompletionOn) {
280             mCompletions = completions;
281             if (completions == null) {
282                 setSuggestions(null, false, false);
283                 return;
284             }
285 
286             List<String> stringList = new ArrayList<String>();
287             for (int i=0; i<(completions != null ? completions.length : 0); i++) {
288                 CompletionInfo ci = completions[i];
289                 if (ci != null) stringList.add(ci.getText().toString());
290             }
291             setSuggestions(stringList, true, true);
292         }
293     }
294 
295     /**
296      * This translates incoming hard key events in to edit operations on an
297      * InputConnection.  It is only needed when using the
298      * PROCESS_HARD_KEYS option.
299      */
translateKeyDown(int keyCode, KeyEvent event)300     private boolean translateKeyDown(int keyCode, KeyEvent event) {
301         mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState,
302                 keyCode, event);
303         int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(mMetaState));
304         mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
305         InputConnection ic = getCurrentInputConnection();
306         if (c == 0 || ic == null) {
307             return false;
308         }
309 
310         boolean dead = false;
311 
312         if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
313             dead = true;
314             c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
315         }
316 
317         if (mComposing.length() > 0) {
318             char accent = mComposing.charAt(mComposing.length() -1 );
319             int composed = KeyEvent.getDeadChar(accent, c);
320 
321             if (composed != 0) {
322                 c = composed;
323                 mComposing.setLength(mComposing.length()-1);
324             }
325         }
326 
327         onKey(c, null);
328 
329         return true;
330     }
331 
332     /**
333      * Use this to monitor key events being delivered to the application.
334      * We get first crack at them, and can either resume them or let them
335      * continue to the app.
336      */
onKeyDown(int keyCode, KeyEvent event)337     @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
338         switch (keyCode) {
339             case KeyEvent.KEYCODE_BACK:
340                 // The InputMethodService already takes care of the back
341                 // key for us, to dismiss the input method if it is shown.
342                 // However, our keyboard could be showing a pop-up window
343                 // that back should dismiss, so we first allow it to do that.
344                 if (event.getRepeatCount() == 0 && mInputView != null) {
345                     if (mInputView.handleBack()) {
346                         return true;
347                     }
348                 }
349                 break;
350 
351             case KeyEvent.KEYCODE_DEL:
352                 // Special handling of the delete key: if we currently are
353                 // composing text for the user, we want to modify that instead
354                 // of let the application to the delete itself.
355                 if (mComposing.length() > 0) {
356                     onKey(Keyboard.KEYCODE_DELETE, null);
357                     return true;
358                 }
359                 break;
360 
361             case KeyEvent.KEYCODE_ENTER:
362                 // Let the underlying text editor always handle these.
363                 return false;
364 
365             default:
366                 // For all other keys, if we want to do transformations on
367                 // text being entered with a hard keyboard, we need to process
368                 // it and do the appropriate action.
369                 if (PROCESS_HARD_KEYS) {
370                     if (keyCode == KeyEvent.KEYCODE_SPACE
371                             && (event.getMetaState()&KeyEvent.META_ALT_ON) != 0) {
372                         // A silly example: in our input method, Alt+Space
373                         // is a shortcut for 'android' in lower case.
374                         InputConnection ic = getCurrentInputConnection();
375                         if (ic != null) {
376                             // First, tell the editor that it is no longer in the
377                             // shift state, since we are consuming this.
378                             ic.clearMetaKeyStates(KeyEvent.META_ALT_ON);
379                             keyDownUp(KeyEvent.KEYCODE_A);
380                             keyDownUp(KeyEvent.KEYCODE_N);
381                             keyDownUp(KeyEvent.KEYCODE_D);
382                             keyDownUp(KeyEvent.KEYCODE_R);
383                             keyDownUp(KeyEvent.KEYCODE_O);
384                             keyDownUp(KeyEvent.KEYCODE_I);
385                             keyDownUp(KeyEvent.KEYCODE_D);
386                             // And we consume this event.
387                             return true;
388                         }
389                     }
390                     if (mPredictionOn && translateKeyDown(keyCode, event)) {
391                         return true;
392                     }
393                 }
394         }
395 
396         return super.onKeyDown(keyCode, event);
397     }
398 
399     /**
400      * Use this to monitor key events being delivered to the application.
401      * We get first crack at them, and can either resume them or let them
402      * continue to the app.
403      */
onKeyUp(int keyCode, KeyEvent event)404     @Override public boolean onKeyUp(int keyCode, KeyEvent event) {
405         // If we want to do transformations on text being entered with a hard
406         // keyboard, we need to process the up events to update the meta key
407         // state we are tracking.
408         if (PROCESS_HARD_KEYS) {
409             if (mPredictionOn) {
410                 mMetaState = MetaKeyKeyListener.handleKeyUp(mMetaState,
411                         keyCode, event);
412             }
413         }
414 
415         return super.onKeyUp(keyCode, event);
416     }
417 
418     /**
419      * Helper function to commit any text being composed in to the editor.
420      */
commitTyped(InputConnection inputConnection)421     private void commitTyped(InputConnection inputConnection) {
422         if (mComposing.length() > 0) {
423             inputConnection.commitText(mComposing, mComposing.length());
424             mComposing.setLength(0);
425             updateCandidates();
426         }
427     }
428 
429     /**
430      * Helper to update the shift state of our keyboard based on the initial
431      * editor state.
432      */
updateShiftKeyState(EditorInfo attr)433     private void updateShiftKeyState(EditorInfo attr) {
434         if (attr != null
435                 && mInputView != null && mQwertyKeyboard == mInputView.getKeyboard()) {
436             int caps = 0;
437             EditorInfo ei = getCurrentInputEditorInfo();
438             if (ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
439                 caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType);
440             }
441             mInputView.setShifted(mCapsLock || caps != 0);
442         }
443     }
444 
445     /**
446      * Helper to determine if a given character code is alphabetic.
447      */
isAlphabet(int code)448     private boolean isAlphabet(int code) {
449         if (Character.isLetter(code)) {
450             return true;
451         } else {
452             return false;
453         }
454     }
455 
456     /**
457      * Helper to send a key down / key up pair to the current editor.
458      */
keyDownUp(int keyEventCode)459     private void keyDownUp(int keyEventCode) {
460         getCurrentInputConnection().sendKeyEvent(
461                 new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
462         getCurrentInputConnection().sendKeyEvent(
463                 new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
464     }
465 
466     /**
467      * Helper to send a character to the editor as raw key events.
468      */
sendKey(int keyCode)469     private void sendKey(int keyCode) {
470         switch (keyCode) {
471             case '\n':
472                 keyDownUp(KeyEvent.KEYCODE_ENTER);
473                 break;
474             default:
475                 if (keyCode >= '0' && keyCode <= '9') {
476                     keyDownUp(keyCode - '0' + KeyEvent.KEYCODE_0);
477                 } else {
478                     getCurrentInputConnection().commitText(String.valueOf((char) keyCode), 1);
479                 }
480                 break;
481         }
482     }
483 
484     // Implementation of KeyboardViewListener
485 
onKey(int primaryCode, int[] keyCodes)486     public void onKey(int primaryCode, int[] keyCodes) {
487         if (isWordSeparator(primaryCode)) {
488             // Handle separator
489             if (mComposing.length() > 0) {
490                 commitTyped(getCurrentInputConnection());
491             }
492             sendKey(primaryCode);
493             updateShiftKeyState(getCurrentInputEditorInfo());
494         } else if (primaryCode == Keyboard.KEYCODE_DELETE) {
495             handleBackspace();
496         } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
497             handleShift();
498         } else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
499             handleClose();
500             return;
501         } else if (primaryCode == LatinKeyboardView.KEYCODE_OPTIONS) {
502             // Show a menu or somethin'
503         } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE
504                 && mInputView != null) {
505             Keyboard current = mInputView.getKeyboard();
506             if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) {
507                 current = mQwertyKeyboard;
508             } else {
509                 current = mSymbolsKeyboard;
510             }
511             mInputView.setKeyboard(current);
512             if (current == mSymbolsKeyboard) {
513                 current.setShifted(false);
514             }
515         } else {
516             handleCharacter(primaryCode, keyCodes);
517         }
518     }
519 
onText(CharSequence text)520     public void onText(CharSequence text) {
521         InputConnection ic = getCurrentInputConnection();
522         if (ic == null) return;
523         ic.beginBatchEdit();
524         if (mComposing.length() > 0) {
525             commitTyped(ic);
526         }
527         ic.commitText(text, 0);
528         ic.endBatchEdit();
529         updateShiftKeyState(getCurrentInputEditorInfo());
530     }
531 
532     /**
533      * Update the list of available candidates from the current composing
534      * text.  This will need to be filled in by however you are determining
535      * candidates.
536      */
updateCandidates()537     private void updateCandidates() {
538         if (!mCompletionOn) {
539             if (mComposing.length() > 0) {
540                 ArrayList<String> list = new ArrayList<String>();
541                 list.add(mComposing.toString());
542                 setSuggestions(list, true, true);
543             } else {
544                 setSuggestions(null, false, false);
545             }
546         }
547     }
548 
setSuggestions(List<String> suggestions, boolean completions, boolean typedWordValid)549     public void setSuggestions(List<String> suggestions, boolean completions,
550             boolean typedWordValid) {
551         if (suggestions != null && suggestions.size() > 0) {
552             setCandidatesViewShown(true);
553         } else if (isExtractViewShown()) {
554             setCandidatesViewShown(true);
555         }
556         if (mCandidateView != null) {
557             mCandidateView.setSuggestions(suggestions, completions, typedWordValid);
558         }
559     }
560 
handleBackspace()561     private void handleBackspace() {
562         final int length = mComposing.length();
563         if (length > 1) {
564             mComposing.delete(length - 1, length);
565             getCurrentInputConnection().setComposingText(mComposing, 1);
566             updateCandidates();
567         } else if (length > 0) {
568             mComposing.setLength(0);
569             getCurrentInputConnection().commitText("", 0);
570             updateCandidates();
571         } else {
572             keyDownUp(KeyEvent.KEYCODE_DEL);
573         }
574         updateShiftKeyState(getCurrentInputEditorInfo());
575     }
576 
handleShift()577     private void handleShift() {
578         if (mInputView == null) {
579             return;
580         }
581 
582         Keyboard currentKeyboard = mInputView.getKeyboard();
583         if (mQwertyKeyboard == currentKeyboard) {
584             // Alphabet keyboard
585             checkToggleCapsLock();
586             mInputView.setShifted(mCapsLock || !mInputView.isShifted());
587         } else if (currentKeyboard == mSymbolsKeyboard) {
588             mSymbolsKeyboard.setShifted(true);
589             mInputView.setKeyboard(mSymbolsShiftedKeyboard);
590             mSymbolsShiftedKeyboard.setShifted(true);
591         } else if (currentKeyboard == mSymbolsShiftedKeyboard) {
592             mSymbolsShiftedKeyboard.setShifted(false);
593             mInputView.setKeyboard(mSymbolsKeyboard);
594             mSymbolsKeyboard.setShifted(false);
595         }
596     }
597 
handleCharacter(int primaryCode, int[] keyCodes)598     private void handleCharacter(int primaryCode, int[] keyCodes) {
599         if (isInputViewShown()) {
600             if (mInputView.isShifted()) {
601                 primaryCode = Character.toUpperCase(primaryCode);
602             }
603         }
604         if (isAlphabet(primaryCode) && mPredictionOn) {
605             mComposing.append((char) primaryCode);
606             getCurrentInputConnection().setComposingText(mComposing, 1);
607             updateShiftKeyState(getCurrentInputEditorInfo());
608             updateCandidates();
609         } else {
610             getCurrentInputConnection().commitText(
611                     String.valueOf((char) primaryCode), 1);
612         }
613     }
614 
handleClose()615     private void handleClose() {
616         commitTyped(getCurrentInputConnection());
617         requestHideSelf(0);
618         mInputView.closing();
619     }
620 
checkToggleCapsLock()621     private void checkToggleCapsLock() {
622         long now = System.currentTimeMillis();
623         if (mLastShiftTime + 800 > now) {
624             mCapsLock = !mCapsLock;
625             mLastShiftTime = 0;
626         } else {
627             mLastShiftTime = now;
628         }
629     }
630 
getWordSeparators()631     private String getWordSeparators() {
632         return mWordSeparators;
633     }
634 
isWordSeparator(int code)635     public boolean isWordSeparator(int code) {
636         String separators = getWordSeparators();
637         return separators.contains(String.valueOf((char)code));
638     }
639 
pickDefaultCandidate()640     public void pickDefaultCandidate() {
641         pickSuggestionManually(0);
642     }
643 
pickSuggestionManually(int index)644     public void pickSuggestionManually(int index) {
645         if (mCompletionOn && mCompletions != null && index >= 0
646                 && index < mCompletions.length) {
647             CompletionInfo ci = mCompletions[index];
648             getCurrentInputConnection().commitCompletion(ci);
649             if (mCandidateView != null) {
650                 mCandidateView.clear();
651             }
652             updateShiftKeyState(getCurrentInputEditorInfo());
653         } else if (mComposing.length() > 0) {
654             // If we were generating candidate suggestions for the current
655             // text, we would commit one of them here.  But for this sample,
656             // we will just commit the current text.
657             commitTyped(getCurrentInputConnection());
658         }
659     }
660 
swipeRight()661     public void swipeRight() {
662         if (mCompletionOn) {
663             pickDefaultCandidate();
664         }
665     }
666 
swipeLeft()667     public void swipeLeft() {
668         handleBackspace();
669     }
670 
swipeDown()671     public void swipeDown() {
672         handleClose();
673     }
674 
swipeUp()675     public void swipeUp() {
676     }
677 
onPress(int primaryCode)678     public void onPress(int primaryCode) {
679     }
680 
onRelease(int primaryCode)681     public void onRelease(int primaryCode) {
682     }
683 }
684