OSDN Git Service

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