Home | History | Annotate | Download | only in openwnn
      1 /*
      2  * Copyright (C) 2008-2012  OMRON SOFTWARE Co., Ltd.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package jp.co.omronsoft.openwnn;
     18 
     19 import jp.co.omronsoft.openwnn.EN.*;
     20 import android.content.SharedPreferences;
     21 import android.content.Context;
     22 import android.content.res.Configuration;
     23 import android.os.Message;
     24 import android.os.Handler;
     25 import android.preference.PreferenceManager;
     26 import android.text.SpannableStringBuilder;
     27 import android.text.Spanned;
     28 import android.text.method.MetaKeyKeyListener;
     29 import android.text.style.BackgroundColorSpan;
     30 import android.text.style.CharacterStyle;
     31 import android.text.style.ForegroundColorSpan;
     32 import android.text.style.UnderlineSpan;
     33 import android.util.Log;
     34 import android.view.KeyCharacterMap;
     35 import android.view.KeyEvent;
     36 import android.view.MotionEvent;
     37 import android.view.View;
     38 import android.view.inputmethod.EditorInfo;
     39 
     40 /**
     41  * The OpenWnn English IME class.
     42  *
     43  * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
     44  */
     45 public class OpenWnnEN extends OpenWnn {
     46     /** A space character */
     47     private static final char[] SPACE = {' '};
     48 
     49     /** Character style of underline */
     50     private static final CharacterStyle SPAN_UNDERLINE   = new UnderlineSpan();
     51     /** Highlight color style for the selected string  */
     52     private static final CharacterStyle SPAN_EXACT_BGCOLOR_HL     = new BackgroundColorSpan(0xFF66CDAA);
     53     /** Highlight color style for the composing text */
     54     private static final CharacterStyle SPAN_REMAIN_BGCOLOR_HL    = new BackgroundColorSpan(0xFFF0FFFF);
     55     /** Highlight text color */
     56     private static final CharacterStyle SPAN_TEXTCOLOR  = new ForegroundColorSpan(0xFF000000);
     57 
     58     /** A private area code(ALT+SHIFT+X) to be ignore (G1 specific). */
     59     private static final int PRIVATE_AREA_CODE = 61184;
     60     /** Never move cursor in to the composing text (adapting to IMF's specification change) */
     61     private static final boolean FIX_CURSOR_TEXT_END = true;
     62 
     63     /** Spannable string for the composing text */
     64     protected SpannableStringBuilder mDisplayText;
     65 
     66     /** Characters treated as a separator */
     67     private String mWordSeparators;
     68     /** Previous event's code */
     69     private int mPreviousEventCode;
     70 
     71     /** Array of words from the user dictionary */
     72     private WnnWord[] mUserDictionaryWords = null;
     73 
     74     /** The converter for English prediction/spell correction */
     75     private OpenWnnEngineEN mConverterEN;
     76     /** The symbol list generator */
     77     private SymbolList mSymbolList;
     78     /** Whether it is displaying symbol list */
     79     private boolean mSymbolMode;
     80     /** Whether prediction is enabled */
     81     private boolean mOptPrediction;
     82     /** Whether spell correction is enabled */
     83     private boolean mOptSpellCorrection;
     84     /** Whether learning is enabled */
     85     private boolean mOptLearning;
     86 
     87     /** SHIFT key state */
     88     private int mHardShift;
     89     /** SHIFT key state (pressing) */
     90     private boolean mShiftPressing;
     91     /** ALT key state */
     92     private int mHardAlt;
     93     /** ALT key state (pressing) */
     94     private boolean mAltPressing;
     95 
     96     /** Instance of this service */
     97     private static OpenWnnEN mSelf = null;
     98 
     99     /** Shift lock toggle definition */
    100     private static final int[] mShiftKeyToggle = {0, MetaKeyKeyListener.META_SHIFT_ON, MetaKeyKeyListener.META_CAP_LOCKED};
    101     /** ALT lock toggle definition */
    102     private static final int[] mAltKeyToggle = {0, MetaKeyKeyListener.META_ALT_ON, MetaKeyKeyListener.META_ALT_LOCKED};
    103     /** Auto caps mode */
    104     private boolean mAutoCaps = false;
    105 
    106     /** Whether dismissing the keyboard when the enter key is pressed */
    107     private boolean mEnableAutoHideKeyboard = true;
    108 
    109     /** Tutorial */
    110     private TutorialEN mTutorial;
    111 
    112     /** Whether tutorial mode or not */
    113     private boolean mEnableTutorial;
    114 
    115     /** Message for {@code mHandler} (execute prediction) */
    116     private static final int MSG_PREDICTION = 0;
    117 
    118     /** Message for {@code mHandler} (execute tutorial) */
    119     private static final int MSG_START_TUTORIAL = 1;
    120 
    121     /** Message for {@code mHandler} (close) */
    122     private static final int MSG_CLOSE = 2;
    123 
    124     /** Delay time(msec.) to start prediction after key input when the candidates view is not shown. */
    125     private static final int PREDICTION_DELAY_MS_1ST = 200;
    126 
    127     /** Delay time(msec.) to start prediction after key input when the candidates view is shown. */
    128     private static final int PREDICTION_DELAY_MS_SHOWING_CANDIDATE = 200;
    129 
    130     /** {@code Handler} for drawing candidates/displaying tutorial */
    131     Handler mHandler = new Handler() {
    132             @Override public void handleMessage(Message msg) {
    133                 switch (msg.what) {
    134                 case MSG_PREDICTION:
    135                     updatePrediction();
    136                     break;
    137                 case MSG_START_TUTORIAL:
    138                     if (mTutorial == null) {
    139                         if (isInputViewShown()) {
    140                             DefaultSoftKeyboardEN inputManager = ((DefaultSoftKeyboardEN) mInputViewManager);
    141                             View v = inputManager.getKeyboardView();
    142                             mTutorial = new TutorialEN(OpenWnnEN.this, v, inputManager);
    143 
    144                             mTutorial.start();
    145                         } else {
    146                             /* Try again soon if the view is not yet showing */
    147                             sendMessageDelayed(obtainMessage(MSG_START_TUTORIAL), 100);
    148                         }
    149                     }
    150                     break;
    151                 case MSG_CLOSE:
    152                     if (mConverterEN != null) mConverterEN.close();
    153                     if (mSymbolList != null) mSymbolList.close();
    154                     break;
    155                 }
    156             }
    157         };
    158 
    159     /**
    160      * Constructor
    161      */
    162     public OpenWnnEN() {
    163         super();
    164         mSelf = this;
    165 
    166         /* used by OpenWnn */
    167         mComposingText = new ComposingText();
    168         mCandidatesViewManager = new TextCandidatesViewManager(-1);
    169         mInputViewManager = new DefaultSoftKeyboardEN();
    170 
    171         if (OpenWnn.getCurrentIme() != null) {
    172             if (mConverterEN == null) {
    173                 mConverterEN = new OpenWnnEngineEN("/data/data/jp.co.omronsoft.openwnn/writableEN.dic");
    174             }
    175         }
    176 
    177         mConverter = mConverterEN;
    178         mSymbolList = null;
    179 
    180         /* etc */
    181         mDisplayText = new SpannableStringBuilder();
    182         mAutoHideMode = false;
    183         mSymbolMode = false;
    184         mOptPrediction = true;
    185         mOptSpellCorrection = true;
    186         mOptLearning = true;
    187     }
    188 
    189     /**
    190      * Constructor
    191      *
    192      * @param context       The context
    193      */
    194     public OpenWnnEN(Context context) {
    195         this();
    196         attachBaseContext(context);
    197     }
    198     /**
    199      * Get the instance of this service.
    200      * <br>
    201      * Before using this method, the constructor of this service must be invoked.
    202      *
    203      * @return      The instance of this object
    204      */
    205     public static OpenWnnEN getInstance() {
    206         return mSelf;
    207     }
    208 
    209     /**
    210      * Insert a character into the composing text.
    211      *
    212      * @param chars     A array of character
    213      */
    214     private void insertCharToComposingText(char[] chars) {
    215         StrSegment seg = new StrSegment(chars);
    216 
    217         if (chars[0] == SPACE[0] || chars[0] == '\u0009') {
    218             /* if the character is a space, commit the composing text */
    219             commitText(1);
    220             commitText(seg.string);
    221             mComposingText.clear();
    222         } else if (mWordSeparators.contains(seg.string)) {
    223             /* if the character is a separator, remove an auto-inserted space and commit the composing text. */
    224             if (mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) {
    225                 mInputConnection.deleteSurroundingText(1, 0);
    226             }
    227             commitText(1);
    228             commitText(seg.string);
    229             mComposingText.clear();
    230         } else {
    231             mComposingText.insertStrSegment(0, 1, seg);
    232             updateComposingText(1);
    233         }
    234     }
    235 
    236     /**
    237      * Insert a character into the composing text.
    238      *
    239      * @param charCode      A character code
    240      * @return              {@code true} if success; {@code false} if an error occurs.
    241      */
    242     private boolean insertCharToComposingText(int charCode) {
    243         if (charCode == 0) {
    244             return false;
    245         }
    246         insertCharToComposingText(Character.toChars(charCode));
    247         return true;
    248     }
    249 
    250     /**
    251      * Get the shift key state from the editor.
    252      *
    253      * @param editor    Editor
    254      *
    255      * @return          State ID of the shift key (0:off, 1:on)
    256      */
    257     protected int getShiftKeyState(EditorInfo editor) {
    258         return (getCurrentInputConnection().getCursorCapsMode(editor.inputType) == 0) ? 0 : 1;
    259     }
    260 
    261     /**
    262      * Set the mode of the symbol list.
    263      *
    264      * @param mode      {@code SymbolList.SYMBOL_ENGLISH} or {@code null}.
    265      */
    266     private void setSymbolMode(String mode) {
    267         if (mode != null) {
    268             mHandler.removeMessages(MSG_PREDICTION);
    269             mSymbolMode = true;
    270             mSymbolList.setDictionary(mode);
    271             mConverter = mSymbolList;
    272         } else {
    273             if (!mSymbolMode) {
    274                 return;
    275             }
    276             mHandler.removeMessages(MSG_PREDICTION);
    277             mSymbolMode = false;
    278             mConverter = mConverterEN;
    279         }
    280     }
    281 
    282     /***********************************************************************
    283      * InputMethodServer
    284      ***********************************************************************/
    285     /** @see jp.co.omronsoft.openwnn.OpenWnn#onCreate */
    286     @Override public void onCreate() {
    287         super.onCreate();
    288         mWordSeparators = getResources().getString(R.string.en_word_separators);
    289 
    290         if (mConverterEN == null) {
    291             mConverterEN = new OpenWnnEngineEN("/data/data/jp.co.omronsoft.openwnn/writableEN.dic");
    292         }
    293 
    294         if (mSymbolList == null) {
    295             mSymbolList = new SymbolList(this, SymbolList.LANG_EN);
    296         }
    297     }
    298 
    299     /** @see jp.co.omronsoft.openwnn.OpenWnn#onCreateInputView */
    300     @Override public View onCreateInputView() {
    301         int hiddenState = getResources().getConfiguration().hardKeyboardHidden;
    302         boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
    303         ((DefaultSoftKeyboardEN) mInputViewManager).setHardKeyboardHidden(hidden);
    304         mEnableTutorial = hidden;
    305 
    306         return super.onCreateInputView();
    307     }
    308 
    309     /** @see jp.co.omronsoft.openwnn.OpenWnn#onStartInputView */
    310     @Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
    311         super.onStartInputView(attribute, restarting);
    312 
    313         /* initialize views */
    314         mCandidatesViewManager.clearCandidates();
    315         mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
    316 
    317         mHardShift = 0;
    318         mHardAlt   = 0;
    319         updateMetaKeyStateDisplay();
    320 
    321         /* load preferences */
    322         SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
    323 
    324         /* auto caps mode */
    325         mAutoCaps = pref.getBoolean("auto_caps", true);
    326 
    327         /* set TextCandidatesViewManager's option */
    328         ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(true);
    329 
    330         /* display status icon */
    331         showStatusIcon(R.drawable.immodeic_half_alphabet);
    332 
    333         if (mComposingText != null) {
    334             mComposingText.clear();
    335         }
    336         /* initialize the engine's state */
    337         fitInputType(pref, attribute);
    338 
    339         ((DefaultSoftKeyboard) mInputViewManager).resetCurrentKeyboard();
    340 
    341         if (OpenWnn.isXLarge()) {
    342             mTextCandidatesViewManager.setPreferences(pref);
    343         }
    344     }
    345 
    346     /** @see jp.co.omronsoft.openwnn.OpenWnn#hideWindow */
    347     @Override public void hideWindow() {
    348         ((BaseInputView)((DefaultSoftKeyboard) mInputViewManager).getCurrentView()).closeDialog();
    349         mComposingText.clear();
    350         mInputViewManager.onUpdateState(this);
    351         mHandler.removeMessages(MSG_START_TUTORIAL);
    352         mInputViewManager.closing();
    353         if (mTutorial != null) {
    354             mTutorial.close();
    355             mTutorial = null;
    356         }
    357 
    358         super.hideWindow();
    359     }
    360 
    361     /** @see jp.co.omronsoft.openwnn.OpenWnn#onUpdateSelection */
    362     @Override public void onUpdateSelection(int oldSelStart, int oldSelEnd,
    363             int newSelStart, int newSelEnd, int candidatesStart,
    364             int candidatesEnd) {
    365 
    366         boolean isNotComposing = ((candidatesStart < 0) && (candidatesEnd < 0));
    367         if (isNotComposing) {
    368             mComposingText.clear();
    369             updateComposingText(1);
    370         } else {
    371             if (mComposingText.size(1) != 0) {
    372                 updateComposingText(1);
    373             }
    374         }
    375     }
    376 
    377     /** @see jp.co.omronsoft.openwnn.OpenWnn#onConfigurationChanged */
    378     @Override public void onConfigurationChanged(Configuration newConfig) {
    379         try {
    380             super.onConfigurationChanged(newConfig);
    381             if (mInputConnection != null) {
    382                 updateComposingText(1);
    383             }
    384             /* Hardware keyboard */
    385             int hiddenState = newConfig.hardKeyboardHidden;
    386             boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
    387             mEnableTutorial = hidden;
    388         } catch (Exception ex) {
    389         }
    390     }
    391 
    392     /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateFullscreenMode */
    393     @Override public boolean onEvaluateFullscreenMode() {
    394         return false;
    395     }
    396 
    397     /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateInputViewShown */
    398     @Override public boolean onEvaluateInputViewShown() {
    399         return true;
    400     }
    401 
    402     /***********************************************************************
    403      * OpenWnn
    404      ***********************************************************************/
    405     /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvent */
    406     @Override synchronized public boolean onEvent(OpenWnnEvent ev) {
    407         /* handling events which are valid when InputConnection is not active. */
    408         switch (ev.code) {
    409 
    410         case OpenWnnEvent.KEYUP:
    411             onKeyUpEvent(ev.keyEvent);
    412             return true;
    413 
    414         case OpenWnnEvent.INITIALIZE_LEARNING_DICTIONARY:
    415             return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_LEARN );
    416 
    417         case OpenWnnEvent.INITIALIZE_USER_DICTIONARY:
    418             return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_USER );
    419 
    420         case OpenWnnEvent.LIST_WORDS_IN_USER_DICTIONARY:
    421             mUserDictionaryWords = mConverterEN.getUserDictionaryWords( );
    422             return true;
    423 
    424         case OpenWnnEvent.GET_WORD:
    425             if( mUserDictionaryWords != null ) {
    426                 ev.word = mUserDictionaryWords[ 0 ];
    427                 for( int i = 0 ; i < mUserDictionaryWords.length-1 ; i++ ) {
    428                     mUserDictionaryWords[ i ] = mUserDictionaryWords[ i + 1 ];
    429                 }
    430                 mUserDictionaryWords[ mUserDictionaryWords.length-1 ] = null;
    431                 if( mUserDictionaryWords[ 0 ] == null ) {
    432                     mUserDictionaryWords = null;
    433                 }
    434                 return true;
    435             }
    436             break;
    437 
    438         case OpenWnnEvent.ADD_WORD:
    439             mConverterEN.addWord(ev.word);
    440             return true;
    441 
    442         case OpenWnnEvent.DELETE_WORD:
    443             mConverterEN.deleteWord(ev.word);
    444             return true;
    445 
    446         case OpenWnnEvent.CHANGE_MODE:
    447             return false;
    448 
    449         case OpenWnnEvent.UPDATE_CANDIDATE:
    450             updateComposingText(ComposingText.LAYER1);
    451             return true;
    452 
    453         case OpenWnnEvent.CHANGE_INPUT_VIEW:
    454             setInputView(onCreateInputView());
    455             return true;
    456 
    457         case OpenWnnEvent.CANDIDATE_VIEW_TOUCH:
    458             boolean ret;
    459                 ret = ((TextCandidatesViewManager)mCandidatesViewManager).onTouchSync();
    460             return ret;
    461 
    462         default:
    463             break;
    464         }
    465 
    466         dismissPopupKeyboard();
    467         KeyEvent keyEvent = ev.keyEvent;
    468         int keyCode = 0;
    469         if (keyEvent != null) {
    470             keyCode = keyEvent.getKeyCode();
    471         }
    472         if (mDirectInputMode) {
    473             if (ev.code == OpenWnnEvent.INPUT_SOFT_KEY && mInputConnection != null) {
    474                 mInputConnection.sendKeyEvent(keyEvent);
    475                 mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
    476                                                            keyEvent.getKeyCode()));
    477             }
    478             return false;
    479         }
    480 
    481         if (ev.code == OpenWnnEvent.LIST_CANDIDATES_FULL) {
    482             mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_FULL);
    483             return true;
    484         } else if (ev.code == OpenWnnEvent.LIST_CANDIDATES_NORMAL) {
    485             mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
    486             return true;
    487         }
    488 
    489         boolean ret = false;
    490         switch (ev.code) {
    491         case OpenWnnEvent.INPUT_CHAR:
    492              ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false);
    493             EditorInfo edit = getCurrentInputEditorInfo();
    494             if( edit.inputType == EditorInfo.TYPE_CLASS_PHONE){
    495                 commitText(new String(ev.chars));
    496             }else{
    497                 setSymbolMode(null);
    498                 insertCharToComposingText(ev.chars);
    499                 ret = true;
    500                 mPreviousEventCode = ev.code;
    501             }
    502             break;
    503 
    504         case OpenWnnEvent.INPUT_KEY:
    505             keyCode = ev.keyEvent.getKeyCode();
    506             /* update shift/alt state */
    507             switch (keyCode) {
    508             case KeyEvent.KEYCODE_ALT_LEFT:
    509             case KeyEvent.KEYCODE_ALT_RIGHT:
    510                 if (ev.keyEvent.getRepeatCount() == 0) {
    511                     if (++mHardAlt > 2) { mHardAlt = 0; }
    512                 }
    513                 mAltPressing   = true;
    514                 updateMetaKeyStateDisplay();
    515                 return true;
    516 
    517             case KeyEvent.KEYCODE_SHIFT_LEFT:
    518             case KeyEvent.KEYCODE_SHIFT_RIGHT:
    519                 if (ev.keyEvent.getRepeatCount() == 0) {
    520                     if (++mHardShift > 2) { mHardShift = 0; }
    521                 }
    522                 mShiftPressing = true;
    523                 updateMetaKeyStateDisplay();
    524                 return true;
    525             }
    526             setSymbolMode(null);
    527             updateComposingText(1);
    528             /* handle other key event */
    529             ret = processKeyEvent(ev.keyEvent);
    530             mPreviousEventCode = ev.code;
    531             break;
    532 
    533         case OpenWnnEvent.INPUT_SOFT_KEY:
    534             setSymbolMode(null);
    535             updateComposingText(1);
    536             ret = processKeyEvent(ev.keyEvent);
    537             if (!ret) {
    538             	int code = keyEvent.getKeyCode();
    539             	if (code == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) {
    540                     sendKeyChar('\n');
    541             	} else {
    542                     mInputConnection.sendKeyEvent(keyEvent);
    543                     mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, code));
    544             	}
    545                 ret = true;
    546             }
    547             mPreviousEventCode = ev.code;
    548             break;
    549 
    550         case OpenWnnEvent.SELECT_CANDIDATE:
    551             if (mSymbolMode) {
    552                 commitText(ev.word, false);
    553             } else {
    554                 if (mWordSeparators.contains(ev.word.candidate) &&
    555                     mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) {
    556                     mInputConnection.deleteSurroundingText(1, 0);
    557                 }
    558                 commitText(ev.word, true);
    559             }
    560             mComposingText.clear();
    561             mPreviousEventCode = ev.code;
    562             updateComposingText(1);
    563             break;
    564 
    565         case OpenWnnEvent.LIST_SYMBOLS:
    566             commitText(1);
    567             mComposingText.clear();
    568             setSymbolMode(SymbolList.SYMBOL_ENGLISH);
    569             updateComposingText(1);
    570             break;
    571 
    572         default:
    573             break;
    574         }
    575 
    576         if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
    577         	mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
    578         }
    579 
    580         return ret;
    581     }
    582 
    583     /***********************************************************************
    584      * OpenWnnEN
    585      ***********************************************************************/
    586     /**
    587      * Handling KeyEvent
    588      * <br>
    589      * This method is called from {@link #onEvent()}.
    590      *
    591      * @param ev   A key event
    592      * @return      {@code true} if the event is processed in this method; {@code false} if the event is not processed in this method
    593      */
    594     private boolean processKeyEvent(KeyEvent ev) {
    595 
    596         int key = ev.getKeyCode();
    597         EditorInfo edit = getCurrentInputEditorInfo();
    598         /* keys which produce a glyph */
    599         if (ev.isPrintingKey()) {
    600             /* do nothing if the character is not able to display or the character is dead key */
    601             if ((mHardShift > 0 && mHardAlt > 0) || (ev.isAltPressed() && ev.isShiftPressed())) {
    602                 int charCode = ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON | MetaKeyKeyListener.META_ALT_ON);
    603                 if (charCode == 0 || (charCode & KeyCharacterMap.COMBINING_ACCENT) != 0 || charCode == PRIVATE_AREA_CODE) {
    604                     if(mHardShift == 1){
    605                         mShiftPressing = false;
    606                     }
    607                     if(mHardAlt == 1){
    608                         mAltPressing   = false;
    609                     }
    610                     if(!ev.isAltPressed()){
    611                         if (mHardAlt == 1) {
    612                             mHardAlt = 0;
    613                         }
    614                     }
    615                     if(!ev.isShiftPressed()){
    616                         if (mHardShift == 1) {
    617                             mHardShift = 0;
    618                         }
    619                     }
    620                     if(!ev.isShiftPressed() && !ev.isAltPressed()){
    621                         updateMetaKeyStateDisplay();
    622                     }
    623                     return true;
    624                 }
    625             }
    626 
    627             ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false);
    628 
    629             /* get the key character */
    630             if (mHardShift== 0  && mHardAlt == 0) {
    631                 /* no meta key is locked */
    632                 int shift = (mAutoCaps) ? getShiftKeyState(edit) : 0;
    633                 if (shift != mHardShift && (key >= KeyEvent.KEYCODE_A && key <= KeyEvent.KEYCODE_Z)) {
    634                     /* handling auto caps for a alphabet character */
    635                     insertCharToComposingText(ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON));
    636                 } else {
    637                     insertCharToComposingText(ev.getUnicodeChar());
    638                 }
    639             } else {
    640                 insertCharToComposingText(ev.getUnicodeChar(mShiftKeyToggle[mHardShift]
    641                                                             | mAltKeyToggle[mHardAlt]));
    642                 if(mHardShift == 1){
    643                     mShiftPressing = false;
    644                 }
    645                 if(mHardAlt == 1){
    646                     mAltPressing   = false;
    647                 }
    648                 /* back to 0 (off) if 1 (on/not locked) */
    649                 if(!ev.isAltPressed()){
    650                     if (mHardAlt == 1) {
    651                         mHardAlt = 0;
    652                     }
    653                 }
    654                 if(!ev.isShiftPressed()){
    655                     if (mHardShift == 1) {
    656                         mHardShift = 0;
    657                     }
    658                 }
    659                 if(!ev.isShiftPressed() && !ev.isAltPressed()){
    660                     updateMetaKeyStateDisplay();
    661                 }
    662             }
    663 
    664             if (edit.inputType == EditorInfo.TYPE_CLASS_PHONE) {
    665                 commitText(1);
    666                 mComposingText.clear();
    667                 return true;
    668             }
    669             return true;
    670 
    671         } else if (key == KeyEvent.KEYCODE_SPACE) {
    672             if (ev.isAltPressed()) {
    673                 /* display the symbol list (G1 specific. same as KEYCODE_SYM) */
    674                 commitText(1);
    675                 mComposingText.clear();
    676                 setSymbolMode(SymbolList.SYMBOL_ENGLISH);
    677                 updateComposingText(1);
    678                 mHardAlt = 0;
    679                 updateMetaKeyStateDisplay();
    680             } else {
    681                 insertCharToComposingText(SPACE);
    682             }
    683             return true;
    684         } else if (key == KeyEvent.KEYCODE_SYM) {
    685             /* display the symbol list */
    686             commitText(1);
    687             mComposingText.clear();
    688             setSymbolMode(SymbolList.SYMBOL_ENGLISH);
    689             updateComposingText(1);
    690             mHardAlt = 0;
    691             updateMetaKeyStateDisplay();
    692         }
    693 
    694 
    695         /* Functional key */
    696         if (mComposingText.size(1) > 0) {
    697             switch (key) {
    698             case KeyEvent.KEYCODE_DEL:
    699                 mComposingText.delete(1, false);
    700                 updateComposingText(1);
    701                 return true;
    702 
    703             case KeyEvent.KEYCODE_BACK:
    704                 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
    705                     mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
    706                 } else {
    707                     mComposingText.clear();
    708                     updateComposingText(1);
    709                 }
    710                 return true;
    711 
    712             case KeyEvent.KEYCODE_DPAD_LEFT:
    713                 mComposingText.moveCursor(1, -1);
    714                 updateComposingText(1);
    715                 return true;
    716 
    717             case KeyEvent.KEYCODE_DPAD_RIGHT:
    718                 mComposingText.moveCursor(1, 1);
    719                 updateComposingText(1);
    720                 return true;
    721 
    722             case KeyEvent.KEYCODE_ENTER:
    723             case KeyEvent.KEYCODE_NUMPAD_ENTER:
    724             case KeyEvent.KEYCODE_DPAD_CENTER:
    725                 commitText(1);
    726                 mComposingText.clear();
    727                 if (mEnableAutoHideKeyboard) {
    728                     mInputViewManager.closing();
    729                     requestHideSelf(0);
    730                 }
    731                 return true;
    732 
    733             default:
    734                 return !isThroughKeyCode(key);
    735             }
    736         } else {
    737             /* if there is no composing string. */
    738             if (mCandidatesViewManager.getCurrentView().isShown()) {
    739             	if (key == KeyEvent.KEYCODE_BACK) {
    740             		if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
    741             			mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
    742             		} else {
    743             			mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
    744             		}
    745             		return true;
    746             	}
    747             } else {
    748                 switch (key) {
    749                 case KeyEvent.KEYCODE_DPAD_CENTER:
    750                 case KeyEvent.KEYCODE_ENTER:
    751                 case KeyEvent.KEYCODE_NUMPAD_ENTER:
    752                     if (mEnableAutoHideKeyboard) {
    753                         mInputViewManager.closing();
    754                         requestHideSelf(0);
    755                         return true;
    756                     }
    757                     break;
    758                 case KeyEvent.KEYCODE_BACK:
    759                     /*
    760                      * If 'BACK' key is pressed when the SW-keyboard is shown
    761                      * and the candidates view is not shown, dismiss the SW-keyboard.
    762                      */
    763                     if (isInputViewShown()) {
    764                         mInputViewManager.closing();
    765                         requestHideSelf(0);
    766                         return true;
    767                     }
    768                     break;
    769                 default:
    770                     break;
    771                 }
    772             }
    773         }
    774 
    775         return false;
    776     }
    777 
    778     /**
    779      * Thread for updating the candidates view
    780      */
    781     private void updatePrediction() {
    782         int candidates = 0;
    783         if (mConverter != null) {
    784             /* normal prediction */
    785             candidates = mConverter.predict(mComposingText, 0, -1);
    786         }
    787         /* update the candidates view */
    788         if (candidates > 0) {
    789             mCandidatesViewManager.displayCandidates(mConverter);
    790         } else {
    791             mCandidatesViewManager.clearCandidates();
    792         }
    793     }
    794 
    795     /**
    796      * Update the composing text.
    797      *
    798      * @param layer  {@link mComposingText}'s layer to display
    799      */
    800     private void updateComposingText(int layer) {
    801         /* update the candidates view */
    802         if (!mOptPrediction) {
    803             commitText(1);
    804             mComposingText.clear();
    805             if (mSymbolMode) {
    806                 mHandler.removeMessages(MSG_PREDICTION);
    807                 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0);
    808             }
    809         } else {
    810             if (mComposingText.size(1) != 0) {
    811                 mHandler.removeMessages(MSG_PREDICTION);
    812                 if (mCandidatesViewManager.getCurrentView().isShown()) {
    813                     mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
    814                                                 PREDICTION_DELAY_MS_SHOWING_CANDIDATE);
    815                 } else {
    816                     mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
    817                                                 PREDICTION_DELAY_MS_1ST);
    818                 }
    819             } else {
    820                 mHandler.removeMessages(MSG_PREDICTION);
    821                 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0);
    822             }
    823 
    824             /* notice to the input view */
    825             this.mInputViewManager.onUpdateState(this);
    826 
    827             /* set the text for displaying as the composing text */
    828             SpannableStringBuilder disp = mDisplayText;
    829             disp.clear();
    830             disp.insert(0, mComposingText.toString(layer));
    831 
    832             /* add decoration to the text */
    833             int cursor = mComposingText.getCursor(layer);
    834             if (disp.length() != 0) {
    835                 if (cursor > 0 && cursor < disp.length()) {
    836                     disp.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor,
    837                             Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    838                 }
    839                 if (cursor < disp.length()) {
    840                     mDisplayText.setSpan(SPAN_REMAIN_BGCOLOR_HL, cursor, disp.length(),
    841                             Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    842                     mDisplayText.setSpan(SPAN_TEXTCOLOR, 0, disp.length(),
    843                             Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    844                 }
    845 
    846                 disp.setSpan(SPAN_UNDERLINE, 0, disp.length(),
    847                         Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    848             }
    849 
    850             int displayCursor = cursor;
    851             if (FIX_CURSOR_TEXT_END) {
    852                 displayCursor = (cursor == 0) ?  0 : 1;
    853             }
    854             /* update the composing text on the EditView */
    855             mInputConnection.setComposingText(disp, displayCursor);
    856         }
    857     }
    858 
    859     /**
    860      * Commit the composing text.
    861      *
    862      * @param layer  {@link mComposingText}'s layer to commit.
    863      */
    864     private void commitText(int layer) {
    865         String tmp = mComposingText.toString(layer);
    866 
    867         if (mOptLearning && mConverter != null && tmp.length() > 0) {
    868             WnnWord word = new WnnWord(tmp, tmp);
    869             mConverter.learn(word);
    870         }
    871 
    872         mInputConnection.commitText(tmp, (FIX_CURSOR_TEXT_END ? 1 : tmp.length()));
    873         mCandidatesViewManager.clearCandidates();
    874     }
    875 
    876     /**
    877      * Commit a word
    878      *
    879      * @param word          A word to commit
    880      * @param withSpace     Append a space after the word if {@code true}.
    881      */
    882     private void commitText(WnnWord word, boolean withSpace) {
    883 
    884         if (mOptLearning && mConverter != null) {
    885             mConverter.learn(word);
    886         }
    887 
    888         mInputConnection.commitText(word.candidate, (FIX_CURSOR_TEXT_END ? 1 : word.candidate.length()));
    889 
    890         if (withSpace) {
    891             commitText(" ");
    892         }
    893     }
    894 
    895     /**
    896      * Commit a string
    897      * <br>
    898      * The string is not registered into the learning dictionary.
    899      *
    900      * @param str  A string to commit
    901      */
    902     private void commitText(String str) {
    903         mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length()));
    904         mCandidatesViewManager.clearCandidates();
    905     }
    906 
    907     /**
    908      * Dismiss the pop-up keyboard
    909      */
    910     protected void dismissPopupKeyboard() {
    911         DefaultSoftKeyboardEN kbd = (DefaultSoftKeyboardEN)mInputViewManager;
    912         if (kbd != null) {
    913             kbd.dismissPopupKeyboard();
    914         }
    915     }
    916 
    917     /**
    918      * Display current meta-key state.
    919      */
    920     private void updateMetaKeyStateDisplay() {
    921         int mode = 0;
    922         if(mHardShift == 0 && mHardAlt == 0){
    923             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
    924         }else if(mHardShift == 1 && mHardAlt == 0){
    925             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_OFF;
    926         }else if(mHardShift == 2  && mHardAlt == 0){
    927             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_OFF;
    928         }else if(mHardShift == 0 && mHardAlt == 1){
    929             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_ON;
    930         }else if(mHardShift == 0 && mHardAlt == 2){
    931             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_LOCK;
    932         }else if(mHardShift == 1 && mHardAlt == 1){
    933             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_ON;
    934         }else if(mHardShift == 1 && mHardAlt == 2){
    935             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_LOCK;
    936         }else if(mHardShift == 2 && mHardAlt == 1){
    937             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_ON;
    938         }else if(mHardShift == 2 && mHardAlt == 2){
    939             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK;
    940         }else{
    941             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
    942         }
    943 
    944         ((DefaultSoftKeyboard) mInputViewManager).updateIndicator(mode);
    945     }
    946 
    947     /**
    948      * Handling KeyEvent(KEYUP)
    949      * <br>
    950      * This method is called from {@link #onEvent()}.
    951      *
    952      * @param ev   An up key event
    953      */
    954     private void onKeyUpEvent(KeyEvent ev) {
    955         int key = ev.getKeyCode();
    956         if(!mShiftPressing){
    957             if(key == KeyEvent.KEYCODE_SHIFT_LEFT || key == KeyEvent.KEYCODE_SHIFT_RIGHT){
    958                 mHardShift = 0;
    959                 mShiftPressing = true;
    960                 updateMetaKeyStateDisplay();
    961             }
    962         }
    963         if(!mAltPressing ){
    964             if(key == KeyEvent.KEYCODE_ALT_LEFT || key == KeyEvent.KEYCODE_ALT_RIGHT){
    965                 mHardAlt = 0;
    966                 mAltPressing   = true;
    967                 updateMetaKeyStateDisplay();
    968             }
    969         }
    970     }
    971     /**
    972      * Fits an editor info.
    973      *
    974      * @param preferences  The preference data.
    975      * @param info          The editor info.
    976      */
    977     private void fitInputType(SharedPreferences preference, EditorInfo info) {
    978         if (info.inputType == EditorInfo.TYPE_NULL) {
    979             mDirectInputMode = true;
    980             return;
    981         }
    982 
    983         mEnableAutoHideKeyboard = false;
    984 
    985         /* set prediction & spell correction mode */
    986         mOptPrediction      = preference.getBoolean("opt_en_prediction", true);
    987         mOptSpellCorrection = preference.getBoolean("opt_en_spell_correction", true);
    988         mOptLearning        = preference.getBoolean("opt_en_enable_learning", true);
    989 
    990         /* prediction on/off */
    991         switch (info.inputType & EditorInfo.TYPE_MASK_CLASS) {
    992         case EditorInfo.TYPE_CLASS_NUMBER:
    993         case EditorInfo.TYPE_CLASS_DATETIME:
    994         case EditorInfo.TYPE_CLASS_PHONE:
    995             mOptPrediction = false;
    996             mOptLearning = false;
    997             break;
    998 
    999         case EditorInfo.TYPE_CLASS_TEXT:
   1000             switch (info.inputType & EditorInfo.TYPE_MASK_VARIATION) {
   1001             case EditorInfo.TYPE_TEXT_VARIATION_PASSWORD:
   1002             case EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
   1003                 mOptLearning = false;
   1004                 mOptPrediction = false;
   1005                 break;
   1006 
   1007             case EditorInfo.TYPE_TEXT_VARIATION_PHONETIC:
   1008                 mOptLearning = false;
   1009                 mOptPrediction = false;
   1010                 break;
   1011             default:
   1012                 break;
   1013             }
   1014         }
   1015 
   1016         /* doesn't learn any word if it is not prediction mode */
   1017         if (!mOptPrediction) {
   1018             mOptLearning = false;
   1019         }
   1020 
   1021         /* set engine's mode */
   1022         if (mOptSpellCorrection) {
   1023             mConverterEN.setDictionary(OpenWnnEngineEN.DICT_FOR_CORRECT_MISTYPE);
   1024         } else {
   1025             mConverterEN.setDictionary(OpenWnnEngineEN.DICT_DEFAULT);
   1026         }
   1027         checkTutorial(info.privateImeOptions);
   1028     }
   1029 
   1030     /**
   1031      * Check and start the tutorial if it is the tutorial mode.
   1032      *
   1033      * @param privateImeOptions IME's options
   1034      */
   1035     private void checkTutorial(String privateImeOptions) {
   1036         if (privateImeOptions == null) return;
   1037         if (privateImeOptions.equals("com.google.android.setupwizard:ShowTutorial")) {
   1038             if ((mTutorial == null) && mEnableTutorial) startTutorial();
   1039         } else if (privateImeOptions.equals("com.google.android.setupwizard:HideTutorial")) {
   1040             if (mTutorial != null) {
   1041                 if (mTutorial.close()) {
   1042                     mTutorial = null;
   1043                 }
   1044             }
   1045         }
   1046     }
   1047 
   1048     /**
   1049      * Start the tutorial
   1050      */
   1051     private void startTutorial() {
   1052         DefaultSoftKeyboardEN inputManager = ((DefaultSoftKeyboardEN) mInputViewManager);
   1053         View v = inputManager.getKeyboardView();
   1054         v.setOnTouchListener(new View.OnTouchListener() {
   1055 				public boolean onTouch(View v, MotionEvent event) {
   1056 					return true;
   1057 				}});
   1058         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
   1059     }
   1060 
   1061     /**
   1062      * Close the tutorial
   1063      */
   1064     public void tutorialDone() {
   1065         mTutorial = null;
   1066     }
   1067 
   1068     /** @see OpenWnn#close */
   1069     @Override protected void close() {
   1070         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLOSE), 0);
   1071     }
   1072 }
   1073