PageRenderTime 54ms CodeModel.GetById 14ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/CircleIME/src/com/marvin/circleime/SoftKeyboard.java

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