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 
     20 import jp.co.omronsoft.openwnn.EN.OpenWnnEngineEN;
     21 import jp.co.omronsoft.openwnn.JAJP.*;
     22 import android.content.SharedPreferences;
     23 import android.content.Context;
     24 import android.content.res.Configuration;
     25 import android.os.Handler;
     26 import android.os.Message;
     27 import android.preference.PreferenceManager;
     28 import android.text.SpannableStringBuilder;
     29 import android.text.Spanned;
     30 import android.text.style.BackgroundColorSpan;
     31 import android.text.style.CharacterStyle;
     32 import android.text.style.ForegroundColorSpan;
     33 import android.text.style.UnderlineSpan;
     34 import android.util.Log;
     35 import android.view.KeyEvent;
     36 import android.view.inputmethod.EditorInfo;
     37 import android.view.MotionEvent;
     38 import android.view.View;
     39 import android.view.KeyCharacterMap;
     40 import android.text.method.MetaKeyKeyListener;
     41 
     42 import java.util.regex.Pattern;
     43 import java.util.regex.Matcher;
     44 
     45 /**
     46  * The OpenWnn Japanese IME class
     47  *
     48  * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
     49  */
     50 public class OpenWnnJAJP extends OpenWnn {
     51     /**
     52      * Mode of the convert engine (Full-width KATAKANA).
     53      * Use with {@code OpenWnn.CHANGE_MODE} event.
     54      */
     55     public static final int ENGINE_MODE_FULL_KATAKANA = 101;
     56 
     57     /**
     58      * Mode of the convert engine (Half-width KATAKANA).
     59      * Use with {@code OpenWnn.CHANGE_MODE} event.
     60      */
     61     public static final int ENGINE_MODE_HALF_KATAKANA = 102;
     62 
     63     /**
     64      * Mode of the convert engine (EISU-KANA conversion).
     65      * Use with {@code OpenWnn.CHANGE_MODE} event.
     66      */
     67     public static final int ENGINE_MODE_EISU_KANA = 103;
     68 
     69     /**
     70      * Mode of the convert engine (Symbol list).
     71      * Use with {@code OpenWnn.CHANGE_MODE} event.
     72      */
     73     public static final int ENGINE_MODE_SYMBOL = 104;
     74 
     75     /**
     76      * Mode of the convert engine (Keyboard type is QWERTY).
     77      * Use with {@code OpenWnn.CHANGE_MODE} event to change ambiguous searching pattern.
     78      */
     79     public static final int ENGINE_MODE_OPT_TYPE_QWERTY = 105;
     80 
     81     /**
     82      * Mode of the convert engine (Keyboard type is 12-keys).
     83      * Use with {@code OpenWnn.CHANGE_MODE} event to change ambiguous searching pattern.
     84      */
     85     public static final int ENGINE_MODE_OPT_TYPE_12KEY = 106;
     86 
     87     /** Never move cursor in to the composing text (adapting to IMF's specification change) */
     88     private static final boolean FIX_CURSOR_TEXT_END = true;
     89 
     90     /** Highlight color style for the converted clause */
     91     private static final CharacterStyle SPAN_CONVERT_BGCOLOR_HL   = new BackgroundColorSpan(0xFF8888FF);
     92     /** Highlight color style for the selected string  */
     93     private static final CharacterStyle SPAN_EXACT_BGCOLOR_HL     = new BackgroundColorSpan(0xFF66CDAA);
     94     /** Highlight color style for EISU-KANA conversion */
     95     private static final CharacterStyle SPAN_EISUKANA_BGCOLOR_HL  = new BackgroundColorSpan(0xFF9FB6CD);
     96     /** Highlight color style for the composing text */
     97     private static final CharacterStyle SPAN_REMAIN_BGCOLOR_HL    = new BackgroundColorSpan(0xFFF0FFFF);
     98     /** Highlight text color */
     99     private static final CharacterStyle SPAN_TEXTCOLOR  = new ForegroundColorSpan(0xFF000000);
    100     /** Underline style for the composing text */
    101     private static final CharacterStyle SPAN_UNDERLINE            = new UnderlineSpan();
    102 
    103     /** IME's status for {@code mStatus} input/no candidates). */
    104     private static final int STATUS_INIT            = 0x0000;
    105     /** IME's status for {@code mStatus}(input characters). */
    106     private static final int STATUS_INPUT           = 0x0001;
    107     /** IME's status for {@code mStatus}(input functional keys). */
    108     private static final int STATUS_INPUT_EDIT      = 0x0003;
    109     /** IME's status for {@code mStatus}(all candidates are displayed). */
    110     private static final int STATUS_CANDIDATE_FULL  = 0x0010;
    111 
    112     /** Alphabet-last pattern */
    113     private static final Pattern ENGLISH_CHARACTER_LAST = Pattern.compile(".*[a-zA-Z]$");
    114 
    115     /**
    116      *  Private area character code got by {@link KeyEvent#getUnicodeChar()}.
    117      *   (SHIFT+ALT+X G1 specific)
    118      */
    119     private static final int PRIVATE_AREA_CODE = 61184;
    120 
    121     /** Maximum length of input string */
    122     private static final int LIMIT_INPUT_NUMBER = 30;
    123 
    124     /** Bit flag for English auto commit mode (ON) */
    125     private static final int AUTO_COMMIT_ENGLISH_ON      = 0x0000;
    126     /** Bit flag for English auto commit mode (OFF) */
    127     private static final int AUTO_COMMIT_ENGLISH_OFF     = 0x0001;
    128     /** Bit flag for English auto commit mode (symbol list) */
    129     private static final int AUTO_COMMIT_ENGLISH_SYMBOL  = 0x0010;
    130 
    131     /** Message for {@code mHandler} (execute prediction) */
    132     private static final int MSG_PREDICTION = 0;
    133 
    134     /** Message for {@code mHandler} (execute tutorial) */
    135     private static final int MSG_START_TUTORIAL = 1;
    136 
    137     /** Message for {@code mHandler} (close) */
    138     private static final int MSG_CLOSE = 2;
    139 
    140     /** Delay time(msec.) to start prediction after key input when the candidates view is not shown. */
    141     private static final int PREDICTION_DELAY_MS_1ST = 200;
    142 
    143     /** Delay time(msec.) to start prediction after key input when the candidates view is shown. */
    144     private static final int PREDICTION_DELAY_MS_SHOWING_CANDIDATE = 200;
    145 
    146 
    147     /** Convert engine's state */
    148     private class EngineState {
    149         /** Definition for {@code EngineState.*} (invalid) */
    150         public static final int INVALID = -1;
    151 
    152         /** Definition for {@code EngineState.dictionarySet} (Japanese) */
    153         public static final int DICTIONARYSET_JP = 0;
    154 
    155         /** Definition for {@code EngineState.dictionarySet} (English) */
    156         public static final int DICTIONARYSET_EN = 1;
    157 
    158         /** Definition for {@code EngineState.convertType} (prediction/no conversion) */
    159         public static final int CONVERT_TYPE_NONE = 0;
    160 
    161         /** Definition for {@code EngineState.convertType} (consecutive clause conversion) */
    162         public static final int CONVERT_TYPE_RENBUN = 1;
    163 
    164         /** Definition for {@code EngineState.convertType} (EISU-KANA conversion) */
    165         public static final int CONVERT_TYPE_EISU_KANA = 2;
    166 
    167         /** Definition for {@code EngineState.temporaryMode} (change back to the normal dictionary) */
    168         public static final int TEMPORARY_DICTIONARY_MODE_NONE = 0;
    169 
    170         /** Definition for {@code EngineState.temporaryMode} (change to the symbol dictionary) */
    171         public static final int TEMPORARY_DICTIONARY_MODE_SYMBOL = 1;
    172 
    173         /** Definition for {@code EngineState.temporaryMode} (change to the user dictionary) */
    174         public static final int TEMPORARY_DICTIONARY_MODE_USER = 2;
    175 
    176         /** Definition for {@code EngineState.preferenceDictionary} (no preference dictionary) */
    177         public static final int PREFERENCE_DICTIONARY_NONE = 0;
    178 
    179         /** Definition for {@code EngineState.preferenceDictionary} (person's name) */
    180         public static final int PREFERENCE_DICTIONARY_PERSON_NAME = 1;
    181 
    182         /** Definition for {@code EngineState.preferenceDictionary} (place name) */
    183         public static final int PREFERENCE_DICTIONARY_POSTAL_ADDRESS = 2;
    184 
    185         /** Definition for {@code EngineState.preferenceDictionary} (email/URI) */
    186         public static final int PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI = 3;
    187 
    188         /** Definition for {@code EngineState.keyboard} (undefined) */
    189         public static final int KEYBOARD_UNDEF = 0;
    190 
    191         /** Definition for {@code EngineState.keyboard} (QWERTY) */
    192         public static final int KEYBOARD_QWERTY = 1;
    193 
    194         /** Definition for {@code EngineState.keyboard} (12-keys) */
    195         public static final int KEYBOARD_12KEY  = 2;
    196 
    197         /** Set of dictionaries */
    198         public int dictionarySet = INVALID;
    199 
    200         /** Type of conversion */
    201         public int convertType = INVALID;
    202 
    203         /** Temporary mode */
    204         public int temporaryMode = INVALID;
    205 
    206         /** Preference dictionary setting */
    207         public int preferenceDictionary = INVALID;
    208 
    209         /** keyboard */
    210         public int keyboard = INVALID;
    211 
    212         /**
    213          * Returns whether current type of conversion is consecutive clause(RENBUNSETSU) conversion.
    214          *
    215          * @return {@code true} if current type of conversion is consecutive clause conversion.
    216          */
    217         public boolean isRenbun() {
    218             return convertType == CONVERT_TYPE_RENBUN;
    219         }
    220 
    221         /**
    222          * Returns whether current type of conversion is EISU-KANA conversion.
    223          *
    224          * @return {@code true} if current type of conversion is EISU-KANA conversion.
    225          */
    226         public boolean isEisuKana() {
    227             return convertType == CONVERT_TYPE_EISU_KANA;
    228         }
    229 
    230         /**
    231          * Returns whether current type of conversion is no conversion.
    232          *
    233          * @return {@code true} if no conversion is executed currently.
    234          */
    235         public boolean isConvertState() {
    236             return convertType != CONVERT_TYPE_NONE;
    237         }
    238 
    239         /**
    240          * Check whether or not the mode is "symbol list".
    241          *
    242          * @return {@code true} if the mode is "symbol list".
    243          */
    244         public boolean isSymbolList() {
    245             return temporaryMode == TEMPORARY_DICTIONARY_MODE_SYMBOL;
    246         }
    247 
    248         /**
    249          * Check whether or not the current language is English.
    250          *
    251          * @return {@code true} if the current language is English.
    252          */
    253         public boolean isEnglish() {
    254             return dictionarySet == DICTIONARYSET_EN;
    255         }
    256     }
    257 
    258     /** IME's status */
    259     protected int mStatus = STATUS_INIT;
    260 
    261     /** Whether exact match searching or not */
    262     protected boolean mExactMatchMode = false;
    263 
    264     /** Spannable string builder for displaying the composing text */
    265     protected SpannableStringBuilder mDisplayText;
    266 
    267     /** Instance of this service */
    268     private static OpenWnnJAJP mSelf = null;
    269 
    270     /** Backup for switching the converter */
    271     private WnnEngine mConverterBack;
    272 
    273     /** Backup for switching the pre-converter */
    274     private LetterConverter mPreConverterBack;
    275 
    276     /** OpenWnn conversion engine for Japanese */
    277     private OpenWnnEngineJAJP mConverterJAJP;
    278 
    279     /** OpenWnn conversion engine for English */
    280     private OpenWnnEngineEN mConverterEN;
    281 
    282     /** Conversion engine for listing symbols */
    283     private SymbolList mConverterSymbolEngineBack;
    284 
    285     /** Symbol lists to display when the symbol key is pressed */
    286     private static final String[] SYMBOL_LISTS = {
    287         SymbolList.SYMBOL_JAPANESE_FACE, SymbolList.SYMBOL_JAPANESE, SymbolList.SYMBOL_ENGLISH
    288     };
    289 
    290     /** Current symbol list */
    291     private int mCurrentSymbol = 0;
    292 
    293     /** Romaji-to-Kana converter (HIRAGANA) */
    294     private Romkan mPreConverterHiragana;
    295 
    296     /** Romaji-to-Kana converter (full-width KATAKANA) */
    297     private RomkanFullKatakana mPreConverterFullKatakana;
    298 
    299     /** Romaji-to-Kana converter (half-width KATAKANA) */
    300     private RomkanHalfKatakana mPreConverterHalfKatakana;
    301 
    302     /** Conversion Engine's state */
    303     private EngineState mEngineState = new EngineState();
    304 
    305     /** Whether learning function is active of not. */
    306     private boolean mEnableLearning = true;
    307 
    308     /** Whether prediction is active or not. */
    309     private boolean mEnablePrediction = true;
    310 
    311     /** Whether using the converter */
    312     private boolean mEnableConverter = true;
    313 
    314     /** Whether displaying the symbol list */
    315     private boolean mEnableSymbolList = true;
    316 
    317     /** Whether non ASCII code is enabled */
    318     private boolean mEnableSymbolListNonHalf = true;
    319 
    320     /** Enable mistyping spell correction or not */
    321     private boolean mEnableSpellCorrection = true;
    322 
    323     /** Auto commit state (in English mode) */
    324     private int mDisableAutoCommitEnglishMask = AUTO_COMMIT_ENGLISH_ON;
    325 
    326     /** Whether removing a space before a separator or not. (in English mode) */
    327     private boolean mEnableAutoDeleteSpace = false;
    328 
    329     /** Whether auto-spacing is enabled or not. */
    330     private boolean mEnableAutoInsertSpace = true;
    331 
    332     /** Whether dismissing the keyboard when the enter key is pressed */
    333     private boolean mEnableAutoHideKeyboard = true;
    334 
    335     /** Number of committed clauses on consecutive clause conversion */
    336     private int mCommitCount = 0;
    337 
    338     /** Target layer of the {@link ComposingText} */
    339     private int mTargetLayer = 1;
    340 
    341     /** Current orientation of the display */
    342     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
    343 
    344     /** Current normal dictionary set */
    345     private int mPrevDictionarySet = OpenWnnEngineJAJP.DIC_LANG_INIT;
    346 
    347     /** Regular expression pattern for English separators */
    348     private  Pattern mEnglishAutoCommitDelimiter = null;
    349 
    350     /** Cursor position in the composing text */
    351     private int mComposingStartCursor = 0;
    352 
    353     /** Cursor position before committing text */
    354     private int mCommitStartCursor = 0;
    355 
    356     /** Previous committed text */
    357     private StringBuffer mPrevCommitText = null;
    358 
    359     /** Call count of {@code commitText} */
    360     private int mPrevCommitCount = 0;
    361 
    362     /** Shift lock status of the Hardware keyboard */
    363     private int mHardShift;
    364 
    365     /** SHIFT key state (pressing) */
    366     private boolean mShiftPressing;
    367 
    368     /** ALT lock status of the Hardware keyboard */
    369     private int mHardAlt;
    370 
    371     /** ALT key state (pressing) */
    372     private boolean mAltPressing;
    373 
    374     /** Shift lock toggle definition */
    375     private static final int[] mShiftKeyToggle = {0, MetaKeyKeyListener.META_SHIFT_ON, MetaKeyKeyListener.META_CAP_LOCKED};
    376 
    377     /** ALT lock toggle definition */
    378     private static final int[] mAltKeyToggle = {0, MetaKeyKeyListener.META_ALT_ON, MetaKeyKeyListener.META_ALT_LOCKED};
    379 
    380     /** Auto caps mode */
    381     private boolean mAutoCaps = false;
    382 
    383     /** List of words in the user dictionary */
    384     private WnnWord[] mUserDictionaryWords = null;
    385 
    386     /** Tutorial */
    387     private TutorialJAJP mTutorial;
    388 
    389     /** Whether tutorial mode or not */
    390     private boolean mEnableTutorial;
    391 
    392     /** Whether there is a continued predicted candidate */
    393     private boolean mHasContinuedPrediction = false;
    394 
    395     /** Whether text selection has started */
    396     private boolean mHasStartedTextSelection = true;
    397 
    398     /** {@code Handler} for drawing candidates/displaying tutorial */
    399     Handler mHandler = new Handler() {
    400             @Override
    401                 public void handleMessage(Message msg) {
    402                 switch (msg.what) {
    403                 case MSG_PREDICTION:
    404                     updatePrediction();
    405                     break;
    406                 case MSG_START_TUTORIAL:
    407                     if (mTutorial == null) {
    408                         if (isInputViewShown()) {
    409                             DefaultSoftKeyboardJAJP inputManager = ((DefaultSoftKeyboardJAJP) mInputViewManager);
    410                             View v = inputManager.getKeyboardView();
    411                             mTutorial = new TutorialJAJP(OpenWnnJAJP.this, v, inputManager);
    412 
    413                             mTutorial.start();
    414                         } else {
    415                             /* Try again soon if the view is not yet showing */
    416                             sendMessageDelayed(obtainMessage(MSG_START_TUTORIAL), 100);
    417                         }
    418                     }
    419                     break;
    420                 case MSG_CLOSE:
    421                     if (mConverterJAJP != null) mConverterJAJP.close();
    422                     if (mConverterEN != null) mConverterEN.close();
    423                     if (mConverterSymbolEngineBack != null) mConverterSymbolEngineBack.close();
    424                     break;
    425                 }
    426             }
    427         };
    428 
    429     /** The candidate filter */
    430     private CandidateFilter mFilter;
    431 
    432     /**
    433      * Constructor
    434      */
    435     public OpenWnnJAJP() {
    436         super();
    437         mSelf = this;
    438         mComposingText = new ComposingText();
    439         mCandidatesViewManager = new TextCandidatesViewManager(-1);
    440         mInputViewManager  = new DefaultSoftKeyboardJAJP();
    441         mConverter = mConverterJAJP = new OpenWnnEngineJAJP("/data/data/jp.co.omronsoft.openwnn/writableJAJP.dic");
    442         mConverterEN = new OpenWnnEngineEN("/data/data/jp.co.omronsoft.openwnn/writableEN.dic");
    443         mPreConverter = mPreConverterHiragana = new Romkan();
    444         mPreConverterFullKatakana = new RomkanFullKatakana();
    445         mPreConverterHalfKatakana = new RomkanHalfKatakana();
    446         mFilter = new CandidateFilter();
    447 
    448         mDisplayText = new SpannableStringBuilder();
    449         mAutoHideMode = false;
    450 
    451         mPrevCommitText = new StringBuffer();
    452     }
    453 
    454     /**
    455      * Constructor
    456      *
    457      * @param context       The context
    458      */
    459     public OpenWnnJAJP(Context context) {
    460         this();
    461         attachBaseContext(context);
    462     }
    463 
    464     /** @see jp.co.omronsoft.openwnn.OpenWnn#onCreate */
    465     @Override public void onCreate() {
    466         super.onCreate();
    467 
    468         String delimiter = Pattern.quote(getResources().getString(R.string.en_word_separators));
    469         mEnglishAutoCommitDelimiter = Pattern.compile(".*[" + delimiter + "]$");
    470         if (mConverterSymbolEngineBack == null) {
    471             mConverterSymbolEngineBack = new SymbolList(this, SymbolList.LANG_JA);
    472         }
    473     }
    474 
    475     /** @see jp.co.omronsoft.openwnn.OpenWnn#onCreateInputView */
    476     @Override public View onCreateInputView() {
    477         int hiddenState = getResources().getConfiguration().hardKeyboardHidden;
    478         boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
    479         ((DefaultSoftKeyboardJAJP) mInputViewManager).setHardKeyboardHidden(hidden);
    480         mEnableTutorial = hidden;
    481         return super.onCreateInputView();
    482     }
    483 
    484     /** @see jp.co.omronsoft.openwnn.OpenWnn#onStartInputView */
    485     @Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
    486 
    487         if (restarting) {
    488             super.onStartInputView(attribute, restarting);
    489         } else {
    490             EngineState state = new EngineState();
    491             state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
    492             updateEngineState(state);
    493 
    494             mPrevCommitCount = 0;
    495             clearCommitInfo();
    496 
    497             ((DefaultSoftKeyboard) mInputViewManager).resetCurrentKeyboard();
    498 
    499             super.onStartInputView(attribute, restarting);
    500 
    501             mCandidatesViewManager.clearCandidates();
    502             mStatus = STATUS_INIT;
    503             mExactMatchMode = false;
    504 
    505             /* hardware keyboard support */
    506             mHardShift = 0;
    507             mHardAlt   = 0;
    508             updateMetaKeyStateDisplay();
    509         }
    510         /* load preferences */
    511         SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
    512 
    513         /* initialize the engine's state */
    514         fitInputType(pref, attribute);
    515 
    516         ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(true);
    517 
    518         if (isEnableL2Converter()) {
    519             breakSequence();
    520         }
    521     }
    522 
    523     /** @see jp.co.omronsoft.openwnn.OpenWnn#hideWindow */
    524     @Override public void hideWindow() {
    525         mComposingText.clear();
    526         mInputViewManager.onUpdateState(this);
    527         clearCommitInfo();
    528         mHandler.removeMessages(MSG_START_TUTORIAL);
    529         mInputViewManager.closing();
    530         if (mTutorial != null) {
    531             mTutorial.close();
    532             mTutorial = null;
    533         }
    534 
    535         super.hideWindow();
    536     }
    537 
    538     /** @see jp.co.omronsoft.openwnn.OpenWnn#onUpdateSelection */
    539     @Override public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
    540 
    541         mComposingStartCursor = (candidatesStart < 0) ? newSelEnd : candidatesStart;
    542 
    543         boolean prevSelection = mHasStartedTextSelection;
    544         if (newSelStart != newSelEnd) {
    545             clearCommitInfo();
    546             mHasStartedTextSelection = true;
    547         } else {
    548             mHasStartedTextSelection = false;
    549         }
    550 
    551         if (mHasContinuedPrediction) {
    552             mHasContinuedPrediction = false;
    553             if (0 < mPrevCommitCount) {
    554                 mPrevCommitCount--;
    555             }
    556             return;
    557         }
    558 
    559         boolean isNotComposing = ((candidatesStart < 0) && (candidatesEnd < 0));
    560         if ((mComposingText.size(ComposingText.LAYER1) != 0)
    561             && !isNotComposing) {
    562             updateViewStatus(mTargetLayer, false, true);
    563         } else {
    564             if (0 < mPrevCommitCount) {
    565                 mPrevCommitCount--;
    566             } else {
    567                 int commitEnd = mCommitStartCursor + mPrevCommitText.length();
    568                 if ((((newSelEnd < oldSelEnd) || (commitEnd < newSelEnd)) && clearCommitInfo())
    569                     || isNotComposing) {
    570                     if (isEnableL2Converter()) {
    571                         breakSequence();
    572                     }
    573 
    574                     if (mInputConnection != null) {
    575                         if (isNotComposing && (mComposingText.size(ComposingText.LAYER1) != 0)) {
    576                             mInputConnection.finishComposingText();
    577                         }
    578                     }
    579                     if ((prevSelection != mHasStartedTextSelection) || !mHasStartedTextSelection) {
    580                         initializeScreen();
    581                     }
    582                 }
    583             }
    584         }
    585     }
    586 
    587     /** @see jp.co.omronsoft.openwnn.OpenWnn#onConfigurationChanged */
    588     @Override public void onConfigurationChanged(Configuration newConfig) {
    589         try {
    590             super.onConfigurationChanged(newConfig);
    591 
    592             if (mInputConnection != null) {
    593                 if (super.isInputViewShown()) {
    594                     updateViewStatus(mTargetLayer, true, true);
    595                 }
    596 
    597                 /* display orientation */
    598                 if (mOrientation != newConfig.orientation) {
    599                     mOrientation = newConfig.orientation;
    600                     commitConvertingText();
    601                     initializeScreen();
    602                 }
    603 
    604                 /* Hardware keyboard */
    605                 int hiddenState = newConfig.hardKeyboardHidden;
    606                 boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
    607                 ((DefaultSoftKeyboardJAJP) mInputViewManager).setHardKeyboardHidden(hidden);
    608                 mEnableTutorial = hidden;
    609             }
    610         } catch (Exception ex) {
    611             /* do nothing if an error occurs. */
    612         }
    613     }
    614 
    615     /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvent */
    616     @Override synchronized public boolean onEvent(OpenWnnEvent ev) {
    617 
    618         EngineState state;
    619 
    620         /* handling events which are valid when InputConnection is not active. */
    621         switch (ev.code) {
    622 
    623         case OpenWnnEvent.KEYUP:
    624             onKeyUpEvent(ev.keyEvent);
    625             return true;
    626 
    627         case OpenWnnEvent.INITIALIZE_LEARNING_DICTIONARY:
    628             mConverterEN.initializeDictionary(WnnEngine.DICTIONARY_TYPE_LEARN);
    629             mConverterJAJP.initializeDictionary(WnnEngine.DICTIONARY_TYPE_LEARN);
    630             return true;
    631 
    632         case OpenWnnEvent.INITIALIZE_USER_DICTIONARY:
    633             return mConverterJAJP.initializeDictionary( WnnEngine.DICTIONARY_TYPE_USER );
    634 
    635         case OpenWnnEvent.LIST_WORDS_IN_USER_DICTIONARY:
    636             mUserDictionaryWords = mConverterJAJP.getUserDictionaryWords( );
    637             return true;
    638 
    639         case OpenWnnEvent.GET_WORD:
    640             if (mUserDictionaryWords != null) {
    641                 ev.word = mUserDictionaryWords[0];
    642                 for (int i = 0 ; i < mUserDictionaryWords.length - 1 ; i++) {
    643                     mUserDictionaryWords[i] = mUserDictionaryWords[i + 1];
    644                 }
    645                 mUserDictionaryWords[mUserDictionaryWords.length - 1] = null;
    646                 if (mUserDictionaryWords[0] == null) {
    647                     mUserDictionaryWords = null;
    648                 }
    649                 return true;
    650             }
    651             break;
    652 
    653         case OpenWnnEvent.ADD_WORD:
    654             mConverterJAJP.addWord(ev.word);
    655             return true;
    656 
    657         case OpenWnnEvent.DELETE_WORD:
    658             mConverterJAJP.deleteWord(ev.word);
    659             return true;
    660 
    661         case OpenWnnEvent.CHANGE_MODE:
    662             changeEngineMode(ev.mode);
    663             if (!(ev.mode == ENGINE_MODE_SYMBOL || ev.mode == ENGINE_MODE_EISU_KANA)) {
    664                 initializeScreen();
    665             }
    666             return true;
    667 
    668         case OpenWnnEvent.UPDATE_CANDIDATE:
    669             if (mEngineState.isRenbun()) {
    670                 mComposingText.setCursor(ComposingText.LAYER1,
    671                                          mComposingText.toString(ComposingText.LAYER1).length());
    672                 mExactMatchMode = false;
    673                 updateViewStatusForPrediction(true, true);
    674             } else {
    675                 updateViewStatus(mTargetLayer, true, true);
    676             }
    677             return true;
    678 
    679         case OpenWnnEvent.CHANGE_INPUT_VIEW:
    680             setInputView(onCreateInputView());
    681             return true;
    682 
    683         case OpenWnnEvent.CANDIDATE_VIEW_TOUCH:
    684             boolean ret;
    685             ret = ((TextCandidatesViewManager)mCandidatesViewManager).onTouchSync();
    686             return ret;
    687 
    688         case OpenWnnEvent.TOUCH_OTHER_KEY:
    689             mStatus |= STATUS_INPUT_EDIT;
    690             return true;
    691 
    692         default:
    693             break;
    694         }
    695 
    696         KeyEvent keyEvent = ev.keyEvent;
    697         int keyCode = 0;
    698         if (keyEvent != null) {
    699             keyCode = keyEvent.getKeyCode();
    700         }
    701 
    702         if (mDirectInputMode) {
    703             if (mInputConnection != null) {
    704                 switch (ev.code) {
    705                 case OpenWnnEvent.INPUT_SOFT_KEY:
    706                     if (keyCode == KeyEvent.KEYCODE_ENTER) {
    707                         sendKeyChar('\n');
    708                     } else {
    709                         mInputConnection.sendKeyEvent(keyEvent);
    710                         mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
    711                                                                    keyEvent.getKeyCode()));
    712                     }
    713                     break;
    714                 case OpenWnnEvent.INPUT_CHAR:
    715                     sendKeyChar(ev.chars[0]);
    716                     break;
    717                 default:
    718                     break;
    719                 }
    720             }
    721 
    722             /* return if InputConnection is not active */
    723             return false;
    724         }
    725 
    726         if (!((ev.code == OpenWnnEvent.COMMIT_COMPOSING_TEXT)
    727               || ((keyEvent != null)
    728                   && ((keyCode == KeyEvent.KEYCODE_SHIFT_LEFT)
    729                       || (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)
    730                       || (keyCode == KeyEvent.KEYCODE_ALT_LEFT)
    731                       || (keyCode == KeyEvent.KEYCODE_ALT_RIGHT)
    732                       || (keyEvent.isAltPressed() && (keyCode == KeyEvent.KEYCODE_SPACE)))))) {
    733 
    734             clearCommitInfo();
    735         }
    736 
    737         /* change back the dictionary if necessary */
    738         if (!((ev.code == OpenWnnEvent.SELECT_CANDIDATE)
    739               || (ev.code == OpenWnnEvent.LIST_CANDIDATES_NORMAL)
    740               || (ev.code == OpenWnnEvent.LIST_CANDIDATES_FULL)
    741               || ((keyEvent != null)
    742                   && ((keyCode == KeyEvent.KEYCODE_SHIFT_LEFT)
    743                       ||(keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)
    744                       ||(keyCode == KeyEvent.KEYCODE_ALT_LEFT)
    745                       ||(keyCode == KeyEvent.KEYCODE_ALT_RIGHT)
    746                       ||(keyCode == KeyEvent.KEYCODE_BACK && mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL)
    747                       ||(keyEvent.isAltPressed() && (keyCode == KeyEvent.KEYCODE_SPACE)))))) {
    748 
    749             state = new EngineState();
    750             state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
    751             updateEngineState(state);
    752         }
    753 
    754         if (ev.code == OpenWnnEvent.LIST_CANDIDATES_FULL) {
    755             mStatus |= STATUS_CANDIDATE_FULL;
    756             mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_FULL);
    757             return true;
    758         } else if (ev.code == OpenWnnEvent.LIST_CANDIDATES_NORMAL) {
    759             mStatus &= ~STATUS_CANDIDATE_FULL;
    760             mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
    761             return true;
    762         }
    763 
    764         boolean ret = false;
    765         switch (ev.code) {
    766         case OpenWnnEvent.INPUT_CHAR:
    767             if ((mPreConverter == null) && !isEnableL2Converter()) {
    768                 /* direct input (= full-width alphabet/number input) */
    769                 commitText(false);
    770                 commitText(new String(ev.chars));
    771                 mCandidatesViewManager.clearCandidates();
    772             } else if (!isEnableL2Converter()) {
    773                 processSoftKeyboardCodeWithoutConversion(ev.chars);
    774             } else {
    775                 processSoftKeyboardCode(ev.chars);
    776             }
    777             ret = true;
    778             break;
    779 
    780         case OpenWnnEvent.TOGGLE_CHAR:
    781             processSoftKeyboardToggleChar(ev.toggleTable);
    782             ret = true;
    783             break;
    784 
    785         case OpenWnnEvent.TOGGLE_REVERSE_CHAR:
    786             if (((mStatus & ~STATUS_CANDIDATE_FULL) == STATUS_INPUT)
    787                 && !(mEngineState.isConvertState()) && (ev.toggleTable != null)) {
    788 
    789                 int cursor = mComposingText.getCursor(ComposingText.LAYER1);
    790                 if (cursor > 0) {
    791                     String prevChar = mComposingText.getStrSegment(ComposingText.LAYER1, cursor - 1).string;
    792                     String c = searchToggleCharacter(prevChar, ev.toggleTable, true);
    793                     if (c != null) {
    794                         mComposingText.delete(ComposingText.LAYER1, false);
    795                         appendStrSegment(new StrSegment(c));
    796                         updateViewStatusForPrediction(true, true);
    797                         ret = true;
    798                         break;
    799                     }
    800                 }
    801             }
    802             break;
    803 
    804         case OpenWnnEvent.REPLACE_CHAR:
    805             int cursor = mComposingText.getCursor(ComposingText.LAYER1);
    806             if ((cursor > 0)
    807                 && !(mEngineState.isConvertState())) {
    808 
    809                 String search = mComposingText.getStrSegment(ComposingText.LAYER1, cursor - 1).string;
    810                 String c = (String)ev.replaceTable.get(search);
    811                 if (c != null) {
    812                     mComposingText.delete(1, false);
    813                     appendStrSegment(new StrSegment(c));
    814                     updateViewStatusForPrediction(true, true);
    815                     ret = true;
    816                     mStatus = STATUS_INPUT_EDIT;
    817                     break;
    818                 }
    819             }
    820             break;
    821 
    822         case OpenWnnEvent.INPUT_KEY:
    823             /* update shift/alt state */
    824             switch (keyCode) {
    825             case KeyEvent.KEYCODE_DPAD_DOWN:
    826             case KeyEvent.KEYCODE_DPAD_LEFT:
    827             case KeyEvent.KEYCODE_DPAD_RIGHT:
    828             case KeyEvent.KEYCODE_DPAD_UP:
    829                 if (mTutorial != null) {
    830                     return true;
    831                 }
    832                 break;
    833 
    834             case KeyEvent.KEYCODE_ALT_LEFT:
    835             case KeyEvent.KEYCODE_ALT_RIGHT:
    836                 if (keyEvent.getRepeatCount() == 0) {
    837                     if (++mHardAlt > 2) { mHardAlt = 0; }
    838                 }
    839                 mAltPressing   = true;
    840                 updateMetaKeyStateDisplay();
    841                 return true;
    842 
    843             case KeyEvent.KEYCODE_SHIFT_LEFT:
    844             case KeyEvent.KEYCODE_SHIFT_RIGHT:
    845                 if (keyEvent.getRepeatCount() == 0) {
    846                     if (++mHardShift > 2) { mHardShift = 0; }
    847                 }
    848                 mShiftPressing = true;
    849                 updateMetaKeyStateDisplay();
    850                 return true;
    851             }
    852 
    853             /* handle other key event */
    854             ret = processKeyEvent(keyEvent);
    855             break;
    856 
    857         case OpenWnnEvent.INPUT_SOFT_KEY:
    858             ret = processKeyEvent(keyEvent);
    859             if (!ret) {
    860                 int code = keyEvent.getKeyCode();
    861                 if (code == KeyEvent.KEYCODE_ENTER) {
    862                     sendKeyChar('\n');
    863                 } else {
    864                     mInputConnection.sendKeyEvent(keyEvent);
    865                     mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, code));
    866                 }
    867                 ret = true;
    868             }
    869             break;
    870 
    871         case OpenWnnEvent.SELECT_CANDIDATE:
    872             initCommitInfoForWatchCursor();
    873             if (isEnglishPrediction()) {
    874                 mComposingText.clear();
    875             }
    876             mStatus = commitText(ev.word);
    877             if (isEnglishPrediction() && !mEngineState.isSymbolList() && mEnableAutoInsertSpace) {
    878                 commitSpaceJustOne();
    879             }
    880             checkCommitInfo();
    881 
    882             if (mEngineState.isSymbolList()) {
    883                 mEnableAutoDeleteSpace = false;
    884             }
    885             break;
    886 
    887         case OpenWnnEvent.CONVERT:
    888             startConvert(EngineState.CONVERT_TYPE_RENBUN);
    889             break;
    890 
    891         case OpenWnnEvent.COMMIT_COMPOSING_TEXT:
    892             commitAllText();
    893             break;
    894         }
    895 
    896         return ret;
    897     }
    898 
    899     /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateFullscreenMode */
    900     @Override public boolean onEvaluateFullscreenMode() {
    901         /* never use full-screen mode */
    902         return false;
    903     }
    904 
    905     /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateInputViewShown */
    906     @Override public boolean onEvaluateInputViewShown() {
    907         return true;
    908     }
    909 
    910     /**
    911      * Get the instance of this service.
    912      * <br>
    913      * Before using this method, the constructor of this service must be invoked.
    914      *
    915      * @return      The instance of this service
    916      */
    917     public static OpenWnnJAJP getInstance() {
    918         return mSelf;
    919     }
    920 
    921     /**
    922      * Create a {@link StrSegment} from a character code.
    923      * <br>
    924      * @param charCode           A character code
    925      * @return                  {@link StrSegment} created; {@code null} if an error occurs.
    926      */
    927     private StrSegment createStrSegment(int charCode) {
    928         if (charCode == 0) {
    929             return null;
    930         }
    931         return new StrSegment(Character.toChars(charCode));
    932     }
    933 
    934     /**
    935      * Key event handler.
    936      *
    937      * @param ev        A key event
    938      * @return  {@code true} if the event is handled in this method.
    939      */
    940     private boolean processKeyEvent(KeyEvent ev) {
    941         int key = ev.getKeyCode();
    942 
    943         /* keys which produce a glyph */
    944         if (ev.isPrintingKey()) {
    945             /* do nothing if the character is not able to display or the character is dead key */
    946             if ((mHardShift > 0 && mHardAlt > 0) ||
    947                 (ev.isAltPressed() && ev.isShiftPressed())) {
    948                 int charCode = ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON | MetaKeyKeyListener.META_ALT_ON);
    949                 if (charCode == 0 || (charCode & KeyCharacterMap.COMBINING_ACCENT) != 0 || charCode == PRIVATE_AREA_CODE) {
    950                     if(mHardShift == 1){
    951                         mShiftPressing = false;
    952                     }
    953                     if(mHardAlt == 1){
    954                         mAltPressing   = false;
    955                     }
    956                     if(!ev.isAltPressed()){
    957                         if (mHardAlt == 1) {
    958                             mHardAlt = 0;
    959                         }
    960                     }
    961                     if(!ev.isShiftPressed()){
    962                         if (mHardShift == 1) {
    963                             mHardShift = 0;
    964                         }
    965                     }
    966                     if(!ev.isShiftPressed() && !ev.isAltPressed()){
    967                         updateMetaKeyStateDisplay();
    968                     }
    969                     return true;
    970                 }
    971             }
    972 
    973             commitConvertingText();
    974 
    975             EditorInfo edit = getCurrentInputEditorInfo();
    976             StrSegment str;
    977 
    978             /* get the key character */
    979             if (mHardShift== 0 && mHardAlt == 0) {
    980                 /* no meta key is locked */
    981                 int shift = (mAutoCaps)? getShiftKeyState(edit) : 0;
    982                 if (shift != mHardShift && (key >= KeyEvent.KEYCODE_A && key <= KeyEvent.KEYCODE_Z)) {
    983                     /* handling auto caps for a alphabet character */
    984                     str = createStrSegment(ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON));
    985                 } else {
    986                     str = createStrSegment(ev.getUnicodeChar());
    987                 }
    988             } else {
    989                 str = createStrSegment(ev.getUnicodeChar(mShiftKeyToggle[mHardShift]
    990                                                          | mAltKeyToggle[mHardAlt]));
    991                 if(mHardShift == 1){
    992                     mShiftPressing = false;
    993                 }
    994                 if(mHardAlt == 1){
    995                     mAltPressing   = false;
    996                 }
    997                 /* back to 0 (off) if 1 (on/not locked) */
    998                 if (!ev.isAltPressed()) {
    999                     if (mHardAlt == 1) {
   1000                         mHardAlt = 0;
   1001                     }
   1002                 }
   1003                 if (!ev.isShiftPressed()) {
   1004                     if (mHardShift == 1) {
   1005                         mHardShift = 0;
   1006                     }
   1007                 }
   1008                 if (!ev.isShiftPressed() && !ev.isShiftPressed()) {
   1009                     updateMetaKeyStateDisplay();
   1010                 }
   1011             }
   1012 
   1013             if (str == null) {
   1014                 return true;
   1015             }
   1016 
   1017             /* append the character to the composing text if the character is not TAB */
   1018             if (str.string.charAt(0) != '\u0009') {
   1019                 processHardwareKeyboardInputChar(str);
   1020                 return true;
   1021             } else {
   1022                 commitText(true);
   1023                 commitText(str.string);
   1024                 initializeScreen();
   1025                 return true;
   1026             }
   1027 
   1028         } else if (key == KeyEvent.KEYCODE_SPACE) {
   1029             /* H/W space key */
   1030             processHardwareKeyboardSpaceKey(ev);
   1031             return true;
   1032 
   1033         } else if (key == KeyEvent.KEYCODE_SYM) {
   1034             /* display the symbol list */
   1035             initCommitInfoForWatchCursor();
   1036             mStatus = commitText(true);
   1037             checkCommitInfo();
   1038             changeEngineMode(ENGINE_MODE_SYMBOL);
   1039             mHardAlt = 0;
   1040             updateMetaKeyStateDisplay();
   1041             return true;
   1042         }
   1043 
   1044         /* Functional key */
   1045         if (mComposingText.size(ComposingText.LAYER1) > 0) {
   1046             switch (key) {
   1047             case KeyEvent.KEYCODE_DEL:
   1048                 mStatus = STATUS_INPUT_EDIT;
   1049                 if (mEngineState.isConvertState()) {
   1050                     mComposingText.setCursor(ComposingText.LAYER1,
   1051                                              mComposingText.toString(ComposingText.LAYER1).length());
   1052                     mExactMatchMode = false;
   1053                 } else {
   1054                     if ((mComposingText.size(ComposingText.LAYER1) == 1)
   1055                         && mComposingText.getCursor(ComposingText.LAYER1) != 0) {
   1056                         initializeScreen();
   1057                         return true;
   1058                     } else {
   1059                         mComposingText.delete(ComposingText.LAYER1, false);
   1060                     }
   1061                 }
   1062                 updateViewStatusForPrediction(true, true);
   1063                 return true;
   1064 
   1065             case KeyEvent.KEYCODE_BACK:
   1066                 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
   1067                     mStatus &= ~STATUS_CANDIDATE_FULL;
   1068                     mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
   1069                 } else {
   1070                     if (!mEngineState.isConvertState()) {
   1071                         initializeScreen();
   1072                         if (mConverter != null) {
   1073                             mConverter.init();
   1074                         }
   1075                     } else {
   1076                         mCandidatesViewManager.clearCandidates();
   1077                         mStatus = STATUS_INPUT_EDIT;
   1078                         mExactMatchMode = false;
   1079                         mComposingText.setCursor(ComposingText.LAYER1,
   1080                                                  mComposingText.toString(ComposingText.LAYER1).length());
   1081                         updateViewStatusForPrediction(true, true);
   1082                     }
   1083                 }
   1084                 return true;
   1085 
   1086             case KeyEvent.KEYCODE_DPAD_LEFT:
   1087                 if (!isEnableL2Converter()) {
   1088                     commitText(false);
   1089                     return false;
   1090                 } else {
   1091                     processLeftKeyEvent();
   1092                     return true;
   1093                 }
   1094 
   1095             case KeyEvent.KEYCODE_DPAD_RIGHT:
   1096                 if (!isEnableL2Converter()) {
   1097                     if (mEngineState.keyboard == EngineState.KEYBOARD_12KEY) {
   1098                         commitText(false);
   1099                     }
   1100                 } else {
   1101                     processRightKeyEvent();
   1102                 }
   1103                 return true;
   1104 
   1105             case KeyEvent.KEYCODE_DPAD_CENTER:
   1106             case KeyEvent.KEYCODE_ENTER:
   1107                 if (!isEnglishPrediction()) {
   1108                     int cursor = mComposingText.getCursor(ComposingText.LAYER1);
   1109                     if (cursor < 1) {
   1110                         return true;
   1111                     }
   1112                 }
   1113                 initCommitInfoForWatchCursor();
   1114                 mStatus = commitText(true);
   1115                 checkCommitInfo();
   1116 
   1117                 if (isEnglishPrediction()) {
   1118                     initializeScreen();
   1119                 }
   1120 
   1121                 if (mEnableAutoHideKeyboard) {
   1122                     mInputViewManager.closing();
   1123                     requestHideSelf(0);
   1124                 }
   1125                 return true;
   1126 
   1127             case KeyEvent.KEYCODE_CALL:
   1128             case KeyEvent.KEYCODE_VOLUME_DOWN:
   1129             case KeyEvent.KEYCODE_VOLUME_UP:
   1130                 return false;
   1131 
   1132             default:
   1133                 return true;
   1134             }
   1135         } else {
   1136             /* if there is no composing string. */
   1137             if (mCandidatesViewManager.getCurrentView().isShown()) {
   1138                 /* displaying relational prediction candidates */
   1139                 switch (key) {
   1140                 case KeyEvent.KEYCODE_DPAD_LEFT:
   1141                     if (isEnableL2Converter()) {
   1142                         /* initialize the converter */
   1143                         mConverter.init();
   1144                     }
   1145                     mStatus = STATUS_INPUT_EDIT;
   1146                     updateViewStatusForPrediction(true, true);
   1147                     return false;
   1148 
   1149                 case KeyEvent.KEYCODE_DPAD_RIGHT:
   1150                     if (isEnableL2Converter()) {
   1151                         /* initialize the converter */
   1152                         mConverter.init();
   1153                     }
   1154                     mStatus = STATUS_INPUT_EDIT;
   1155                     updateViewStatusForPrediction(true, true);
   1156                     return false;
   1157 
   1158                 default:
   1159                     return processKeyEventNoInputCandidateShown(ev);
   1160                 }
   1161             } else {
   1162                 switch (key) {
   1163                 case KeyEvent.KEYCODE_BACK:
   1164                     /*
   1165                      * If 'BACK' key is pressed when the SW-keyboard is shown
   1166                      * and the candidates view is not shown, dismiss the SW-keyboard.
   1167                      */
   1168                     if (isInputViewShown()) {
   1169                         mInputViewManager.closing();
   1170                         requestHideSelf(0);
   1171                         return true;
   1172                     }
   1173                     break;
   1174                 default:
   1175                     break;
   1176                 }
   1177             }
   1178         }
   1179 
   1180         return false;
   1181     }
   1182 
   1183     /**
   1184      * Handle the space key event from the Hardware keyboard.
   1185      *
   1186      * @param ev  The space key event
   1187      */
   1188     private void processHardwareKeyboardSpaceKey(KeyEvent ev) {
   1189         /* H/W space key */
   1190         if (ev.isShiftPressed()) {
   1191             /* change Japanese <-> English mode */
   1192             mHardAlt = 0;
   1193             mHardShift = 0;
   1194             updateMetaKeyStateDisplay();
   1195             if (mEngineState.isEnglish()) {
   1196                 /* English mode to Japanese mode */
   1197                 ((DefaultSoftKeyboardJAJP) mInputViewManager).changeKeyMode(DefaultSoftKeyboard.KEYMODE_JA_FULL_HIRAGANA);
   1198                 mConverter = mConverterJAJP;
   1199             } else {
   1200                 /* Japanese mode to English mode */
   1201                 ((DefaultSoftKeyboardJAJP) mInputViewManager).changeKeyMode(DefaultSoftKeyboard.KEYMODE_JA_HALF_ALPHABET);
   1202                 mConverter = mConverterEN;
   1203             }
   1204             mCandidatesViewManager.clearCandidates();
   1205 
   1206         } else if(ev.isAltPressed()){
   1207             /* display the symbol list (G1 specific. same as KEYCODE_SYM) */
   1208             if (!mEngineState.isSymbolList()) {
   1209                 commitAllText();
   1210             }
   1211             changeEngineMode(ENGINE_MODE_SYMBOL);
   1212             mHardAlt = 0;
   1213             updateMetaKeyStateDisplay();
   1214 
   1215         } else if (isEnglishPrediction()) {
   1216             /* Auto commit if English mode */
   1217             if (mComposingText.size(0) == 0) {
   1218                 commitText(" ");
   1219                 mCandidatesViewManager.clearCandidates();
   1220                 breakSequence();
   1221             } else {
   1222                 initCommitInfoForWatchCursor();
   1223                 commitText(true);
   1224                 commitSpaceJustOne();
   1225                 checkCommitInfo();
   1226             }
   1227             mEnableAutoDeleteSpace = false;
   1228 
   1229         } else {
   1230             /* start consecutive clause conversion if Japanese mode */
   1231             if (mComposingText.size(0) == 0) {
   1232                 commitText(" ");
   1233                 mCandidatesViewManager.clearCandidates();
   1234                 breakSequence();
   1235             } else {
   1236                 startConvert(EngineState.CONVERT_TYPE_RENBUN);
   1237             }
   1238         }
   1239     }
   1240 
   1241     /**
   1242      * Handle the character code from the hardware keyboard except the space key.
   1243      *
   1244      * @param str  The input character
   1245      */
   1246     private void processHardwareKeyboardInputChar(StrSegment str) {
   1247         if (isEnableL2Converter()) {
   1248             boolean commit = false;
   1249             if (mPreConverter == null) {
   1250                 Matcher m = mEnglishAutoCommitDelimiter.matcher(str.string);
   1251                 if (m.matches()) {
   1252                     commitText(true);
   1253 
   1254                     commit = true;
   1255                 }
   1256                 appendStrSegment(str);
   1257             } else {
   1258                 appendStrSegment(str);
   1259                 mPreConverter.convert(mComposingText);
   1260             }
   1261 
   1262             if (commit) {
   1263                 commitText(true);
   1264             } else {
   1265                 mStatus = STATUS_INPUT;
   1266                 updateViewStatusForPrediction(true, true);
   1267             }
   1268         } else {
   1269             appendStrSegment(str);
   1270             boolean completed = true;
   1271             if (mPreConverter != null) {
   1272                 completed = mPreConverter.convert(mComposingText);
   1273             }
   1274 
   1275             if (completed) {
   1276                 if (!mEngineState.isEnglish()) {
   1277                     commitTextWithoutLastAlphabet();
   1278                 } else {
   1279                     commitText(false);
   1280                 }
   1281             } else {
   1282                 updateViewStatus(ComposingText.LAYER1, false, true);
   1283             }
   1284         }
   1285     }
   1286 
   1287     /** Thread for updating the candidates view */
   1288     private void updatePrediction() {
   1289         int candidates = 0;
   1290         int cursor = mComposingText.getCursor(ComposingText.LAYER1);
   1291         if (isEnableL2Converter() || mEngineState.isSymbolList()) {
   1292             if (mExactMatchMode) {
   1293                 /* exact matching */
   1294                 candidates = mConverter.predict(mComposingText, 0, cursor);
   1295             } else {
   1296                 /* normal prediction */
   1297                 candidates = mConverter.predict(mComposingText, 0, -1);
   1298             }
   1299         }
   1300 
   1301         /* update the candidates view */
   1302         if (candidates > 0) {
   1303             mHasContinuedPrediction = ((mComposingText.size(ComposingText.LAYER1) == 0)
   1304                                        && !mEngineState.isSymbolList());
   1305             mCandidatesViewManager.displayCandidates(mConverter);
   1306         } else {
   1307             mCandidatesViewManager.clearCandidates();
   1308         }
   1309     }
   1310 
   1311     /**
   1312      * Handle a left key event.
   1313      */
   1314     private void processLeftKeyEvent() {
   1315         if (mEngineState.isConvertState()) {
   1316             if (mEngineState.isEisuKana()) {
   1317                 mExactMatchMode = true;
   1318             }
   1319 
   1320             if (1 < mComposingText.getCursor(ComposingText.LAYER1)) {
   1321                 mComposingText.moveCursor(ComposingText.LAYER1, -1);
   1322             }
   1323         } else if (mExactMatchMode) {
   1324             mComposingText.moveCursor(ComposingText.LAYER1, -1);
   1325         } else {
   1326             if (isEnglishPrediction()) {
   1327                 mComposingText.moveCursor(ComposingText.LAYER1, -1);
   1328             } else {
   1329                 mExactMatchMode = true;
   1330             }
   1331         }
   1332 
   1333         mCommitCount = 0; /* retry consecutive clause conversion if necessary. */
   1334         mStatus = STATUS_INPUT_EDIT;
   1335         updateViewStatus(mTargetLayer, true, true);
   1336     }
   1337 
   1338     /**
   1339      * Handle a right key event.
   1340      */
   1341     private void processRightKeyEvent() {
   1342         int layer = mTargetLayer;
   1343         ComposingText composingText = mComposingText;
   1344         if (mExactMatchMode || (mEngineState.isConvertState())) {
   1345             int textSize = composingText.size(ComposingText.LAYER1);
   1346             if (composingText.getCursor(ComposingText.LAYER1) == textSize) {
   1347                 mExactMatchMode = false;
   1348                 layer = ComposingText.LAYER1; /* convert -> prediction */
   1349                 EngineState state = new EngineState();
   1350                 state.convertType = EngineState.CONVERT_TYPE_NONE;
   1351                 updateEngineState(state);
   1352             } else {
   1353                 if (mEngineState.isEisuKana()) {
   1354                     mExactMatchMode = true;
   1355                 }
   1356                 composingText.moveCursor(ComposingText.LAYER1, 1);
   1357             }
   1358         } else {
   1359             if (composingText.getCursor(ComposingText.LAYER1)
   1360                     < composingText.size(ComposingText.LAYER1)) {
   1361                 composingText.moveCursor(ComposingText.LAYER1, 1);
   1362             }
   1363         }
   1364 
   1365         mCommitCount = 0; /* retry consecutive clause conversion if necessary. */
   1366         mStatus = STATUS_INPUT_EDIT;
   1367 
   1368         updateViewStatus(layer, true, true);
   1369     }
   1370 
   1371     /**
   1372      * Handle a key event which is not right or left key when the
   1373      * composing text is empty and some candidates are shown.
   1374      *
   1375      * @param ev        A key event
   1376      * @return          {@code true} if this consumes the event; {@code false} if not.
   1377      */
   1378     boolean processKeyEventNoInputCandidateShown(KeyEvent ev) {
   1379         boolean ret = true;
   1380 
   1381         switch (ev.getKeyCode()) {
   1382         case KeyEvent.KEYCODE_DEL:
   1383             ret = true;
   1384             break;
   1385         case KeyEvent.KEYCODE_ENTER:
   1386         case KeyEvent.KEYCODE_DPAD_UP:
   1387         case KeyEvent.KEYCODE_DPAD_DOWN:
   1388         case KeyEvent.KEYCODE_MENU:
   1389             ret = false;
   1390             break;
   1391 
   1392         case KeyEvent.KEYCODE_CALL:
   1393         case KeyEvent.KEYCODE_VOLUME_DOWN:
   1394         case KeyEvent.KEYCODE_VOLUME_UP:
   1395             return false;
   1396 
   1397         case KeyEvent.KEYCODE_DPAD_CENTER:
   1398             ret = true;
   1399             break;
   1400 
   1401         case KeyEvent.KEYCODE_BACK:
   1402             if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
   1403                 mStatus &= ~STATUS_CANDIDATE_FULL;
   1404                 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
   1405                 return true;
   1406             } else {
   1407                 ret = true;
   1408             }
   1409             break;
   1410 
   1411         default:
   1412             return true;
   1413         }
   1414 
   1415         if (mConverter != null) {
   1416             /* initialize the converter */
   1417             mConverter.init();
   1418         }
   1419         updateViewStatusForPrediction(true, true);
   1420         return ret;
   1421     }
   1422 
   1423     /**
   1424      * Update views and the display of the composing text for predict mode.
   1425      *
   1426      * @param updateCandidates  {@code true} to update the candidates view
   1427      * @param updateEmptyText   {@code false} to update the composing text if it is not empty; {@code true} to update always.
   1428      */
   1429     private void updateViewStatusForPrediction(boolean updateCandidates, boolean updateEmptyText) {
   1430         EngineState state = new EngineState();
   1431         state.convertType = EngineState.CONVERT_TYPE_NONE;
   1432         updateEngineState(state);
   1433 
   1434         updateViewStatus(ComposingText.LAYER1, updateCandidates, updateEmptyText);
   1435     }
   1436 
   1437     /**
   1438      * Update views and the display of the composing text.
   1439      *
   1440      * @param layer                      Display layer of the composing text
   1441      * @param updateCandidates  {@code true} to update the candidates view
   1442      * @param updateEmptyText   {@code false} to update the composing text if it is not empty; {@code true} to update always.
   1443      */
   1444     private void updateViewStatus(int layer, boolean updateCandidates, boolean updateEmptyText) {
   1445         mTargetLayer = layer;
   1446 
   1447         if (updateCandidates) {
   1448             updateCandidateView();
   1449         }
   1450         /* notice to the input view */
   1451         mInputViewManager.onUpdateState(this);
   1452 
   1453         /* set the text for displaying as the composing text */
   1454         mDisplayText.clear();
   1455         mDisplayText.insert(0, mComposingText.toString(layer));
   1456 
   1457         /* add decoration to the text */
   1458         int cursor = mComposingText.getCursor(layer);
   1459         if ((mInputConnection != null) && (mDisplayText.length() != 0 || updateEmptyText)) {
   1460             if (cursor != 0) {
   1461                 int highlightEnd = 0;
   1462 
   1463                 if ((mExactMatchMode && (!mEngineState.isEisuKana()))
   1464                     || (FIX_CURSOR_TEXT_END && isEnglishPrediction()
   1465                         && (cursor < mComposingText.size(ComposingText.LAYER1)))){
   1466 
   1467                     mDisplayText.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor,
   1468                                          Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
   1469                     highlightEnd = cursor;
   1470 
   1471                 } else if (FIX_CURSOR_TEXT_END && mEngineState.isEisuKana()) {
   1472                     mDisplayText.setSpan(SPAN_EISUKANA_BGCOLOR_HL, 0, cursor,
   1473                                          Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
   1474                     highlightEnd = cursor;
   1475 
   1476                 } else if (layer == ComposingText.LAYER2) {
   1477                     highlightEnd = mComposingText.toString(layer, 0, 0).length();
   1478 
   1479                     /* highlights the first segment */
   1480                     mDisplayText.setSpan(SPAN_CONVERT_BGCOLOR_HL, 0,
   1481                                          highlightEnd,
   1482                                          Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
   1483                 }
   1484 
   1485                 if (FIX_CURSOR_TEXT_END && (highlightEnd != 0)) {
   1486                     /* highlights remaining text */
   1487                     mDisplayText.setSpan(SPAN_REMAIN_BGCOLOR_HL, highlightEnd,
   1488                                          mComposingText.toString(layer).length(),
   1489                                          Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
   1490 
   1491                     /* text color in the highlight */
   1492                     mDisplayText.setSpan(SPAN_TEXTCOLOR, 0,
   1493                                          mComposingText.toString(layer).length(),
   1494                                          Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
   1495                 }
   1496             }
   1497 
   1498             mDisplayText.setSpan(SPAN_UNDERLINE, 0, mDisplayText.length(),
   1499                                  Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
   1500 
   1501             int displayCursor = mComposingText.toString(layer, 0, cursor - 1).length();
   1502             if (FIX_CURSOR_TEXT_END) {
   1503                 displayCursor = (cursor == 0) ?  0 : 1;
   1504             }
   1505             /* update the composing text on the EditView */
   1506             if ((mDisplayText.length() != 0) || !mHasStartedTextSelection) {
   1507                 mInputConnection.setComposingText(mDisplayText, displayCursor);
   1508             }
   1509         }
   1510     }
   1511 
   1512     /**
   1513      * Update the candidates view.
   1514      */
   1515     private void updateCandidateView() {
   1516         switch (mTargetLayer) {
   1517         case ComposingText.LAYER0:
   1518         case ComposingText.LAYER1: /* prediction */
   1519             if (mEnablePrediction || mEngineState.isSymbolList() || mEngineState.isEisuKana()) {
   1520                 /* update the candidates view */
   1521                 if ((mComposingText.size(ComposingText.LAYER1) != 0)
   1522                     && !mEngineState.isConvertState()) {
   1523 
   1524                     mHandler.removeMessages(MSG_PREDICTION);
   1525                     if (mCandidatesViewManager.getCurrentView().isShown()) {
   1526                         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
   1527                                                     PREDICTION_DELAY_MS_SHOWING_CANDIDATE);
   1528                     } else {
   1529                         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
   1530                                                     PREDICTION_DELAY_MS_1ST);
   1531                     }
   1532                 } else {
   1533                     mHandler.removeMessages(MSG_PREDICTION);
   1534                     updatePrediction();
   1535                 }
   1536             } else {
   1537                 mHandler.removeMessages(MSG_PREDICTION);
   1538                 mCandidatesViewManager.clearCandidates();
   1539             }
   1540             break;
   1541         case ComposingText.LAYER2: /* convert */
   1542             if (mCommitCount == 0) {
   1543                 mHandler.removeMessages(MSG_PREDICTION);
   1544                 mConverter.convert(mComposingText);
   1545             }
   1546 
   1547             int candidates = mConverter.makeCandidateListOf(mCommitCount);
   1548 
   1549             if (candidates != 0) {
   1550                 mComposingText.setCursor(ComposingText.LAYER2, 1);
   1551                 mCandidatesViewManager.displayCandidates(mConverter);
   1552             } else {
   1553                 mComposingText.setCursor(ComposingText.LAYER1,
   1554                                          mComposingText.toString(ComposingText.LAYER1).length());
   1555                 mCandidatesViewManager.clearCandidates();
   1556             }
   1557             break;
   1558         default:
   1559             break;
   1560         }
   1561     }
   1562 
   1563     /**
   1564      * Commit the displaying composing text.
   1565      *
   1566      * @param learn  {@code true} to register the committed string to the learning dictionary.
   1567      * @return          IME's status after commit
   1568      */
   1569     private int commitText(boolean learn) {
   1570         if (isEnglishPrediction()) {
   1571             mComposingText.setCursor(ComposingText.LAYER1,
   1572                                      mComposingText.size(ComposingText.LAYER1));
   1573         }
   1574 
   1575         int layer = mTargetLayer;
   1576         int cursor = mComposingText.getCursor(layer);
   1577         if (cursor == 0) {
   1578             return mStatus;
   1579         }
   1580         String tmp = mComposingText.toString(layer, 0, cursor - 1);
   1581 
   1582         if (mConverter != null) {
   1583             if (learn) {
   1584                 if (mEngineState.isRenbun()) {
   1585                     learnWord(0); /* select the top of the clauses */
   1586                 } else {
   1587                     if (mComposingText.size(ComposingText.LAYER1) != 0) {
   1588                         String stroke = mComposingText.toString(ComposingText.LAYER1, 0, mComposingText.getCursor(layer) - 1);
   1589                         WnnWord word = new WnnWord(tmp, stroke);
   1590 
   1591                         learnWord(word);
   1592                     }
   1593                 }
   1594             } else {
   1595                 breakSequence();
   1596             }
   1597         }
   1598         return commitTextThroughInputConnection(tmp);
   1599     }
   1600 
   1601     /**
   1602      * Commit the composing text except the alphabet character at the tail.
   1603      */
   1604     private void commitTextWithoutLastAlphabet() {
   1605         int layer = mTargetLayer;
   1606         String tmp = mComposingText.getStrSegment(layer, -1).string;
   1607 
   1608         if (isAlphabetLast(tmp)) {
   1609             mComposingText.moveCursor(ComposingText.LAYER1, -1);
   1610             commitText(false);
   1611             mComposingText.moveCursor(ComposingText.LAYER1, 1);
   1612         } else {
   1613             commitText(false);
   1614         }
   1615     }
   1616 
   1617     /**
   1618      * Commit all uncommitted words.
   1619      */
   1620     private void commitAllText() {
   1621         initCommitInfoForWatchCursor();
   1622         if (mEngineState.isConvertState()) {
   1623             commitConvertingText();
   1624         } else {
   1625             mComposingText.setCursor(ComposingText.LAYER1,
   1626                                      mComposingText.size(ComposingText.LAYER1));
   1627             mStatus = commitText(true);
   1628         }
   1629         checkCommitInfo();
   1630     }
   1631 
   1632     /**
   1633      * Commit a word.
   1634      *
   1635      * @param word              A word to commit
   1636      * @return                  IME's status after commit
   1637      */
   1638     private int commitText(WnnWord word) {
   1639         if (mConverter != null) {
   1640             learnWord(word);
   1641         }
   1642         return commitTextThroughInputConnection(word.candidate);
   1643     }
   1644 
   1645     /**
   1646      * Commit a string.
   1647      *
   1648      * @param str  A string to commit
   1649      */
   1650     private void commitText(String str) {
   1651         mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length()));
   1652         mPrevCommitText.append(str);
   1653         mPrevCommitCount++;
   1654         mEnableAutoDeleteSpace = true;
   1655         updateViewStatusForPrediction(false, false);
   1656     }
   1657 
   1658     /**
   1659      * Commit a string through {@link InputConnection}.
   1660      *
   1661      * @param string  A string to commit
   1662      * @return                  IME's status after commit
   1663      */
   1664     private int commitTextThroughInputConnection(String string) {
   1665         int layer = mTargetLayer;
   1666 
   1667         mInputConnection.commitText(string, (FIX_CURSOR_TEXT_END ? 1 : string.length()));
   1668         mPrevCommitText.append(string);
   1669         mPrevCommitCount++;
   1670 
   1671         int cursor = mComposingText.getCursor(layer);
   1672         if (cursor > 0) {
   1673             mComposingText.deleteStrSegment(layer, 0, mComposingText.getCursor(layer) - 1);
   1674             mComposingText.setCursor(layer, mComposingText.size(layer));
   1675         }
   1676         mExactMatchMode = false;
   1677         mCommitCount++;
   1678 
   1679         if ((layer == ComposingText.LAYER2) && (mComposingText.size(layer) == 0)) {
   1680             layer = 1; /* for connected prediction */
   1681         }
   1682 
   1683         boolean committed = autoCommitEnglish();
   1684         mEnableAutoDeleteSpace = true;
   1685 
   1686         if (layer == ComposingText.LAYER2) {
   1687             EngineState state = new EngineState();
   1688             state.convertType = EngineState.CONVERT_TYPE_RENBUN;
   1689             updateEngineState(state);
   1690             updateViewStatus(layer, !committed, false);
   1691         } else {
   1692             updateViewStatusForPrediction(!committed, false);
   1693         }
   1694 
   1695         if (mComposingText.size(ComposingText.LAYER0) == 0) {
   1696             return STATUS_INIT;
   1697         } else {
   1698             return STATUS_INPUT_EDIT;
   1699         }
   1700     }
   1701 
   1702     /**
   1703      * Returns whether it is English prediction mode or not.
   1704      *
   1705      * @return  {@code true} if it is English prediction mode; otherwise, {@code false}.
   1706      */
   1707     private boolean isEnglishPrediction() {
   1708         return (mEngineState.isEnglish() && isEnableL2Converter());
   1709     }
   1710 
   1711     /**
   1712      * Change the conversion engine and the letter converter(Romaji-to-Kana converter).
   1713      *
   1714      * @param mode  Engine's mode to be changed
   1715      * @see jp.co.omronsoft.openwnn.OpenWnnEvent.Mode
   1716      * @see jp.co.omronsoft.openwnn.JAJP.DefaultSoftKeyboardJAJP
   1717      */
   1718     private void changeEngineMode(int mode) {
   1719         EngineState state = new EngineState();
   1720 
   1721         switch (mode) {
   1722         case ENGINE_MODE_OPT_TYPE_QWERTY:
   1723             state.keyboard = EngineState.KEYBOARD_QWERTY;
   1724             updateEngineState(state);
   1725             clearCommitInfo();
   1726             return;
   1727 
   1728         case ENGINE_MODE_OPT_TYPE_12KEY:
   1729             state.keyboard = EngineState.KEYBOARD_12KEY;
   1730             updateEngineState(state);
   1731             clearCommitInfo();
   1732             return;
   1733 
   1734         case ENGINE_MODE_EISU_KANA:
   1735             if (mEngineState.isEisuKana()) {
   1736                 state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
   1737                 updateEngineState(state);
   1738                 updateViewStatusForPrediction(true, true); /* prediction only */
   1739             } else {
   1740                 startConvert(EngineState.CONVERT_TYPE_EISU_KANA);
   1741             }
   1742             return;
   1743 
   1744         case ENGINE_MODE_SYMBOL:
   1745             if (mEnableSymbolList && !mDirectInputMode) {
   1746                 state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_SYMBOL;
   1747                 updateEngineState(state);
   1748                 updateViewStatusForPrediction(true, true);
   1749             }
   1750             return;
   1751 
   1752         default:
   1753             break;
   1754         }
   1755 
   1756         state = new EngineState();
   1757         state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
   1758         updateEngineState(state);
   1759 
   1760         state = new EngineState();
   1761         switch (mode) {
   1762         case OpenWnnEvent.Mode.DIRECT:
   1763             /* Full/Half-width number or Full-width alphabet */
   1764             mConverter = null;
   1765             mPreConverter = null;
   1766             break;
   1767 
   1768         case OpenWnnEvent.Mode.NO_LV1_CONV:
   1769             /* no Romaji-to-Kana conversion (=English prediction mode) */
   1770             state.dictionarySet = EngineState.DICTIONARYSET_EN;
   1771             updateEngineState(state);
   1772             mConverter = mConverterEN;
   1773             mPreConverter = null;
   1774             break;
   1775 
   1776         case OpenWnnEvent.Mode.NO_LV2_CONV:
   1777             mConverter = null;
   1778             mPreConverter = mPreConverterHiragana;
   1779             break;
   1780 
   1781         case ENGINE_MODE_FULL_KATAKANA:
   1782             mConverter = null;
   1783             mPreConverter = mPreConverterFullKatakana;
   1784             break;
   1785 
   1786         case ENGINE_MODE_HALF_KATAKANA:
   1787             mConverter = null;
   1788             mPreConverter = mPreConverterHalfKatakana;
   1789             break;
   1790 
   1791         default:
   1792             /* HIRAGANA input mode */
   1793             state.dictionarySet = EngineState.DICTIONARYSET_JP;
   1794             updateEngineState(state);
   1795             mConverter = mConverterJAJP;
   1796             mPreConverter = mPreConverterHiragana;
   1797             break;
   1798         }
   1799 
   1800         mPreConverterBack = mPreConverter;
   1801         mConverterBack = mConverter;
   1802     }
   1803 
   1804     /**
   1805      * Update the conversion engine's state.
   1806      *
   1807      * @param state  Engine's state to be updated
   1808      */
   1809     private void updateEngineState(EngineState state) {
   1810         EngineState myState = mEngineState;
   1811 
   1812         /* language */
   1813         if ((state.dictionarySet != EngineState.INVALID)
   1814             && (myState.dictionarySet != state.dictionarySet)) {
   1815 
   1816             switch (state.dictionarySet) {
   1817             case EngineState.DICTIONARYSET_EN:
   1818                 setDictionary(OpenWnnEngineJAJP.DIC_LANG_EN);
   1819                 break;
   1820 
   1821             case EngineState.DICTIONARYSET_JP:
   1822             default:
   1823                 setDictionary(OpenWnnEngineJAJP.DIC_LANG_JP);
   1824                 break;
   1825             }
   1826             myState.dictionarySet = state.dictionarySet;
   1827             breakSequence();
   1828 
   1829             /* update keyboard setting */
   1830             if (state.keyboard == EngineState.INVALID) {
   1831                 state.keyboard = myState.keyboard;
   1832             }
   1833         }
   1834 
   1835         /* type of conversion */
   1836         if ((state.convertType != EngineState.INVALID)
   1837             && (myState.convertType != state.convertType)) {
   1838 
   1839             switch (state.convertType) {
   1840             case EngineState.CONVERT_TYPE_NONE:
   1841                 setDictionary(mPrevDictionarySet);
   1842                 break;
   1843 
   1844             case EngineState.CONVERT_TYPE_EISU_KANA:
   1845                 setDictionary(OpenWnnEngineJAJP.DIC_LANG_JP_EISUKANA);
   1846                 break;
   1847 
   1848             case EngineState.CONVERT_TYPE_RENBUN:
   1849             default:
   1850                 setDictionary(OpenWnnEngineJAJP.DIC_LANG_JP);
   1851                 break;
   1852             }
   1853             myState.convertType = state.convertType;
   1854         }
   1855 
   1856         /* temporary dictionary */
   1857         if (state.temporaryMode != EngineState.INVALID) {
   1858 
   1859             switch (state.temporaryMode) {
   1860             case EngineState.TEMPORARY_DICTIONARY_MODE_NONE:
   1861                 if (myState.temporaryMode != EngineState.TEMPORARY_DICTIONARY_MODE_NONE) {
   1862                     setDictionary(mPrevDictionarySet);
   1863                     mCurrentSymbol = 0;
   1864                     mPreConverter = mPreConverterBack;
   1865                     mConverter = mConverterBack;
   1866                     mDisableAutoCommitEnglishMask &= ~AUTO_COMMIT_ENGLISH_SYMBOL;
   1867                 }
   1868                 break;
   1869 
   1870             case EngineState.TEMPORARY_DICTIONARY_MODE_SYMBOL:
   1871                 if (++mCurrentSymbol >= SYMBOL_LISTS.length) {
   1872                     mCurrentSymbol = 0;
   1873                 }
   1874                 if (mEnableSymbolListNonHalf) {
   1875                     mConverterSymbolEngineBack.setDictionary(SYMBOL_LISTS[mCurrentSymbol]);
   1876                 } else {
   1877                     mConverterSymbolEngineBack.setDictionary(SymbolList.SYMBOL_ENGLISH);
   1878                 }
   1879                 mConverter = mConverterSymbolEngineBack;
   1880                 mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_SYMBOL;
   1881                 breakSequence();
   1882                 break;
   1883 
   1884             default:
   1885                 break;
   1886             }
   1887             myState.temporaryMode = state.temporaryMode;
   1888         }
   1889 
   1890         /* preference dictionary */
   1891         if ((state.preferenceDictionary != EngineState.INVALID)
   1892             && (myState.preferenceDictionary != state.preferenceDictionary)) {
   1893 
   1894             myState.preferenceDictionary = state.preferenceDictionary;
   1895             setDictionary(mPrevDictionarySet);
   1896         }
   1897 
   1898         /* keyboard type */
   1899         if (state.keyboard != EngineState.INVALID) {
   1900             switch (state.keyboard) {
   1901             case EngineState.KEYBOARD_12KEY:
   1902                 mConverterJAJP.setKeyboardType(OpenWnnEngineJAJP.KEYBOARD_KEYPAD12);
   1903                 mConverterEN.setDictionary(OpenWnnEngineEN.DICT_DEFAULT);
   1904                 break;
   1905 
   1906             case EngineState.KEYBOARD_QWERTY:
   1907             default:
   1908                 mConverterJAJP.setKeyboardType(OpenWnnEngineJAJP.KEYBOARD_QWERTY);
   1909                 if (mEnableSpellCorrection) {
   1910                     mConverterEN.setDictionary(OpenWnnEngineEN.DICT_FOR_CORRECT_MISTYPE);
   1911                 } else {
   1912                     mConverterEN.setDictionary(OpenWnnEngineEN.DICT_DEFAULT);
   1913                 }
   1914                 break;
   1915             }
   1916             myState.keyboard = state.keyboard;
   1917         }
   1918     }
   1919 
   1920     /**
   1921      * Set dictionaries to be used.
   1922      *
   1923      * @param mode  Definition of dictionaries
   1924      */
   1925     private void setDictionary(int mode) {
   1926         int target = mode;
   1927         switch (target) {
   1928 
   1929         case OpenWnnEngineJAJP.DIC_LANG_JP:
   1930 
   1931             switch (mEngineState.preferenceDictionary) {
   1932             case EngineState.PREFERENCE_DICTIONARY_PERSON_NAME:
   1933                 target = OpenWnnEngineJAJP.DIC_LANG_JP_PERSON_NAME;
   1934                 break;
   1935             case EngineState.PREFERENCE_DICTIONARY_POSTAL_ADDRESS:
   1936                 target = OpenWnnEngineJAJP.DIC_LANG_JP_POSTAL_ADDRESS;
   1937                 break;
   1938             default:
   1939                 break;
   1940             }
   1941 
   1942             break;
   1943 
   1944         case OpenWnnEngineJAJP.DIC_LANG_EN:
   1945 
   1946             switch (mEngineState.preferenceDictionary) {
   1947             case EngineState.PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI:
   1948                 target = OpenWnnEngineJAJP.DIC_LANG_EN_EMAIL_ADDRESS;
   1949                 break;
   1950             default:
   1951                 break;
   1952             }
   1953 
   1954             break;
   1955 
   1956         default:
   1957             break;
   1958         }
   1959 
   1960         switch (mode) {
   1961         case OpenWnnEngineJAJP.DIC_LANG_JP:
   1962         case OpenWnnEngineJAJP.DIC_LANG_EN:
   1963             mPrevDictionarySet = mode;
   1964             break;
   1965         default:
   1966             break;
   1967         }
   1968 
   1969         mConverterJAJP.setDictionary(target);
   1970     }
   1971 
   1972     /**
   1973      * Handle a toggle key input event.
   1974      *
   1975      * @param table  Table of toggle characters
   1976      */
   1977     private void processSoftKeyboardToggleChar(String[] table) {
   1978         if (table == null) {
   1979             return;
   1980         }
   1981 
   1982         commitConvertingText();
   1983 
   1984         boolean toggled = false;
   1985         if ((mStatus & ~STATUS_CANDIDATE_FULL) == STATUS_INPUT) {
   1986             int cursor = mComposingText.getCursor(ComposingText.LAYER1);
   1987             if (cursor > 0) {
   1988                 String prevChar = mComposingText.getStrSegment(ComposingText.LAYER1,
   1989                                                                cursor - 1).string;
   1990                 String c = searchToggleCharacter(prevChar, table, false);
   1991                 if (c != null) {
   1992                     mComposingText.delete(ComposingText.LAYER1, false);
   1993                     appendStrSegment(new StrSegment(c));
   1994                     toggled = true;
   1995                 }
   1996             }
   1997         }
   1998 
   1999         if (!toggled) {
   2000             if (!isEnableL2Converter()) {
   2001                 commitText(false);
   2002             }
   2003 
   2004             String str = table[0];
   2005             /* shift on */
   2006             if (mAutoCaps && (getShiftKeyState(getCurrentInputEditorInfo()) == 1)) {
   2007                 char top = table[0].charAt(0);
   2008                 if (Character.isLowerCase(top)) {
   2009                     str = Character.toString(Character.toUpperCase(top));
   2010                 }
   2011             }
   2012             appendStrSegment(new StrSegment(str));
   2013         }
   2014 
   2015         mStatus = STATUS_INPUT;
   2016 
   2017         updateViewStatusForPrediction(true, true);
   2018     }
   2019 
   2020     /**
   2021      * Handle character input from the software keyboard without listing candidates.
   2022      *
   2023      * @param chars  The input character(s)
   2024      */
   2025     private void processSoftKeyboardCodeWithoutConversion(char[] chars) {
   2026         if (chars == null) {
   2027             return;
   2028         }
   2029 
   2030         ComposingText text = mComposingText;
   2031         appendStrSegment(new StrSegment(chars));
   2032 
   2033         if (!isAlphabetLast(text.toString(ComposingText.LAYER1))) {
   2034             /* commit if the input character is not alphabet */
   2035             commitText(false);
   2036         } else {
   2037             boolean completed = mPreConverter.convert(text);
   2038             if (completed) {
   2039                 commitTextWithoutLastAlphabet();
   2040             } else {
   2041                 mStatus = STATUS_INPUT;
   2042                 updateViewStatusForPrediction(true, true);
   2043             }
   2044         }
   2045     }
   2046 
   2047     /**
   2048      * Handle character input from the software keyboard.
   2049      *
   2050      * @param chars   The input character(s)
   2051      */
   2052     private void processSoftKeyboardCode(char[] chars) {
   2053         if (chars == null) {
   2054             return;
   2055         }
   2056 
   2057         if ((chars[0] == ' ') || (chars[0] == '\u3000' /* Full-width space */)) {
   2058             if (mComposingText.size(0) == 0) {
   2059                 mCandidatesViewManager.clearCandidates();
   2060                 commitText(new String(chars));
   2061                 breakSequence();
   2062             } else {
   2063                 if (isEnglishPrediction()) {
   2064                     initCommitInfoForWatchCursor();
   2065                     commitText(true);
   2066                     commitSpaceJustOne();
   2067                     checkCommitInfo();
   2068                 } else {
   2069                     startConvert(EngineState.CONVERT_TYPE_RENBUN);
   2070                 }
   2071             }
   2072             mEnableAutoDeleteSpace = false;
   2073         } else {
   2074             commitConvertingText();
   2075 
   2076             /* Auto-commit a word if it is English and Qwerty mode */
   2077             boolean commit = false;
   2078             if (isEnglishPrediction()
   2079                 && (mEngineState.keyboard == EngineState.KEYBOARD_QWERTY)) {
   2080 
   2081                 Matcher m = mEnglishAutoCommitDelimiter.matcher(new String(chars));
   2082                 if (m.matches()) {
   2083                     commit = true;
   2084                 }
   2085             }
   2086 
   2087             if (commit) {
   2088                 commitText(true);
   2089 
   2090                 appendStrSegment(new StrSegment(chars));
   2091                 commitText(true);
   2092             } else {
   2093                 appendStrSegment(new StrSegment(chars));
   2094                 if (mPreConverter != null) {
   2095                     mPreConverter.convert(mComposingText);
   2096                     mStatus = STATUS_INPUT;
   2097                 }
   2098                 updateViewStatusForPrediction(true, true);
   2099             }
   2100         }
   2101     }
   2102 
   2103     /**
   2104      * Start consecutive clause conversion or EISU-KANA conversion mode.
   2105      *
   2106      * @param convertType               The conversion type({@code EngineState.CONVERT_TYPE_*})
   2107      */
   2108     private void startConvert(int convertType) {
   2109         if (!isEnableL2Converter()) {
   2110             return;
   2111         }
   2112 
   2113         if (mEngineState.convertType != convertType) {
   2114             /* adjust the cursor position */
   2115             if (!mExactMatchMode) {
   2116                 if (convertType == EngineState.CONVERT_TYPE_RENBUN) {
   2117                     /* not specify */
   2118                     mComposingText.setCursor(ComposingText.LAYER1, 0);
   2119                 } else {
   2120                     if (mEngineState.isRenbun()) {
   2121                         /* EISU-KANA conversion specifying the position of the segment if previous mode is conversion mode */
   2122                         mExactMatchMode = true;
   2123                     } else {
   2124                         /* specify all range */
   2125                         mComposingText.setCursor(ComposingText.LAYER1,
   2126                                                  mComposingText.size(ComposingText.LAYER1));
   2127                     }
   2128                 }
   2129             }
   2130 
   2131             if (convertType == EngineState.CONVERT_TYPE_RENBUN) {
   2132                 /* clears variables for the prediction */
   2133                 mExactMatchMode = false;
   2134             }
   2135             /* clears variables for the convert */
   2136             mCommitCount = 0;
   2137 
   2138             int layer;
   2139             if (convertType == EngineState.CONVERT_TYPE_EISU_KANA) {
   2140                 layer = ComposingText.LAYER1;
   2141             } else {
   2142                 layer = ComposingText.LAYER2;
   2143             }
   2144 
   2145             EngineState state = new EngineState();
   2146             state.convertType = convertType;
   2147             updateEngineState(state);
   2148 
   2149             updateViewStatus(layer, true, true);
   2150         }
   2151     }
   2152 
   2153     /**
   2154      * Auto commit a word in English (on half-width alphabet mode).
   2155      *
   2156      * @return  {@code true} if auto-committed; otherwise, {@code false}.
   2157      */
   2158     private boolean autoCommitEnglish() {
   2159         if (isEnglishPrediction() && (mDisableAutoCommitEnglishMask == AUTO_COMMIT_ENGLISH_ON)) {
   2160             CharSequence seq = mInputConnection.getTextBeforeCursor(2, 0);
   2161             Matcher m = mEnglishAutoCommitDelimiter.matcher(seq);
   2162             if (m.matches()) {
   2163                 if ((seq.charAt(0) == ' ') && mEnableAutoDeleteSpace) {
   2164                     mInputConnection.deleteSurroundingText(2, 0);
   2165                     CharSequence str = seq.subSequence(1, 2);
   2166                     mInputConnection.commitText(str, 1);
   2167                     mPrevCommitText.append(str);
   2168                     mPrevCommitCount++;
   2169                 }
   2170 
   2171                 mHandler.removeMessages(MSG_PREDICTION);
   2172                 mCandidatesViewManager.clearCandidates();
   2173                 return true;
   2174             }
   2175         }
   2176         return false;
   2177     }
   2178 
   2179     /**
   2180      * Insert a white space if the previous character is not a white space.
   2181      */
   2182     private void commitSpaceJustOne() {
   2183         CharSequence seq = mInputConnection.getTextBeforeCursor(1, 0);
   2184         if (seq.charAt(0) != ' ') {
   2185             commitText(" ");
   2186         }
   2187     }
   2188 
   2189     /**
   2190      * Get the shift key state from the editor.
   2191      *
   2192      * @param editor    The editor
   2193      * @return          State ID of the shift key (0:off, 1:on)
   2194      */
   2195     protected int getShiftKeyState(EditorInfo editor) {
   2196         return (getCurrentInputConnection().getCursorCapsMode(editor.inputType) == 0) ? 0 : 1;
   2197     }
   2198 
   2199     /**
   2200      * Display current meta-key state.
   2201      */
   2202     private void updateMetaKeyStateDisplay() {
   2203         int mode = 0;
   2204         if(mHardShift == 0 && mHardAlt == 0){
   2205             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
   2206         }else if(mHardShift == 1 && mHardAlt == 0){
   2207             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_OFF;
   2208         }else if(mHardShift == 2  && mHardAlt == 0){
   2209             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_OFF;
   2210         }else if(mHardShift == 0 && mHardAlt == 1){
   2211             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_ON;
   2212         }else if(mHardShift == 0 && mHardAlt == 2){
   2213             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_LOCK;
   2214         }else if(mHardShift == 1 && mHardAlt == 1){
   2215             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_ON;
   2216         }else if(mHardShift == 1 && mHardAlt == 2){
   2217             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_LOCK;
   2218         }else if(mHardShift == 2 && mHardAlt == 1){
   2219             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_ON;
   2220         }else if(mHardShift == 2 && mHardAlt == 2){
   2221             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK;
   2222         }else{
   2223             mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
   2224         }
   2225         ((DefaultSoftKeyboard) mInputViewManager).updateIndicator(mode);
   2226     }
   2227 
   2228     /**
   2229      * Memory a selected word.
   2230      *
   2231      * @param word  A selected word
   2232      */
   2233     private void learnWord(WnnWord word) {
   2234         if (mEnableLearning && word != null) {
   2235             mConverter.learn(word);
   2236         }
   2237     }
   2238 
   2239     /**
   2240      * Memory a clause which is generated by consecutive clause conversion.
   2241      *
   2242      * @param index  Index of a clause
   2243      */
   2244     private void learnWord(int index) {
   2245         ComposingText composingText = mComposingText;
   2246 
   2247         if (mEnableLearning && composingText.size(ComposingText.LAYER2) > index) {
   2248             StrSegment seg = composingText.getStrSegment(ComposingText.LAYER2, index);
   2249             if (seg instanceof StrSegmentClause) {
   2250                 mConverter.learn(((StrSegmentClause)seg).clause);
   2251             } else {
   2252                 String stroke = composingText.toString(ComposingText.LAYER1, seg.from, seg.to);
   2253                 mConverter.learn(new WnnWord(seg.string, stroke));
   2254             }
   2255         }
   2256     }
   2257 
   2258     /**
   2259      * Fits an editor info.
   2260      *
   2261      * @param preferences  The preference data.
   2262      * @param info              The editor info.
   2263      */
   2264     private void fitInputType(SharedPreferences preference, EditorInfo info) {
   2265         if (info.inputType == EditorInfo.TYPE_NULL) {
   2266             mDirectInputMode = true;
   2267             return;
   2268         }
   2269 
   2270         mEnableLearning   = preference.getBoolean("opt_enable_learning", true);
   2271         mEnablePrediction = preference.getBoolean("opt_prediction", true);
   2272         mEnableSpellCorrection = preference.getBoolean("opt_spell_correction", true);
   2273         mDisableAutoCommitEnglishMask &= ~AUTO_COMMIT_ENGLISH_OFF;
   2274         int preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_NONE;
   2275         mEnableConverter = true;
   2276         mEnableSymbolList = true;
   2277         mEnableSymbolListNonHalf = true;
   2278         mAutoCaps = preference.getBoolean("auto_caps", true);
   2279         mFilter.filter = 0;
   2280         mEnableAutoInsertSpace = true;
   2281         mEnableAutoHideKeyboard = false;
   2282 
   2283         switch (info.inputType & EditorInfo.TYPE_MASK_CLASS) {
   2284         case EditorInfo.TYPE_CLASS_NUMBER:
   2285         case EditorInfo.TYPE_CLASS_DATETIME:
   2286             mEnableConverter = false;
   2287             break;
   2288 
   2289         case EditorInfo.TYPE_CLASS_PHONE:
   2290             mEnableSymbolList = false;
   2291             mEnableConverter = false;
   2292             break;
   2293 
   2294         case EditorInfo.TYPE_CLASS_TEXT:
   2295 
   2296             switch (info.inputType & EditorInfo.TYPE_MASK_VARIATION) {
   2297             case EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME:
   2298                 preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_PERSON_NAME;
   2299                 break;
   2300 
   2301             case EditorInfo.TYPE_TEXT_VARIATION_PASSWORD:
   2302             case EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
   2303                 mEnableLearning = false;
   2304                 mEnableConverter = false;
   2305                 mEnableSymbolListNonHalf = false;
   2306                 mFilter.filter = CandidateFilter.FILTER_NON_ASCII;
   2307                 mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_OFF;
   2308                 break;
   2309 
   2310             case EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS:
   2311                 mEnableAutoInsertSpace = false;
   2312                 mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_OFF;
   2313                 preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI;
   2314                 break;
   2315 
   2316             case EditorInfo.TYPE_TEXT_VARIATION_URI:
   2317                 mEnableAutoInsertSpace = false;
   2318                 mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_OFF;
   2319                 preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI;
   2320                 break;
   2321 
   2322             case EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS:
   2323                 preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_POSTAL_ADDRESS;
   2324                 break;
   2325 
   2326             case EditorInfo.TYPE_TEXT_VARIATION_PHONETIC:
   2327                 mEnableLearning = false;
   2328                 mEnableConverter = false;
   2329                 mEnableSymbolList = false;
   2330                 break;
   2331 
   2332             default:
   2333                 break;
   2334             }
   2335             break;
   2336 
   2337         default:
   2338             break;
   2339         }
   2340 
   2341         if (mFilter.filter == 0) {
   2342             mConverterEN.setFilter(null);
   2343             mConverterJAJP.setFilter(null);
   2344         } else {
   2345             mConverterEN.setFilter(mFilter);
   2346             mConverterJAJP.setFilter(mFilter);
   2347         }
   2348 
   2349         EngineState state = new EngineState();
   2350         state.preferenceDictionary = preferenceDictionary;
   2351         state.convertType = EngineState.CONVERT_TYPE_NONE;
   2352         state.keyboard = mEngineState.keyboard;
   2353         updateEngineState(state);
   2354         updateMetaKeyStateDisplay();
   2355 
   2356         checkTutorial(info.privateImeOptions);
   2357     }
   2358 
   2359     /**
   2360      * Append a {@link StrSegment} to the composing text
   2361      * <br>
   2362      * If the length of the composing text exceeds
   2363      * {@code LIMIT_INPUT_NUMBER}, the appending operation is ignored.
   2364      *
   2365      * @param  str  Input segment
   2366      */
   2367     private void appendStrSegment(StrSegment str) {
   2368         ComposingText composingText = mComposingText;
   2369 
   2370         if (composingText.size(ComposingText.LAYER1) >= LIMIT_INPUT_NUMBER) {
   2371             return; /* do nothing */
   2372         }
   2373         composingText.insertStrSegment(ComposingText.LAYER0, ComposingText.LAYER1, str);
   2374         return;
   2375     }
   2376 
   2377     /**
   2378      * Commit the consecutive clause conversion.
   2379      */
   2380     private void commitConvertingText() {
   2381         if (mEngineState.isConvertState()) {
   2382             int size = mComposingText.size(ComposingText.LAYER2);
   2383             for (int i = 0; i < size; i++) {
   2384                 learnWord(i);
   2385             }
   2386 
   2387             String text = mComposingText.toString(ComposingText.LAYER2);
   2388             mInputConnection.commitText(text, (FIX_CURSOR_TEXT_END ? 1 : text.length()));
   2389             mPrevCommitText.append(text);
   2390             mPrevCommitCount++;
   2391             initializeScreen();
   2392         }
   2393     }
   2394 
   2395     /**
   2396      * Initialize the screen displayed by IME
   2397      */
   2398     private void initializeScreen() {
   2399         if (mComposingText.size(ComposingText.LAYER0) != 0) {
   2400             mInputConnection.setComposingText("", 0);
   2401         }
   2402         mComposingText.clear();
   2403         mExactMatchMode = false;
   2404         mStatus = STATUS_INIT;
   2405         mHandler.removeMessages(MSG_PREDICTION);
   2406         View candidateView = mCandidatesViewManager.getCurrentView();
   2407         if ((candidateView != null) && candidateView.isShown()) {
   2408             mCandidatesViewManager.clearCandidates();
   2409         }
   2410         mInputViewManager.onUpdateState(this);
   2411 
   2412         EngineState state = new EngineState();
   2413         state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
   2414         updateEngineState(state);
   2415     }
   2416 
   2417     /**
   2418      * Whether the tail of the string is alphabet or not.
   2419      *
   2420      * @param  str      The string
   2421      * @return          {@code true} if the tail is alphabet; {@code false} if otherwise.
   2422      */
   2423     private boolean isAlphabetLast(String str) {
   2424         Matcher m = ENGLISH_CHARACTER_LAST.matcher(str);
   2425         return m.matches();
   2426     }
   2427 
   2428     /** @see jp.co.omronsoft.openwnn.OpenWnn#onFinishInput */
   2429     @Override public void onFinishInput() {
   2430         if (mInputConnection != null) {
   2431             initializeScreen();
   2432         }
   2433         super.onFinishInput();
   2434     }
   2435 
   2436     /**
   2437      * Check whether or not the converter is active.
   2438      *
   2439      * @return {@code true} if the converter is active.
   2440      */
   2441     private boolean isEnableL2Converter() {
   2442         if (mConverter == null || !mEnableConverter) {
   2443             return false;
   2444         }
   2445 
   2446         if (mEngineState.isEnglish() && !mEnablePrediction) {
   2447             return false;
   2448         }
   2449 
   2450         return true;
   2451     }
   2452 
   2453     /**
   2454      * Handling KeyEvent(KEYUP)
   2455      * <br>
   2456      * This method is called from {@link #onEvent()}.
   2457      *
   2458      * @param ev   An up key event
   2459      */
   2460     private void onKeyUpEvent(KeyEvent ev) {
   2461         int key = ev.getKeyCode();
   2462         if(!mShiftPressing){
   2463             if(key == KeyEvent.KEYCODE_SHIFT_LEFT || key == KeyEvent.KEYCODE_SHIFT_RIGHT){
   2464                 mHardShift = 0;
   2465                 mShiftPressing = true;
   2466                 updateMetaKeyStateDisplay();
   2467             }
   2468         }
   2469         if(!mAltPressing ){
   2470             if(key == KeyEvent.KEYCODE_ALT_LEFT || key == KeyEvent.KEYCODE_ALT_RIGHT){
   2471                 mHardAlt = 0;
   2472                 mAltPressing   = true;
   2473                 updateMetaKeyStateDisplay();
   2474             }
   2475         }
   2476     }
   2477 
   2478     /**
   2479      * Initialize the committed text's information.
   2480      */
   2481     private void initCommitInfoForWatchCursor() {
   2482         if (!isEnableL2Converter()) {
   2483             return;
   2484         }
   2485 
   2486         mCommitStartCursor = mComposingStartCursor;
   2487         mPrevCommitText.delete(0, mPrevCommitText.length());
   2488     }
   2489 
   2490     /**
   2491      * Clear the commit text's info.
   2492      * @return {@code true}:cleared, {@code false}:has already cleared.
   2493      */
   2494     private boolean clearCommitInfo() {
   2495         if (mCommitStartCursor < 0) {
   2496             return false;
   2497         }
   2498 
   2499         mCommitStartCursor = -1;
   2500         return true;
   2501     }
   2502 
   2503     /**
   2504      * Verify the commit text.
   2505      */
   2506     private void checkCommitInfo() {
   2507         if (mCommitStartCursor < 0) {
   2508             return;
   2509         }
   2510 
   2511         int composingLength = mComposingText.toString(mTargetLayer).length();
   2512         CharSequence seq = mInputConnection.getTextBeforeCursor(mPrevCommitText.length() + composingLength, 0);
   2513         seq = seq.subSequence(0, seq.length() - composingLength);
   2514         if (!seq.equals(mPrevCommitText.toString())) {
   2515             mPrevCommitCount = 0;
   2516             clearCommitInfo();
   2517         }
   2518     }
   2519 
   2520     /**
   2521      * Check and start the tutorial if it is the tutorial mode.
   2522      *
   2523      * @param privateImeOptions IME's options
   2524      */
   2525     private void checkTutorial(String privateImeOptions) {
   2526         if (privateImeOptions == null) return;
   2527         if (privateImeOptions.equals("com.google.android.setupwizard:ShowTutorial")) {
   2528             if ((mTutorial == null) && mEnableTutorial) startTutorial();
   2529         } else if (privateImeOptions.equals("com.google.android.setupwizard:HideTutorial")) {
   2530             if (mTutorial != null) {
   2531                 if (mTutorial.close()) {
   2532                     mTutorial = null;
   2533                 }
   2534             }
   2535         }
   2536     }
   2537 
   2538     /**
   2539      * Start the tutorial
   2540      */
   2541     private void startTutorial() {
   2542         DefaultSoftKeyboardJAJP manager = (DefaultSoftKeyboardJAJP) mInputViewManager;
   2543         manager.setDefaultKeyboard();
   2544         if (mEngineState.keyboard == EngineState.KEYBOARD_QWERTY) {
   2545             manager.changeKeyboardType(DefaultSoftKeyboard.KEYBOARD_12KEY);
   2546         }
   2547 
   2548         DefaultSoftKeyboardJAJP inputManager = ((DefaultSoftKeyboardJAJP) mInputViewManager);
   2549         View v = inputManager.getKeyboardView();
   2550         v.setOnTouchListener(new View.OnTouchListener() {
   2551                 public boolean onTouch(View v, MotionEvent event) {
   2552                     return true;
   2553                 }});
   2554         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
   2555     }
   2556 
   2557     /**
   2558      * Close the tutorial
   2559      */
   2560     public void tutorialDone() {
   2561         mTutorial = null;
   2562     }
   2563 
   2564     /** @see OpenWnn#close */
   2565     @Override protected void close() {
   2566         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLOSE), 0);
   2567     }
   2568 
   2569     /**
   2570      * Break the sequence of words.
   2571      */
   2572     private void breakSequence() {
   2573         mEnableAutoDeleteSpace = false;
   2574         mConverterJAJP.breakSequence();
   2575         mConverterEN.breakSequence();
   2576     }
   2577 }
   2578