1 /*
2  * Copyright (C) 2008-2012  OMRON SOFTWARE Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package jp.co.omronsoft.openwnn;
18 
19 
20 import jp.co.omronsoft.openwnn.EN.OpenWnnEngineEN;
21 import jp.co.omronsoft.openwnn.JAJP.*;
22 import android.content.SharedPreferences;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.res.Configuration;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.preference.PreferenceManager;
29 import android.text.SpannableStringBuilder;
30 import android.text.Spanned;
31 import android.text.style.BackgroundColorSpan;
32 import android.text.style.CharacterStyle;
33 import android.text.style.ForegroundColorSpan;
34 import android.text.style.UnderlineSpan;
35 import android.util.Log;
36 import android.view.KeyEvent;
37 import android.view.inputmethod.EditorInfo;
38 import android.view.inputmethod.InputConnection;
39 import android.view.MotionEvent;
40 import android.view.View;
41 import android.view.KeyCharacterMap;
42 import android.text.method.MetaKeyKeyListener;
43 
44 import jp.co.omronsoft.openwnn.BaseInputView;
45 import jp.co.omronsoft.openwnn.OpenWnnControlPanelJAJP;
46 
47 import java.util.HashMap;
48 import java.util.regex.Pattern;
49 import 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  */
56 public 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          */
isRenbun()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          */
isEisuKana()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          */
isConvertState()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          */
isSymbolList()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          */
isEnglish()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      */
OpenWnnJAJP()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      */
OpenWnnJAJP(Context context)496     public OpenWnnJAJP(Context context) {
497         this();
498         attachBaseContext(context);
499     }
500 
501     /** @see jp.co.omronsoft.openwnn.OpenWnn#onCreate */
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 */
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 */
onStartInputView(EditorInfo attribute, boolean restarting)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 */
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 */
onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)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 */
onConfigurationChanged(Configuration newConfig)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 */
onEvent(OpenWnnEvent ev)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 */
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 */
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      */
getInstance()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      */
createStrSegment(int charCode)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      */
processKeyEvent(KeyEvent ev)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      */
processHardwareKeyboardSpaceKey(KeyEvent ev)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      */
processHardwareKeyboardInputChar(StrSegment str)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 */
updatePrediction()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      */
processLeftKeyEvent()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      */
processRightKeyEvent()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      */
processDownKeyEvent()1692     private void processDownKeyEvent() {
1693         mCandidatesViewManager.processMoveKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN);
1694     }
1695 
1696     /**
1697      * Handle a up key event.
1698      */
processUpKeyEvent()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      */
processKeyEventNoInputCandidateShown(KeyEvent ev)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      */
updateViewStatusForPrediction(boolean updateCandidates, boolean updateEmptyText)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      */
updateViewStatus(int layer, boolean updateCandidates, boolean updateEmptyText)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      */
updateCandidateView()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      */
commitText(boolean learn)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      */
commitTextWithoutLastAlphabet()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      */
commitAllText()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      */
commitText(WnnWord word)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      */
commitText(String str)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      */
commitTextThroughInputConnection(String string)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      */
isEnglishPrediction()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      */
changeEngineMode(int mode)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      */
updateEngineState(EngineState state)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      */
setDictionary(int mode)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      */
processSoftKeyboardToggleChar(String[] table)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      */
processSoftKeyboardCodeWithoutConversion(char[] chars)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      */
processSoftKeyboardCode(char[] chars)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      */
startConvert(int convertType)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      */
autoCommitEnglish()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      */
commitSpaceJustOne()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      */
getShiftKeyState(EditorInfo editor)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      */
updateMetaKeyStateDisplay()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      */
learnWord(WnnWord word)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      */
learnWord(int index)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      */
fitInputType(SharedPreferences preference, EditorInfo info)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      */
appendStrSegment(StrSegment str)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      */
commitConvertingText()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      */
initializeScreen()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      */
isAlphabetLast(String str)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 */
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      */
isEnableL2Converter()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      */
onKeyUpEvent(KeyEvent ev)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      */
onKeyLongPressEvent(KeyEvent ev)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      */
initCommitInfoForWatchCursor()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      */
clearCommitInfo()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      */
checkCommitInfo()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      */
checkTutorial(String privateImeOptions)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      */
startTutorial()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      */
tutorialDone()3041     public void tutorialDone() {
3042         mTutorial = null;
3043     }
3044 
3045     /** @see OpenWnn#close */
close()3046     @Override protected void close() {
3047         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLOSE), 0);
3048     }
3049 
3050     /**
3051      * Break the sequence of words.
3052      */
breakSequence()3053     private void breakSequence() {
3054         mEnableAutoDeleteSpace = false;
3055         mConverterJAJP.breakSequence();
3056         mConverterEN.breakSequence();
3057     }
3058 
3059     /**
3060      * Switch symbol list.
3061      */
switchSymbolList()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      */
changeSymbolEngineState(EngineState state, int mode)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      */
setEnabledTabs(boolean enableEmoticon)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      */
isHardKeyboard12KeyLongPress(int keyCode)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      */
processHardware12Keyboard(KeyEvent keyEvent)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