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