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