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