2 * Copyright (C) 2008,2009 OMRON SOFTWARE Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.hiroshica.android.input.nicownn2;
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;
40 import java.util.regex.Pattern;
41 import java.util.regex.Matcher;
43 import com.hiroshica.android.input.nicownn2.EN.NicoWnnEngineEN;
44 import com.hiroshica.android.input.nicownn2.JAJP.*;
47 * The OpenWnn Japanese IME class
49 * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD. All Rights Reserved.
51 public class NicoWnnJAJP extends NicoWnn {
53 * Mode of the convert engine (Full-width KATAKANA).
54 * Use with {@code OpenWnn.CHANGE_MODE} event.
56 public static final int ENGINE_MODE_FULL_KATAKANA = 101;
59 * Mode of the convert engine (Half-width KATAKANA).
60 * Use with {@code OpenWnn.CHANGE_MODE} event.
62 public static final int ENGINE_MODE_HALF_KATAKANA = 102;
65 * Mode of the convert engine (EISU-KANA conversion).
66 * Use with {@code OpenWnn.CHANGE_MODE} event.
68 public static final int ENGINE_MODE_EISU_KANA = 103;
71 * Mode of the convert engine (Symbol list).
72 * Use with {@code OpenWnn.CHANGE_MODE} event.
74 public static final int ENGINE_MODE_SYMBOL = 104;
75 public static final int ENGINE_MODE_DOCOMOSYMBOL = 107;
78 * Mode of the convert engine (Keyboard type is QWERTY).
79 * Use with {@code OpenWnn.CHANGE_MODE} event to change ambiguous searching pattern.
81 public static final int ENGINE_MODE_OPT_TYPE_QWERTY = 105;
84 * Mode of the convert engine (Keyboard type is 12-keys).
85 * Use with {@code OpenWnn.CHANGE_MODE} event to change ambiguous searching pattern.
87 public static final int ENGINE_MODE_OPT_TYPE_12KEY = 106;
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;
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();
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;
114 /** Alphabet-last pattern */
115 private static final Pattern ENGLISH_CHARACTER_LAST = Pattern.compile(".*[a-zA-Z]$");
118 * Private area character code got by {@link KeyEvent#getUnicodeChar()}.
119 * (SHIFT+ALT+X G1 specific)
121 private static final int PRIVATE_AREA_CODE = 61184;
123 /** Maximum length of input string */
124 private static final int LIMIT_INPUT_NUMBER = 30;
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;
133 /** Message for {@code mHandler} (execute prediction) */
134 private static final int MSG_PREDICTION = 0;
136 /** Message for {@code mHandler} (execute tutorial) */
137 private static final int MSG_START_TUTORIAL = 1;
139 /** Message for {@code mHandler} (close) */
140 private static final int MSG_CLOSE = 2;
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;
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;
149 /** Convert engine's state */
150 private class EngineState {
151 /** Definition for {@code EngineState.*} (invalid) */
152 public static final int INVALID = -1;
154 /** Definition for {@code EngineState.dictionarySet} (Japanese) */
155 public static final int DICTIONARYSET_JP = 0;
157 /** Definition for {@code EngineState.dictionarySet} (English) */
158 public static final int DICTIONARYSET_EN = 1;
160 /** Definition for {@code EngineState.convertType} (prediction/no conversion) */
161 public static final int CONVERT_TYPE_NONE = 0;
163 /** Definition for {@code EngineState.convertType} (consecutive clause conversion) */
164 public static final int CONVERT_TYPE_RENBUN = 1;
166 /** Definition for {@code EngineState.convertType} (EISU-KANA conversion) */
167 public static final int CONVERT_TYPE_EISU_KANA = 2;
169 /** Definition for {@code EngineState.temporaryMode} (change back to the normal dictionary) */
170 public static final int TEMPORARY_DICTIONARY_MODE_NONE = 0;
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;
176 /** Definition for {@code EngineState.temporaryMode} (change to the user dictionary) */
177 public static final int TEMPORARY_DICTIONARY_MODE_USER = 2;
179 /** Definition for {@code EngineState.preferenceDictionary} (no preference dictionary) */
180 public static final int PREFERENCE_DICTIONARY_NONE = 0;
182 /** Definition for {@code EngineState.preferenceDictionary} (person's name) */
183 public static final int PREFERENCE_DICTIONARY_PERSON_NAME = 1;
185 /** Definition for {@code EngineState.preferenceDictionary} (place name) */
186 public static final int PREFERENCE_DICTIONARY_POSTAL_ADDRESS = 2;
188 /** Definition for {@code EngineState.preferenceDictionary} (email/URI) */
189 public static final int PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI = 3;
191 /** Definition for {@code EngineState.keyboard} (undefined) */
192 public static final int KEYBOARD_UNDEF = 0;
194 /** Definition for {@code EngineState.keyboard} (QWERTY) */
195 public static final int KEYBOARD_QWERTY = 1;
197 /** Definition for {@code EngineState.keyboard} (12-keys) */
198 public static final int KEYBOARD_12KEY = 2;
200 /** Set of dictionaries */
201 public int dictionarySet = INVALID;
203 /** Type of conversion */
204 public int convertType = INVALID;
206 /** Temporary mode */
207 public int temporaryMode = INVALID;
209 /** Preference dictionary setting */
210 public int preferenceDictionary = INVALID;
213 public int keyboard = INVALID;
216 * Returns whether current type of conversion is consecutive clause(RENBUNSETSU) conversion.
218 * @return {@code true} if current type of conversion is consecutive clause conversion.
220 public boolean isRenbun() {
221 return convertType == CONVERT_TYPE_RENBUN;
225 * Returns whether current type of conversion is EISU-KANA conversion.
227 * @return {@code true} if current type of conversion is EISU-KANA conversion.
229 public boolean isEisuKana() {
230 return convertType == CONVERT_TYPE_EISU_KANA;
234 * Returns whether current type of conversion is no conversion.
236 * @return {@code true} if no conversion is executed currently.
238 public boolean isConvertState() {
239 return convertType != CONVERT_TYPE_NONE;
243 * Check whether or not the mode is "symbol list".
245 * @return {@code true} if the mode is "symbol list".
247 public boolean isSymbolList() {
248 return temporaryMode == TEMPORARY_DICTIONARY_MODE_SYMBOL;
252 * Check whether or not the current language is English.
254 * @return {@code true} if the current language is English.
256 public boolean isEnglish() {
257 return dictionarySet == DICTIONARYSET_EN;
262 protected int mStatus = STATUS_INIT;
264 /** Whether exact match searching or not */
265 protected boolean mExactMatchMode = false;
267 /** Spannable string builder for displaying the composing text */
268 protected SpannableStringBuilder mDisplayText;
270 /** Instance of this service */
271 private static NicoWnnJAJP mSelf = null;
273 /** Backup for switching the converter */
274 private WnnEngine mConverterBack;
276 /** Backup for switching the pre-converter */
277 private LetterConverter mPreConverterBack;
279 /** OpenWnn conversion engine for Japanese */
280 private NicoWnnEngineJAJP mConverterJAJP;
282 /** OpenWnn conversion engine for English */
283 private NicoWnnEngineEN mConverterEN;
285 /** Conversion engine for listing symbols */
286 private SymbolList mConverterSymbolEngineBack;
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
293 /** Current symbol list */
294 private int mCurrentSymbol = 0;
296 /** Romaji-to-Kana converter (HIRAGANA) */
297 private Romkan mPreConverterHiragana;
299 /** Romaji-to-Kana converter (full-width KATAKANA) */
300 private RomkanFullKatakana mPreConverterFullKatakana;
302 /** Romaji-to-Kana converter (half-width KATAKANA) */
303 private RomkanHalfKatakana mPreConverterHalfKatakana;
305 /** Conversion Engine's state */
306 private EngineState mEngineState = new EngineState();
308 /** Whether learning function is active of not. */
309 private boolean mEnableLearning = true;
311 /** Whether prediction is active or not. */
312 private boolean mEnablePrediction = true;
314 /** Whether using the converter */
315 private boolean mEnableConverter = true;
317 /** Whether displaying the symbol list */
318 private boolean mEnableSymbolList = true;
320 /** Whether non ASCII code is enabled */
321 private boolean mEnableSymbolListNonHalf = true;
323 /** Enable mistyping spell correction or not */
324 private boolean mEnableSpellCorrection = true;
326 /** Auto commit state (in English mode) */
327 private int mDisableAutoCommitEnglishMask = AUTO_COMMIT_ENGLISH_ON;
329 /** Whether removing a space before a separator or not. (in English mode) */
330 private boolean mEnableAutoDeleteSpace = false;
332 /** Whether auto-spacing is enabled or not. */
333 private boolean mEnableAutoInsertSpace = true;
335 /** Whether dismissing the keyboard when the enter key is pressed */
336 private boolean mEnableAutoHideKeyboard = true;
338 /** Number of committed clauses on consecutive clause conversion */
339 private int mCommitCount = 0;
341 /** Target layer of the {@link ComposingText} */
342 private int mTargetLayer = 1;
344 /** Current orientation of the display */
345 private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
347 /** Current normal dictionary set */
348 private int mPrevDictionarySet = NicoWnnEngineJAJP.DIC_LANG_INIT;
350 /** Regular expression pattern for English separators */
351 private Pattern mEnglishAutoCommitDelimiter = null;
353 /** Cursor position in the composing text */
354 private int mComposingStartCursor = 0;
356 /** Cursor position before committing text */
357 private int mCommitStartCursor = 0;
359 /** Previous committed text */
360 private StringBuffer mPrevCommitText = null;
362 /** Call count of {@code commitText} */
363 private int mPrevCommitCount = 0;
365 /** Shift lock status of the Hardware keyboard */
366 private int mHardShift;
368 /** SHIFT key state (pressing) */
369 private boolean mShiftPressing;
371 /** ALT lock status of the Hardware keyboard */
372 private int mHardAlt;
374 /** ALT key state (pressing) */
375 private boolean mAltPressing;
377 /** Shift lock toggle definition */
378 private static final int[] mShiftKeyToggle = {0, MetaKeyKeyListener.META_SHIFT_ON, MetaKeyKeyListener.META_CAP_LOCKED};
380 /** ALT lock toggle definition */
381 private static final int[] mAltKeyToggle = {0, MetaKeyKeyListener.META_ALT_ON, MetaKeyKeyListener.META_ALT_LOCKED};
383 /** Auto caps mode */
384 private boolean mAutoCaps = false;
386 /** List of words in the user dictionary */
387 private WnnWord[] mUserDictionaryWords = null;
390 private TutorialJAJP mTutorial;
392 /** Whether tutorial mode or not */
393 private boolean mEnableTutorial;
395 /** Whether there is a continued predicted candidate */
396 private boolean mHasContinuedPrediction = false;
398 /** {@code Handler} for drawing candidates/displaying tutorial */
399 Handler mHandler = new Handler() {
401 public void handleMessage(Message msg) {
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);
415 /* Try again soon if the view is not yet showing */
416 sendMessageDelayed(obtainMessage(MSG_START_TUTORIAL), 100);
421 if (mConverterJAJP != null) mConverterJAJP.close();
422 if (mConverterEN != null) mConverterEN.close();
423 if (mConverterSymbolEngineBack != null) mConverterSymbolEngineBack.close();
429 /** The candidate filter */
430 private CandidateFilter mFilter;
435 public NicoWnnJAJP() {
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();
448 mDisplayText = new SpannableStringBuilder();
449 mAutoHideMode = false;
451 mPrevCommitText = new StringBuffer();
457 * @param context The context
459 public NicoWnnJAJP(Context context) {
461 attachBaseContext(context);
464 /** @see com.hiroshica.android.input.nicownn2.NicoWnn#onCreate */
465 @Override public void onCreate() {
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);
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();
484 /** @see com.hiroshica.android.input.nicownn2.NicoWnn#onStartInputView */
485 @Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
487 EngineState state = new EngineState();
488 state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
489 updateEngineState(state);
491 mPrevCommitCount = 0;
494 /* load preferences */
495 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
497 /* start inputview */
498 ((DefaultSoftKeyboard) mInputViewManager).resetCurrentKeyboard();
500 super.onStartInputView(attribute, restarting);
502 /* initialize views */
503 mCandidatesViewManager.clearCandidates();
504 /* initialize status */
505 mStatus = STATUS_INIT;
506 mExactMatchMode = false;
508 /* hardware keyboard support */
511 updateMetaKeyStateDisplay();
513 /* initialize the engine's state */
514 fitInputType(pref, attribute);
516 ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(true);
518 if (isEnableL2Converter()) {
523 /** @see com.hiroshica.android.input.nicownn2.NicoWnn#hideWindow */
524 @Override public void hideWindow() {
525 mComposingText.clear();
526 mInputViewManager.onUpdateState(this);
528 mHandler.removeMessages(MSG_START_TUTORIAL);
529 mInputViewManager.closing();
530 if (mTutorial != null) {
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) {
541 mComposingStartCursor = (candidatesStart < 0) ? newSelEnd : candidatesStart;
543 if (newSelStart != newSelEnd) {
547 if (mHasContinuedPrediction) {
548 mHasContinuedPrediction = false;
549 if (0 < mPrevCommitCount) {
555 boolean isNotComposing = ((candidatesStart < 0) && (candidatesEnd < 0));
556 if ((mComposingText.size(ComposingText.LAYER1) != 0)
557 && !isNotComposing) {
558 updateViewStatus(mTargetLayer, false, true);
560 if (0 < mPrevCommitCount) {
563 int commitEnd = mCommitStartCursor + mPrevCommitText.length();
564 if ((((newSelEnd < oldSelEnd) || (commitEnd < newSelEnd)) && clearCommitInfo())
566 if (isEnableL2Converter()) {
570 if (mInputConnection != null) {
571 if (isNotComposing && (mComposingText.size(ComposingText.LAYER1) != 0)) {
572 mInputConnection.finishComposingText();
581 /** @see com.hiroshica.android.input.nicownn2.NicoWnn#onConfigurationChanged */
582 @Override public void onConfigurationChanged(Configuration newConfig) {
584 super.onConfigurationChanged(newConfig);
586 if (mInputConnection != null) {
587 if (super.isInputViewShown()) {
588 updateViewStatus(mTargetLayer, true, true);
591 /* display orientation */
592 if (mOrientation != newConfig.orientation) {
593 mOrientation = newConfig.orientation;
594 commitConvertingText();
598 /* Hardware keyboard */
599 int hiddenState = newConfig.hardKeyboardHidden;
600 boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
601 ((DefaultSoftKeyboardJAJP) mInputViewManager).setHardKeyboardHidden(hidden);
602 mEnableTutorial = hidden;
604 } catch (Exception ex) {
605 /* do nothing if an error occurs. */
609 /** @see com.hiroshica.android.input.nicownn2.NicoWnn#onEvent */
610 @Override synchronized public boolean onEvent(NicoWnnEvent ev) {
614 /* handling events which are valid when InputConnection is not active. */
617 case NicoWnnEvent.KEYUP:
618 onKeyUpEvent(ev.keyEvent);
621 case NicoWnnEvent.INITIALIZE_LEARNING_DICTIONARY:
622 mConverterEN.initializeDictionary(WnnEngine.DICTIONARY_TYPE_LEARN);
623 mConverterJAJP.initializeDictionary(WnnEngine.DICTIONARY_TYPE_LEARN);
626 case NicoWnnEvent.INITIALIZE_USER_DICTIONARY:
627 return mConverterJAJP.initializeDictionary( WnnEngine.DICTIONARY_TYPE_USER );
629 case NicoWnnEvent.LIST_WORDS_IN_USER_DICTIONARY:
630 mUserDictionaryWords = mConverterJAJP.getUserDictionaryWords( );
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];
639 mUserDictionaryWords[mUserDictionaryWords.length - 1] = null;
640 if (mUserDictionaryWords[0] == null) {
641 mUserDictionaryWords = null;
647 case NicoWnnEvent.ADD_WORD:
648 mConverterJAJP.addWord(ev.word);
651 case NicoWnnEvent.DELETE_WORD:
652 mConverterJAJP.deleteWord(ev.word);
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)) {
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);
669 updateViewStatus(mTargetLayer, true, true);
673 case NicoWnnEvent.CHANGE_INPUT_VIEW:
674 setInputView(onCreateInputView());
677 case NicoWnnEvent.CANDIDATE_VIEW_TOUCH:
679 ret = ((TextCandidatesViewManager)mCandidatesViewManager).onTouchSync();
682 case NicoWnnEvent.TOUCH_OTHER_KEY:
683 mStatus |= STATUS_INPUT_EDIT;
690 KeyEvent keyEvent = ev.keyEvent;
692 if (keyEvent != null) {
693 keyCode = keyEvent.getKeyCode();
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()));
703 /* return if InputConnection is not active */
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)))))) {
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)))))) {
730 state = new EngineState();
731 state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
732 updateEngineState(state);
735 if (ev.code == NicoWnnEvent.LIST_CANDIDATES_FULL) {
736 mStatus |= STATUS_CANDIDATE_FULL;
737 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_FULL);
739 } else if (ev.code == NicoWnnEvent.LIST_CANDIDATES_NORMAL) {
740 mStatus &= ~STATUS_CANDIDATE_FULL;
741 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
747 case NicoWnnEvent.INPUT_CHAR:
748 if ((mPreConverter == null) && !isEnableL2Converter()) {
749 /* direct input (= full-width alphabet/number input) */
751 commitText(new String(ev.chars));
752 mCandidatesViewManager.clearCandidates();
753 } else if (!isEnableL2Converter()) {
754 processSoftKeyboardCodeWithoutConversion(ev.chars);
756 processSoftKeyboardCode(ev.chars);
761 case NicoWnnEvent.TOGGLE_CHAR:
762 processSoftKeyboardToggleChar(ev.toggleTable);
766 case NicoWnnEvent.TOGGLE_REVERSE_CHAR:
767 if (((mStatus & ~STATUS_CANDIDATE_FULL) == STATUS_INPUT)
768 && !(mEngineState.isConvertState())) {
770 int cursor = mComposingText.getCursor(ComposingText.LAYER1);
772 String prevChar = mComposingText.getStrSegment(ComposingText.LAYER1, cursor - 1).string;
773 String c = searchToggleCharacter(prevChar, ev.toggleTable, true);
775 mComposingText.delete(ComposingText.LAYER1, false);
776 appendStrSegment(new StrSegment(c));
777 updateViewStatusForPrediction(true, true);
785 case NicoWnnEvent.REPLACE_CHAR:
786 int cursor = mComposingText.getCursor(ComposingText.LAYER1);
788 && !(mEngineState.isConvertState())) {
790 String search = mComposingText.getStrSegment(ComposingText.LAYER1, cursor - 1).string;
791 String c = (String)ev.replaceTable.get(search);
793 mComposingText.delete(1, false);
794 appendStrSegment(new StrSegment(c));
795 updateViewStatusForPrediction(true, true);
797 mStatus = STATUS_INPUT_EDIT;
803 case NicoWnnEvent.INPUT_KEY:
804 /* update shift/alt state */
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) {
815 case KeyEvent.KEYCODE_ALT_LEFT:
816 case KeyEvent.KEYCODE_ALT_RIGHT:
817 if (keyEvent.getRepeatCount() == 0) {
818 if (++mHardAlt > 2) { mHardAlt = 0; }
821 updateMetaKeyStateDisplay();
824 case KeyEvent.KEYCODE_SHIFT_LEFT:
825 case KeyEvent.KEYCODE_SHIFT_RIGHT:
826 if (keyEvent.getRepeatCount() == 0) {
827 if (++mHardShift > 2) { mHardShift = 0; }
829 mShiftPressing = true;
830 updateMetaKeyStateDisplay();
834 /* handle other key event */
835 ret = processKeyEvent(keyEvent);
838 case NicoWnnEvent.INPUT_SOFT_KEY:
839 ret = processKeyEvent(keyEvent);
841 mInputConnection.sendKeyEvent(keyEvent);
842 mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEvent.getKeyCode()));
847 case NicoWnnEvent.SELECT_CANDIDATE:
848 initCommitInfoForWatchCursor();
849 if (isEnglishPrediction()) {
850 mComposingText.clear();
852 mStatus = commitText(ev.word);
853 if (isEnglishPrediction() && !mEngineState.isSymbolList() && mEnableAutoInsertSpace) {
854 commitSpaceJustOne();
858 if (mEngineState.isSymbolList()) {
859 mEnableAutoDeleteSpace = false;
863 case NicoWnnEvent.CONVERT:
864 startConvert(EngineState.CONVERT_TYPE_RENBUN);
867 case NicoWnnEvent.COMMIT_COMPOSING_TEXT:
875 /** @see com.hiroshica.android.input.nicownn2.NicoWnn#onEvaluateFullscreenMode */
876 @Override public boolean onEvaluateFullscreenMode() {
877 /* never use full-screen mode */
881 /** @see com.hiroshica.android.input.nicownn2.NicoWnn#onEvaluateInputViewShown */
882 @Override public boolean onEvaluateInputViewShown() {
887 * Get the instance of this service.
889 * Before using this method, the constructor of this service must be invoked.
891 * @return The instance of this service
893 public static NicoWnnJAJP getInstance() {
898 * Create a {@link StrSegment} from a character code.
900 * @param charCode A character code
901 * @return {@link StrSegment} created; {@code null} if an error occurs.
903 private StrSegment createStrSegment(int charCode) {
907 return new StrSegment(Character.toChars(charCode));
913 * @param ev A key event
914 * @return {@code true} if the event is handled in this method.
916 private boolean processKeyEvent(KeyEvent ev) {
917 int key = ev.getKeyCode();
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) {
927 mShiftPressing = false;
930 mAltPressing = false;
932 if(!ev.isAltPressed()){
937 if(!ev.isShiftPressed()){
938 if (mHardShift == 1) {
942 if(!ev.isShiftPressed() && !ev.isAltPressed()){
943 updateMetaKeyStateDisplay();
949 commitConvertingText();
951 EditorInfo edit = getCurrentInputEditorInfo();
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));
962 str = createStrSegment(ev.getUnicodeChar());
965 str = createStrSegment(ev.getUnicodeChar(mShiftKeyToggle[mHardShift]
966 | mAltKeyToggle[mHardAlt]));
968 mShiftPressing = false;
971 mAltPressing = false;
973 /* back to 0 (off) if 1 (on/not locked) */
974 if (!ev.isAltPressed()) {
979 if (!ev.isShiftPressed()) {
980 if (mHardShift == 1) {
984 if (!ev.isShiftPressed() && !ev.isShiftPressed()) {
985 updateMetaKeyStateDisplay();
993 /* append the character to the composing text if the character is not TAB */
994 if (str.string.charAt(0) != '\u0009') {
995 processHardwareKeyboardInputChar(str);
999 commitText(str.string);
1004 } else if (key == KeyEvent.KEYCODE_SPACE) {
1006 processHardwareKeyboardSpaceKey(ev);
1009 } else if (key == KeyEvent.KEYCODE_SYM) {
1010 /* display the symbol list */
1011 initCommitInfoForWatchCursor();
1012 mStatus = commitText(true);
1014 changeEngineMode(ENGINE_MODE_SYMBOL);
1016 updateMetaKeyStateDisplay();
1020 /* Functional key */
1021 if (mComposingText.size(ComposingText.LAYER1) > 0) {
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;
1030 if (mComposingText.size(ComposingText.LAYER1) == 1) {
1034 mComposingText.delete(ComposingText.LAYER1, false);
1037 updateViewStatusForPrediction(true, true);
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);
1045 if (!mEngineState.isConvertState()) {
1047 if (mConverter != null) {
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);
1061 case KeyEvent.KEYCODE_DPAD_LEFT:
1062 if (!isEnableL2Converter()) {
1066 processLeftKeyEvent();
1070 case KeyEvent.KEYCODE_DPAD_RIGHT:
1071 if (!isEnableL2Converter()) {
1072 if (mEngineState.keyboard == EngineState.KEYBOARD_12KEY) {
1076 processRightKeyEvent();
1080 case KeyEvent.KEYCODE_DPAD_CENTER:
1081 case KeyEvent.KEYCODE_ENTER:
1082 if (!isEnglishPrediction()) {
1083 int cursor = mComposingText.getCursor(ComposingText.LAYER1);
1088 initCommitInfoForWatchCursor();
1089 mStatus = commitText(true);
1092 if (isEnglishPrediction()) {
1096 if (mEnableAutoHideKeyboard) {
1097 mInputViewManager.closing();
1102 case KeyEvent.KEYCODE_CALL:
1109 /* if there is no composing string. */
1110 if (mCandidatesViewManager.getCurrentView().isShown()) {
1111 /* displaying relational prediction candidates */
1113 case KeyEvent.KEYCODE_DPAD_LEFT:
1114 if (isEnableL2Converter()) {
1115 /* initialize the converter */
1118 mStatus = STATUS_INPUT_EDIT;
1119 updateViewStatusForPrediction(true, true);
1122 case KeyEvent.KEYCODE_DPAD_RIGHT:
1123 if (isEnableL2Converter()) {
1124 /* initialize the converter */
1127 mStatus = STATUS_INPUT_EDIT;
1128 updateViewStatusForPrediction(true, true);
1132 return processKeyEventNoInputCandidateShown(ev);
1136 case KeyEvent.KEYCODE_DPAD_CENTER:
1137 case KeyEvent.KEYCODE_ENTER:
1138 if (mEnableAutoHideKeyboard) {
1139 mInputViewManager.closing();
1144 case KeyEvent.KEYCODE_BACK:
1146 * If 'BACK' key is pressed when the SW-keyboard is shown
1147 * and the candidates view is not shown, dismiss the SW-keyboard.
1149 if (isInputViewShown()) {
1150 mInputViewManager.closing();
1165 * Handle the space key event from the Hardware keyboard.
1167 * @param ev The space key event
1169 private void processHardwareKeyboardSpaceKey(KeyEvent ev) {
1171 if (ev.isShiftPressed()) {
1172 /* change Japanese <-> English mode */
1175 updateMetaKeyStateDisplay();
1176 if (mEngineState.isEnglish()) {
1177 /* English mode to Japanese mode */
1178 ((DefaultSoftKeyboardJAJP) mInputViewManager).changeKeyMode(DefaultSoftKeyboard.KEYMODE_JA_FULL_NIKO);
1179 mConverter = mConverterJAJP;
1181 /* Japanese mode to English mode */
1182 ((DefaultSoftKeyboardJAJP) mInputViewManager).changeKeyMode(DefaultSoftKeyboard.KEYMODE_JA_HALF_ALPHABET);
1183 mConverter = mConverterEN;
1185 mCandidatesViewManager.clearCandidates();
1187 } else if(ev.isAltPressed()){
1188 /* display the symbol list (G1 specific. same as KEYCODE_SYM) */
1189 if (!mEngineState.isSymbolList()) {
1192 changeEngineMode(ENGINE_MODE_SYMBOL);
1194 updateMetaKeyStateDisplay();
1196 } else if (isEnglishPrediction()) {
1197 /* Auto commit if English mode */
1198 if (mComposingText.size(0) == 0) {
1200 mCandidatesViewManager.clearCandidates();
1203 initCommitInfoForWatchCursor();
1205 commitSpaceJustOne();
1208 mEnableAutoDeleteSpace = false;
1211 /* start consecutive clause conversion if Japanese mode */
1212 if (mComposingText.size(0) == 0) {
1214 mCandidatesViewManager.clearCandidates();
1217 startConvert(EngineState.CONVERT_TYPE_RENBUN);
1223 * Handle the character code from the hardware keyboard except the space key.
1225 * @param str The input character
1227 private void processHardwareKeyboardInputChar(StrSegment str) {
1228 if (isEnableL2Converter()) {
1229 boolean commit = false;
1230 if (mPreConverter == null) {
1231 Matcher m = mEnglishAutoCommitDelimiter.matcher(str.string);
1237 appendStrSegment(str);
1239 appendStrSegment(str);
1240 mPreConverter.convert(mComposingText);
1246 mStatus = STATUS_INPUT;
1247 updateViewStatusForPrediction(true, true);
1250 appendStrSegment(str);
1251 boolean completed = true;
1252 if (mPreConverter != null) {
1253 completed = mPreConverter.convert(mComposingText);
1259 updateViewStatus(ComposingText.LAYER1, false, true);
1264 /** Thread for updating the candidates view */
1265 private void updatePrediction() {
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);
1273 /* normal prediction */
1274 candidates = mConverter.predict(mComposingText, 0, -1);
1278 /* update the candidates view */
1279 if (candidates > 0) {
1280 mHasContinuedPrediction = ((mComposingText.size(ComposingText.LAYER1) == 0)
1281 && !mEngineState.isSymbolList());
1282 mCandidatesViewManager.displayCandidates(mConverter);
1284 mCandidatesViewManager.clearCandidates();
1289 * Handle a left key event.
1291 private void processLeftKeyEvent() {
1292 if (mEngineState.isConvertState()) {
1293 if (mEngineState.isEisuKana()) {
1294 mExactMatchMode = true;
1297 if (1 < mComposingText.getCursor(ComposingText.LAYER1)) {
1298 mComposingText.moveCursor(ComposingText.LAYER1, -1);
1300 } else if (mExactMatchMode) {
1301 mComposingText.moveCursor(ComposingText.LAYER1, -1);
1303 if (isEnglishPrediction()) {
1304 mComposingText.moveCursor(ComposingText.LAYER1, -1);
1306 mExactMatchMode = true;
1310 mCommitCount = 0; /* retry consecutive clause conversion if necessary. */
1311 mStatus = STATUS_INPUT_EDIT;
1312 updateViewStatus(mTargetLayer, true, true);
1316 * Handle a right key event.
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);
1330 if (mEngineState.isEisuKana()) {
1331 mExactMatchMode = true;
1333 composingText.moveCursor(ComposingText.LAYER1, 1);
1336 if (composingText.getCursor(ComposingText.LAYER1)
1337 < composingText.size(ComposingText.LAYER1)) {
1338 composingText.moveCursor(ComposingText.LAYER1, 1);
1342 mCommitCount = 0; /* retry consecutive clause conversion if necessary. */
1343 mStatus = STATUS_INPUT_EDIT;
1345 updateViewStatus(layer, true, true);
1349 * Handle a key event which is not right or left key when the
1350 * composing text is empty and some candidates are shown.
1352 * @param ev A key event
1353 * @return {@code true} if this consumes the event; {@code false} if not.
1355 boolean processKeyEventNoInputCandidateShown(KeyEvent ev) {
1358 switch (ev.getKeyCode()) {
1359 case KeyEvent.KEYCODE_DEL:
1362 case KeyEvent.KEYCODE_ENTER:
1363 case KeyEvent.KEYCODE_DPAD_UP:
1364 case KeyEvent.KEYCODE_DPAD_DOWN:
1365 case KeyEvent.KEYCODE_MENU:
1369 case KeyEvent.KEYCODE_CALL:
1372 case KeyEvent.KEYCODE_DPAD_CENTER:
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);
1390 if (mConverter != null) {
1391 /* initialize the converter */
1394 updateViewStatusForPrediction(true, true);
1399 * Update views and the display of the composing text for predict mode.
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.
1404 private void updateViewStatusForPrediction(boolean updateCandidates, boolean updateEmptyText) {
1405 EngineState state = new EngineState();
1406 state.convertType = EngineState.CONVERT_TYPE_NONE;
1407 updateEngineState(state);
1409 updateViewStatus(ComposingText.LAYER1, updateCandidates, updateEmptyText);
1413 * Update views and the display of the composing text.
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.
1419 private void updateViewStatus(int layer, boolean updateCandidates, boolean updateEmptyText) {
1420 mTargetLayer = layer;
1422 if (updateCandidates) {
1423 updateCandidateView();
1425 /* notice to the input view */
1426 mInputViewManager.onUpdateState(this);
1428 /* set the text for displaying as the composing text */
1429 mDisplayText.clear();
1430 mDisplayText.insert(0, mComposingText.toString(layer));
1432 /* add decoration to the text */
1433 int cursor = mComposingText.getCursor(layer);
1434 if ((mInputConnection != null) && (mDisplayText.length() != 0 || updateEmptyText)) {
1436 int highlightEnd = 0;
1438 if ((mExactMatchMode && (!mEngineState.isEisuKana()))
1439 || (FIX_CURSOR_TEXT_END && isEnglishPrediction()
1440 && (cursor < mComposingText.size(ComposingText.LAYER1)))){
1442 mDisplayText.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor,
1443 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
1444 highlightEnd = cursor;
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;
1451 } else if (layer == ComposingText.LAYER2) {
1452 highlightEnd = mComposingText.toString(layer, 0, 0).length();
1454 /* highlights the first segment */
1455 mDisplayText.setSpan(SPAN_CONVERT_BGCOLOR_HL, 0,
1457 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
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);
1466 /* text color in the highlight */
1467 mDisplayText.setSpan(SPAN_TEXTCOLOR, 0,
1468 mComposingText.toString(layer).length(),
1469 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
1473 mDisplayText.setSpan(SPAN_UNDERLINE, 0, mDisplayText.length(),
1474 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
1476 int displayCursor = mComposingText.toString(layer, 0, cursor - 1).length();
1477 if (FIX_CURSOR_TEXT_END) {
1478 displayCursor = (cursor == 0) ? 0 : 1;
1480 /* update the composing text on the EditView */
1481 mInputConnection.setComposingText(mDisplayText, displayCursor);
1486 * Update the candidates view.
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()) {
1497 mHandler.removeMessages(MSG_PREDICTION);
1498 if (mCandidatesViewManager.getCurrentView().isShown()) {
1499 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
1500 PREDICTION_DELAY_MS_SHOWING_CANDIDATE);
1502 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
1503 PREDICTION_DELAY_MS_1ST);
1506 mHandler.removeMessages(MSG_PREDICTION);
1510 mHandler.removeMessages(MSG_PREDICTION);
1511 mCandidatesViewManager.clearCandidates();
1514 case ComposingText.LAYER2: /* convert */
1515 if (mCommitCount == 0) {
1516 mHandler.removeMessages(MSG_PREDICTION);
1517 mConverter.convert(mComposingText);
1520 int candidates = mConverter.makeCandidateListOf(mCommitCount);
1522 if (candidates != 0) {
1523 mComposingText.setCursor(ComposingText.LAYER2, 1);
1524 mCandidatesViewManager.displayCandidates(mConverter);
1526 mComposingText.setCursor(ComposingText.LAYER1,
1527 mComposingText.toString(ComposingText.LAYER1).length());
1528 mCandidatesViewManager.clearCandidates();
1537 * Commit the displaying composing text.
1539 * @param learn {@code true} to register the committed string to the learning dictionary.
1540 * @return IME's status after commit
1542 private int commitText(boolean learn) {
1543 if (isEnglishPrediction()) {
1544 mComposingText.setCursor(ComposingText.LAYER1,
1545 mComposingText.size(ComposingText.LAYER1));
1548 int layer = mTargetLayer;
1549 int cursor = mComposingText.getCursor(layer);
1553 String tmp = mComposingText.toString(layer, 0, cursor - 1);
1555 if (mConverter != null) {
1557 if (mEngineState.isRenbun()) {
1558 learnWord(0); /* select the top of the clauses */
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);
1571 return commitTextThroughInputConnection(tmp);
1575 * Commit all uncommitted words.
1577 private void commitAllText() {
1578 initCommitInfoForWatchCursor();
1579 if (mEngineState.isConvertState()) {
1580 commitConvertingText();
1582 mComposingText.setCursor(ComposingText.LAYER1,
1583 mComposingText.size(ComposingText.LAYER1));
1584 mStatus = commitText(true);
1592 * @param word A word to commit
1593 * @return IME's status after commit
1595 private int commitText(WnnWord word) {
1596 if (mConverter != null) {
1599 return commitTextThroughInputConnection(word.candidate);
1605 * @param str A string to commit
1607 private void commitText(String str) {
1608 mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length()));
1609 mPrevCommitText.append(str);
1611 mEnableAutoDeleteSpace = true;
1612 updateViewStatusForPrediction(false, false);
1616 * Commit a string through {@link InputConnection}.
1618 * @param string A string to commit
1619 * @return IME's status after commit
1621 private int commitTextThroughInputConnection(String string) {
1622 int layer = mTargetLayer;
1624 mInputConnection.commitText(string, (FIX_CURSOR_TEXT_END ? 1 : string.length()));
1625 mPrevCommitText.append(string);
1628 int cursor = mComposingText.getCursor(layer);
1630 mComposingText.deleteStrSegment(layer, 0, mComposingText.getCursor(layer) - 1);
1631 mComposingText.setCursor(layer, mComposingText.size(layer));
1633 mExactMatchMode = false;
1636 if ((layer == ComposingText.LAYER2) && (mComposingText.size(layer) == 0)) {
1637 layer = 1; /* for connected prediction */
1640 boolean commited = autoCommitEnglish();
1641 mEnableAutoDeleteSpace = true;
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);
1649 updateViewStatusForPrediction(!commited, false);
1652 if (mComposingText.size(ComposingText.LAYER0) == 0) {
1655 return STATUS_INPUT_EDIT;
1660 * Returns whether it is English prediction mode or not.
1662 * @return {@code true} if it is English prediction mode; otherwise, {@code false}.
1664 private boolean isEnglishPrediction() {
1665 return (mEngineState.isEnglish() && isEnableL2Converter());
1669 * Change the conversion engine and the letter converter(Romaji-to-Kana converter).
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
1675 private void changeEngineMode(int mode) {
1676 EngineState state = new EngineState();
1679 case ENGINE_MODE_OPT_TYPE_QWERTY:
1680 state.keyboard = EngineState.KEYBOARD_QWERTY;
1681 updateEngineState(state);
1685 case ENGINE_MODE_OPT_TYPE_12KEY:
1686 state.keyboard = EngineState.KEYBOARD_12KEY;
1687 updateEngineState(state);
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 */
1697 startConvert(EngineState.CONVERT_TYPE_EISU_KANA);
1701 case ENGINE_MODE_SYMBOL:
1702 if (mEnableSymbolList && !mDirectInputMode) {
1703 state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_SYMBOL;
1704 updateEngineState(state);
1705 updateViewStatusForPrediction(true, true);
1709 case ENGINE_MODE_DOCOMOSYMBOL:
1710 if (mEnableSymbolList && !mDirectInputMode) {
1711 state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL;
1712 updateEngineState(state);
1713 updateViewStatusForPrediction(true, true);
1722 state = new EngineState();
1723 state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
1724 updateEngineState(state);
1726 state = new EngineState();
1728 case NicoWnnEvent.Mode.DIRECT:
1729 /* Full/Half-width number or Full-width alphabet */
1731 mPreConverter = null;
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;
1742 case NicoWnnEvent.Mode.NO_LV2_CONV:
1744 mPreConverter = mPreConverterHiragana;
1747 case ENGINE_MODE_FULL_KATAKANA:
1749 mPreConverter = mPreConverterFullKatakana;
1752 case ENGINE_MODE_HALF_KATAKANA:
1754 mPreConverter = mPreConverterHalfKatakana;
1758 /* HIRAGANA input mode */
1759 state.dictionarySet = EngineState.DICTIONARYSET_JP;
1760 updateEngineState(state);
1761 mConverter = mConverterJAJP;
1762 mPreConverter = mPreConverterHiragana;
1766 mPreConverterBack = mPreConverter;
1767 mConverterBack = mConverter;
1771 * Update the conversion engine's state.
1773 * @param state Engine's state to be updated
1775 private void updateEngineState(EngineState state) {
1776 EngineState myState = mEngineState;
1779 if ((state.dictionarySet != EngineState.INVALID)
1780 && (myState.dictionarySet != state.dictionarySet)) {
1782 switch (state.dictionarySet) {
1783 case EngineState.DICTIONARYSET_EN:
1784 setDictionary(NicoWnnEngineJAJP.DIC_LANG_EN);
1787 case EngineState.DICTIONARYSET_JP:
1789 setDictionary(NicoWnnEngineJAJP.DIC_LANG_JP);
1792 myState.dictionarySet = state.dictionarySet;
1795 /* update keyboard setting */
1796 if (state.keyboard == EngineState.INVALID) {
1797 state.keyboard = myState.keyboard;
1801 /* type of conversion */
1802 if ((state.convertType != EngineState.INVALID)
1803 && (myState.convertType != state.convertType)) {
1805 switch (state.convertType) {
1806 case EngineState.CONVERT_TYPE_NONE:
1807 setDictionary(mPrevDictionarySet);
1810 case EngineState.CONVERT_TYPE_EISU_KANA:
1811 setDictionary(NicoWnnEngineJAJP.DIC_LANG_JP_EISUKANA);
1814 case EngineState.CONVERT_TYPE_RENBUN:
1816 setDictionary(NicoWnnEngineJAJP.DIC_LANG_JP);
1819 myState.convertType = state.convertType;
1822 /* temporary dictionary */
1823 if (state.temporaryMode != EngineState.INVALID) {
1825 switch (state.temporaryMode) {
1826 case EngineState.TEMPORARY_DICTIONARY_MODE_NONE:
1827 if (myState.temporaryMode != EngineState.TEMPORARY_DICTIONARY_MODE_NONE) {
1828 setDictionary(mPrevDictionarySet);
1830 mPreConverter = mPreConverterBack;
1831 mConverter = mConverterBack;
1832 mDisableAutoCommitEnglishMask &= ~AUTO_COMMIT_ENGLISH_SYMBOL;
1836 case EngineState.TEMPORARY_DICTIONARY_MODE_SYMBOL:
1837 if (++mCurrentSymbol >= SYMBOL_LISTS.length) {
1840 if (mEnableSymbolListNonHalf) {
1841 mConverterSymbolEngineBack.setDictionary(SYMBOL_LISTS[mCurrentSymbol]);
1843 mConverterSymbolEngineBack.setDictionary(SymbolList.SYMBOL_ENGLISH);
1845 mConverter = mConverterSymbolEngineBack;
1846 mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_SYMBOL;
1850 case EngineState.TEMPORARY_DICTIONARY_MODE_DOCOMOSYMBOL:
1851 mConverterSymbolEngineBack.setDictionary(SymbolList.SYMBOL_DOCOMO_EMOJI);
1852 mConverter = mConverterSymbolEngineBack;
1853 mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_SYMBOL;
1860 myState.temporaryMode = state.temporaryMode;
1863 /* preference dictionary */
1864 if ((state.preferenceDictionary != EngineState.INVALID)
1865 && (myState.preferenceDictionary != state.preferenceDictionary)) {
1867 myState.preferenceDictionary = state.preferenceDictionary;
1868 setDictionary(mPrevDictionarySet);
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);
1879 case EngineState.KEYBOARD_QWERTY:
1881 mConverterJAJP.setKeyboardType(NicoWnnEngineJAJP.KEYBOARD_QWERTY);
1882 if (mEnableSpellCorrection) {
1883 mConverterEN.setDictionary(NicoWnnEngineEN.DICT_FOR_CORRECT_MISTYPE);
1885 mConverterEN.setDictionary(NicoWnnEngineEN.DICT_DEFAULT);
1889 myState.keyboard = state.keyboard;
1894 * Set dictionaries to be used.
1896 * @param mode Definition of dictionaries
1898 private void setDictionary(int mode) {
1902 case NicoWnnEngineJAJP.DIC_LANG_JP:
1904 switch (mEngineState.preferenceDictionary) {
1905 case EngineState.PREFERENCE_DICTIONARY_PERSON_NAME:
1906 target = NicoWnnEngineJAJP.DIC_LANG_JP_PERSON_NAME;
1908 case EngineState.PREFERENCE_DICTIONARY_POSTAL_ADDRESS:
1909 target = NicoWnnEngineJAJP.DIC_LANG_JP_POSTAL_ADDRESS;
1917 case NicoWnnEngineJAJP.DIC_LANG_EN:
1919 switch (mEngineState.preferenceDictionary) {
1920 case EngineState.PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI:
1921 target = NicoWnnEngineJAJP.DIC_LANG_EN_EMAIL_ADDRESS;
1934 case NicoWnnEngineJAJP.DIC_LANG_JP:
1935 case NicoWnnEngineJAJP.DIC_LANG_EN:
1936 mPrevDictionarySet = mode;
1942 mConverterJAJP.setDictionary(target);
1946 * Handle a toggle key input event.
1948 * @param table Table of toggle characters
1950 private void processSoftKeyboardToggleChar(String[] table) {
1951 if (table == null) {
1955 commitConvertingText();
1957 boolean toggled = false;
1958 if ((mStatus & ~STATUS_CANDIDATE_FULL) == STATUS_INPUT) {
1959 int cursor = mComposingText.getCursor(ComposingText.LAYER1);
1961 String prevChar = mComposingText.getStrSegment(ComposingText.LAYER1,
1963 String c = searchToggleCharacter(prevChar, table, false);
1965 mComposingText.delete(ComposingText.LAYER1, false);
1966 appendStrSegment(new StrSegment(c));
1973 if (!isEnableL2Converter()) {
1977 String str = table[0];
1980 && isEnglishPrediction()
1981 && (getShiftKeyState(getCurrentInputEditorInfo()) == 1)) {
1983 char top = table[0].charAt(0);
1984 if (Character.isLowerCase(top)) {
1985 str = Character.toString(Character.toUpperCase(top));
1988 appendStrSegment(new StrSegment(str));
1991 mStatus = STATUS_INPUT;
1993 updateViewStatusForPrediction(true, true);
1997 * Handle character input from the software keyboard without listing candidates.
1999 * @param chars The input character(s)
2001 private void processSoftKeyboardCodeWithoutConversion(char[] chars) {
2002 if (chars == null) {
2006 ComposingText text = mComposingText;
2007 appendStrSegment(new StrSegment(chars));
2009 if (!isAlphabetLast(text.toString(ComposingText.LAYER1))) {
2010 /* commit if the input character is not alphabet */
2013 boolean completed = mPreConverter.convert(text);
2017 mStatus = STATUS_INPUT;
2018 updateViewStatusForPrediction(true, true);
2024 * Handle character input from the software keyboard.
2026 * @param chars The input character(s)
2028 private void processSoftKeyboardCode(char[] chars) {
2029 if (chars == null) {
2033 if ((chars[0] == ' ') || (chars[0] == '\u3000' /* Full-width space */)) {
2034 if (mComposingText.size(0) == 0) {
2035 mCandidatesViewManager.clearCandidates();
2036 commitText(new String(chars));
2039 if (isEnglishPrediction()) {
2040 initCommitInfoForWatchCursor();
2042 commitSpaceJustOne();
2045 startConvert(EngineState.CONVERT_TYPE_RENBUN);
2048 mEnableAutoDeleteSpace = false;
2050 commitConvertingText();
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)) {
2057 Matcher m = mEnglishAutoCommitDelimiter.matcher(new String(chars));
2066 appendStrSegment(new StrSegment(chars));
2069 appendStrSegment(new StrSegment(chars));
2070 if (mPreConverter != null) {
2071 mPreConverter.convert(mComposingText);
2072 mStatus = STATUS_INPUT;
2074 updateViewStatusForPrediction(true, true);
2080 * Start consecutive clause conversion or EISU-KANA conversion mode.
2082 * @param convertType The conversion type({@code EngineState.CONVERT_TYPE_*})
2084 private void startConvert(int convertType) {
2085 if (!isEnableL2Converter()) {
2089 if (mEngineState.convertType != convertType) {
2090 /* adjust the cursor position */
2091 if (!mExactMatchMode) {
2092 if (convertType == EngineState.CONVERT_TYPE_RENBUN) {
2094 mComposingText.setCursor(ComposingText.LAYER1, 0);
2096 if (mEngineState.isRenbun()) {
2097 /* EISU-KANA conversion specifying the position of the segment if previous mode is conversion mode */
2098 mExactMatchMode = true;
2100 /* specify all range */
2101 mComposingText.setCursor(ComposingText.LAYER1,
2102 mComposingText.size(ComposingText.LAYER1));
2107 if (convertType == EngineState.CONVERT_TYPE_RENBUN) {
2108 /* clears variables for the prediction */
2109 mExactMatchMode = false;
2111 /* clears variables for the convert */
2115 if (convertType == EngineState.CONVERT_TYPE_EISU_KANA) {
2116 layer = ComposingText.LAYER1;
2118 layer = ComposingText.LAYER2;
2121 EngineState state = new EngineState();
2122 state.convertType = convertType;
2123 updateEngineState(state);
2125 updateViewStatus(layer, true, true);
2130 * Auto commit a word in English (on half-width alphabet mode).
2132 * @return {@code true} if auto-committed; otherwise, {@code false}.
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);
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);
2147 mHandler.removeMessages(MSG_PREDICTION);
2148 mCandidatesViewManager.clearCandidates();
2156 * Insert a white space if the previous character is not a white space.
2158 private void commitSpaceJustOne() {
2159 CharSequence seq = mInputConnection.getTextBeforeCursor(1, 0);
2160 if (seq.charAt(0) != ' ') {
2166 * Get the shift key state from the editor.
2168 * @param editor The editor
2169 * @return State ID of the shift key (0:off, 1:on)
2171 protected int getShiftKeyState(EditorInfo editor) {
2172 return (getCurrentInputConnection().getCursorCapsMode(editor.inputType) == 0) ? 0 : 1;
2176 * Display current meta-key state.
2178 private void updateMetaKeyStateDisplay() {
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;
2199 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
2201 ((DefaultSoftKeyboard) mInputViewManager).updateIndicator(mode);
2205 * Memory a selected word.
2207 * @param word A selected word
2209 private void learnWord(WnnWord word) {
2210 if (mEnableLearning && word != null) {
2211 mConverter.learn(word);
2216 * Memory a clause which is generated by consecutive clause conversion.
2218 * @param index Index of a clause
2220 private void learnWord(int index) {
2221 ComposingText composingText = mComposingText;
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);
2228 String stroke = composingText.toString(ComposingText.LAYER1, seg.from, seg.to);
2229 mConverter.learn(new WnnWord(seg.string, stroke));
2235 * Fits an editor info.
2237 * @param preferences The preference data.
2238 * @param info The editor info.
2240 private void fitInputType(SharedPreferences preference, EditorInfo info) {
2241 if (info.inputType == EditorInfo.TYPE_NULL) {
2242 mDirectInputMode = true;
2246 mEnableLearning = preference.getBoolean("opt_enable_learning", true);
2247 mEnablePrediction = preference.getBoolean("opt_prediction", true);
2248 mEnableSpellCorrection = preference.getBoolean("opt_spell_correction", true);
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);
2257 //mEnableAutoInsertSpace = true;
2258 mEnableAutoHideKeyboard = false;
2260 mEnableAutoInsertSpace = preference.getBoolean("is_skip_space", true);
2264 switch (info.inputType & EditorInfo.TYPE_MASK_CLASS) {
2265 case EditorInfo.TYPE_CLASS_NUMBER:
2266 case EditorInfo.TYPE_CLASS_DATETIME:
2267 mEnableConverter = false;
2270 case EditorInfo.TYPE_CLASS_PHONE:
2271 mEnableSymbolList = false;
2272 mEnableConverter = false;
2275 case EditorInfo.TYPE_CLASS_TEXT:
2277 switch (info.inputType & EditorInfo.TYPE_MASK_VARIATION) {
2278 case EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME:
2279 preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_PERSON_NAME;
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;
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;
2299 case EditorInfo.TYPE_TEXT_VARIATION_URI:
2300 mEnableAutoInsertSpace = false;
2301 //mDisableAutoCommitEnglishMask |= AUTO_COMMIT_ENGLISH_OFF;
2302 //preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_EMAIL_ADDRESS_URI;
2305 case EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS:
2306 preferenceDictionary = EngineState.PREFERENCE_DICTIONARY_POSTAL_ADDRESS;
2309 case EditorInfo.TYPE_TEXT_VARIATION_PHONETIC:
2310 mEnableLearning = false;
2311 mEnableConverter = false;
2312 mEnableSymbolList = false;
2324 if (mFilter.filter == 0) {
2325 mConverterEN.setFilter(null);
2326 mConverterJAJP.setFilter(null);
2328 mConverterEN.setFilter(mFilter);
2329 mConverterJAJP.setFilter(mFilter);
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();
2339 checkTutorial(info.privateImeOptions);
2343 * Append a {@link StrSegment} to the composing text
2345 * If the length of the composing text exceeds
2346 * {@code LIMIT_INPUT_NUMBER}, the appending operation is ignored.
2348 * @param str Input segment
2350 private void appendStrSegment(StrSegment str) {
2351 ComposingText composingText = mComposingText;
2353 if (composingText.size(ComposingText.LAYER1) >= LIMIT_INPUT_NUMBER) {
2354 return; /* do nothing */
2356 composingText.insertStrSegment(ComposingText.LAYER0, ComposingText.LAYER1, str);
2361 * Commit the consecutive clause conversion.
2363 private void commitConvertingText() {
2364 if (mEngineState.isConvertState()) {
2365 int size = mComposingText.size(ComposingText.LAYER2);
2366 for (int i = 0; i < size; i++) {
2370 String text = mComposingText.toString(ComposingText.LAYER2);
2371 mInputConnection.commitText(text, (FIX_CURSOR_TEXT_END ? 1 : text.length()));
2372 mPrevCommitText.append(text);
2379 * Initialize the screen displayed by IME
2381 private void initializeScreen() {
2382 if (mComposingText.size(ComposingText.LAYER0) != 0) {
2383 mInputConnection.setComposingText("", 0);
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();
2393 mInputViewManager.onUpdateState(this);
2395 EngineState state = new EngineState();
2396 state.temporaryMode = EngineState.TEMPORARY_DICTIONARY_MODE_NONE;
2397 updateEngineState(state);
2401 * Whether the tail of the string is alphabet or not.
2403 * @param str The string
2404 * @return {@code true} if the tail is alphabet; {@code false} if otherwise.
2406 private boolean isAlphabetLast(String str) {
2407 Matcher m = ENGLISH_CHARACTER_LAST.matcher(str);
2411 /** @see com.hiroshica.android.input.nicownn2.NicoWnn#onFinishInput */
2412 @Override public void onFinishInput() {
2413 if (mInputConnection != null) {
2416 super.onFinishInput();
2420 * Check whether or not the converter is active.
2422 * @return {@code true} if the converter is active.
2424 private boolean isEnableL2Converter() {
2425 if (mConverter == null || !mEnableConverter) {
2429 if (mEngineState.isEnglish() && !mEnablePrediction) {
2437 * Handling KeyEvent(KEYUP)
2439 * This method is called from {@link #onEvent()}.
2441 * @param ev An up key event
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){
2448 mShiftPressing = true;
2449 updateMetaKeyStateDisplay();
2453 if(key == KeyEvent.KEYCODE_ALT_LEFT || key == KeyEvent.KEYCODE_ALT_RIGHT){
2455 mAltPressing = true;
2456 updateMetaKeyStateDisplay();
2462 * Initialize the committed text's information.
2464 private void initCommitInfoForWatchCursor() {
2465 if (!isEnableL2Converter()) {
2469 mCommitStartCursor = mComposingStartCursor;
2470 mPrevCommitText.delete(0, mPrevCommitText.length());
2474 * Clear the commit text's info.
2475 * @return {@code true}:cleared, {@code false}:has already cleared.
2477 private boolean clearCommitInfo() {
2478 if (mCommitStartCursor < 0) {
2482 mCommitStartCursor = -1;
2487 * Verify the commit text.
2489 private void checkCommitInfo() {
2490 if (mCommitStartCursor < 0) {
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;
2504 * Check and start the tutorial if it is the tutorial mode.
2506 * @param privateImeOptions IME's options
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()) {
2522 * Start the tutorial
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);
2531 DefaultSoftKeyboardJAJP inputManager = ((DefaultSoftKeyboardJAJP) mInputViewManager);
2532 View v = inputManager.getKeyboardView();
2533 v.setOnTouchListener(new View.OnTouchListener() {
2534 public boolean onTouch(View v, MotionEvent event) {
2537 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
2541 * Close the tutorial
2543 public void tutorialDone() {
2547 /** @see NicoWnn#close */
2548 @Override protected void close() {
2549 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLOSE), 0);
2553 * Break the sequence of words.
2555 private void breakSequence() {
2556 mEnableAutoDeleteSpace = false;
2557 mConverterJAJP.breakSequence();
2558 mConverterEN.breakSequence();