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