OpenWnnEN.java revision 77ffa9b0b986a2d70143f63cdaa8451bf1674f84
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 17package jp.co.omronsoft.openwnn; 18 19import jp.co.omronsoft.openwnn.EN.*; 20import android.content.SharedPreferences; 21import android.content.Context; 22import android.content.res.Configuration; 23import android.inputmethodservice.InputMethodService; 24import android.os.Bundle; 25import android.os.Handler; 26import android.preference.PreferenceManager; 27import android.text.SpannableStringBuilder; 28import android.text.Spanned; 29import android.text.method.MetaKeyKeyListener; 30import android.text.style.BackgroundColorSpan; 31import android.text.style.CharacterStyle; 32import android.text.style.UnderlineSpan; 33import android.util.Log; 34import android.view.KeyCharacterMap; 35import android.view.KeyEvent; 36import android.view.View; 37import android.view.inputmethod.EditorInfo; 38 39/** 40 * The OpenWnn English IME class. 41 * 42 * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD. All Rights Reserved. 43 */ 44public class OpenWnnEN extends OpenWnn { 45 /** A space character */ 46 private static final char[] SPACE = {' '}; 47 48 /** Character style of underline */ 49 private static final CharacterStyle SPAN_UNDERLINE = new UnderlineSpan(); 50 /** Highlight color style for the selected string */ 51 private static final CharacterStyle SPAN_EXACT_BGCOLOR_HL = new BackgroundColorSpan(0xFF66CDAA); 52 /** Highlight color style for the composing text */ 53 private static final CharacterStyle SPAN_REMAIN_BGCOLOR_HL = new BackgroundColorSpan(0xFFF0FFFF); 54 55 /** A private area code(ALT+SHIFT+X) to be ignore (G1 specific). */ 56 private static final int PRIVATE_AREA_CODE = 61184; 57 /** Never move cursor in to the composing text (adapting to IMF's specification change) */ 58 private static final boolean FIX_CURSOR_TEXT_END = true; 59 60 /** Whether using Emoji or not */ 61 private static final boolean ENABLE_EMOJI_LIMITATION = true; 62 63 /** Spannable string for the composing text */ 64 protected SpannableStringBuilder mDisplayText; 65 66 /** Handler for drawing the candidates view */ 67 private Handler mDelayUpdateHandler; 68 /** Characters treated as a separator */ 69 private String mWordSeparators; 70 /** Previous event's code */ 71 private int mPreviousEventCode; 72 73 /** Array of words from the user dictionary */ 74 private WnnWord[] mUserDictionaryWords = null; 75 76 /** The converter for English prediction/spell correction */ 77 private OpenWnnEngineEN mConverterEN; 78 /** The symbol list generator */ 79 private SymbolList mSymbolList; 80 /** Whether it is displaying symbol list */ 81 private boolean mSymbolMode; 82 /** Whether prediction is enabled */ 83 private boolean mOptPrediction; 84 /** Whether spell correction is enabled */ 85 private boolean mOptSpellCorrection; 86 /** Whether learning is enabled */ 87 private boolean mOptLearning; 88 89 /** SHIFT key state */ 90 private int mHardShift; 91 /** SHIFT key state (pressing) */ 92 private boolean mShiftPressing; 93 /** ALT key state */ 94 private int mHardAlt; 95 /** ALT key state (pressing) */ 96 private boolean mAltPressing; 97 98 /** Instance of this service */ 99 private static OpenWnnEN mSelf = null; 100 101 /** Shift lock toggle definition */ 102 private static final int[] mShiftKeyToggle = {0, MetaKeyKeyListener.META_SHIFT_ON, MetaKeyKeyListener.META_CAP_LOCKED}; 103 /** Alt lock toggle definition */ 104 private static final int[] mAltKeyToggle = {0, MetaKeyKeyListener.META_ALT_ON, MetaKeyKeyListener.META_ALT_LOCKED}; 105 /** Auto caps mode */ 106 private boolean mAutoCaps = false; 107 108 private CandidateFilter mFilter; 109 110 /** 111 * Constructor 112 */ 113 public OpenWnnEN() { 114 super(); 115 mSelf = this; 116 117 /* used by OpenWnn */ 118 mComposingText = new ComposingText(); 119 mCandidatesViewManager = new TextCandidatesViewManager(-1); 120 mInputViewManager = new DefaultSoftKeyboardEN(); 121 mConverterEN = new OpenWnnEngineEN("/data/data/jp.co.omronsoft.openwnn/writableEN.dic"); 122 mConverter = mConverterEN; 123 mFilter = new CandidateFilter(); 124 mSymbolList = null; 125 126 /* etc */ 127 mDisplayText = new SpannableStringBuilder(); 128 mAutoHideMode = false; 129 mDelayUpdateHandler = new Handler(); 130 mSymbolMode = false; 131 mOptPrediction = true; 132 mOptSpellCorrection = true; 133 mOptLearning = true; 134 } 135 136 /** 137 * Constructor 138 * 139 * @param context The context 140 */ 141 public OpenWnnEN(Context context) { 142 this(); 143 attachBaseContext(context); 144 } 145 /** 146 * Get the instance of this service. 147 * <br> 148 * Before using this method, the constructor of this service must be invoked. 149 * 150 * @return The instance of this object 151 */ 152 public static OpenWnnEN getInstance() { 153 return mSelf; 154 } 155 156 /** 157 * Insert a character into the composing text. 158 * 159 * @param chars A array of character 160 */ 161 private void insertCharToComposingText(char[] chars) { 162 StrSegment seg = new StrSegment(chars); 163 164 if (chars[0] == SPACE[0] || chars[0] == '\u0009') { 165 /* if the character is a space, commit the composing text */ 166 commitText(1); 167 commitText(seg.string); 168 mComposingText.clear(); 169 } else if (mWordSeparators.contains(seg.string)) { 170 /* if the character is a separator, remove an auto-inserted space and commit the composing text. */ 171 if (mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) { 172 mInputConnection.deleteSurroundingText(1, 0); 173 } 174 commitText(1); 175 commitText(seg.string); 176 mComposingText.clear(); 177 } else { 178 mComposingText.insertStrSegment(0, 1, seg); 179 updateComposingText(1); 180 } 181 } 182 183 /** 184 * Insert a character into the composing text. 185 * 186 * @param charCode A character code 187 * @return {@code true} if success; {@code false} if an error occurs. 188 */ 189 private boolean insertCharToComposingText(int charCode) { 190 if (charCode == 0) { 191 return false; 192 } 193 insertCharToComposingText(Character.toChars(charCode)); 194 return true; 195 } 196 197 /** 198 * Get the shift key state from the editor. 199 * 200 * @param editor Editor 201 * 202 * @return State ID of the shift key (0:off, 1:on) 203 */ 204 protected int getShiftKeyState(EditorInfo editor) { 205 return (getCurrentInputConnection().getCursorCapsMode(editor.inputType) == 0) ? 0 : 1; 206 } 207 208 /** 209 * Set the mode of the symbol list. 210 * 211 * @param mode {@code SymbolList.SYMBOL_ENGLISH} or {@code null}. 212 */ 213 private void setSymbolMode(String mode) { 214 if (mode != null) { 215 mDelayUpdateHandler.removeCallbacks(updatePredictionRunnable); 216 mSymbolMode = true; 217 mSymbolList.setDictionary(mode); 218 mConverter = mSymbolList; 219 } else { 220 if (!mSymbolMode) { 221 return; 222 } 223 mDelayUpdateHandler.removeCallbacks(updatePredictionRunnable); 224 mSymbolMode = false; 225 mConverter = mConverterEN; 226 } 227 } 228 229 /*********************************************************************** 230 * InputMethodServer 231 ***********************************************************************/ 232 /** @see jp.co.omronsoft.openwnn.OpenWnn#onCreate */ 233 @Override public void onCreate() { 234 super.onCreate(); 235 mWordSeparators = getResources().getString(R.string.en_word_separators); 236 237 if (mSymbolList == null) { 238 mSymbolList = new SymbolList(this, SymbolList.LANG_EN); 239 } 240 } 241 242 /** @see jp.co.omronsoft.openwnn.OpenWnn#onCreateInputView */ 243 @Override public View onCreateInputView() { 244 int hiddenState = getResources().getConfiguration().hardKeyboardHidden; 245 boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES); 246 ((DefaultSoftKeyboardEN) mInputViewManager).setHardKeyboardHidden(hidden); 247 ((TextCandidatesViewManager) 248 mCandidatesViewManager).setHardKeyboardHidden(hidden); 249 250 return super.onCreateInputView(); 251 } 252 253 /** @see jp.co.omronsoft.openwnn.OpenWnn#onStartInputView */ 254 @Override public void onStartInputView(EditorInfo attribute, boolean restarting) { 255 super.onStartInputView(attribute, restarting); 256 257 /* initialize views */ 258 mCandidatesViewManager.clearCandidates(); 259 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE); 260 261 mHardShift = 0; 262 mHardAlt = 0; 263 updateMetaKeyStateDisplay(); 264 265 /* load preferences */ 266 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); 267 268 /* auto caps mode */ 269 mAutoCaps = pref.getBoolean("auto_caps", true); 270 271 /* set TextCandidatesViewManager's option */ 272 ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(true); 273 274 /* display status icon */ 275 showStatusIcon(R.drawable.immodeic_half_alphabet); 276 277 /* set prediction & spell correction mode */ 278 mOptPrediction = pref.getBoolean("opt_en_prediction", true); 279 mOptSpellCorrection = pref.getBoolean("opt_en_spell_correction", true); 280 mOptLearning = pref.getBoolean("opt_en_enable_learning", true); 281 282 /* prediction on/off */ 283 switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) { 284 case EditorInfo.TYPE_CLASS_NUMBER: 285 case EditorInfo.TYPE_CLASS_DATETIME: 286 case EditorInfo.TYPE_CLASS_PHONE: 287 mOptPrediction = false; 288 mOptLearning = false; 289 break; 290 291 case EditorInfo.TYPE_CLASS_TEXT: 292 switch (attribute.inputType & EditorInfo.TYPE_MASK_VARIATION) { 293 case EditorInfo.TYPE_TEXT_VARIATION_PASSWORD: 294 case EditorInfo.TYPE_TEXT_VARIATION_PHONETIC: 295 mOptLearning = false; 296 mOptPrediction = false; 297 break; 298 default: 299 break; 300 } 301 } 302 303 /* set engine's mode */ 304 if (mOptSpellCorrection) { 305 mConverterEN.setDictionary(OpenWnnEngineEN.DICT_FOR_CORRECT_MISTYPE); 306 } else { 307 mConverterEN.setDictionary(OpenWnnEngineEN.DICT_DEFAULT); 308 } 309 /* emoji */ 310 if (ENABLE_EMOJI_LIMITATION) { 311 Bundle bundle = attribute.extras; 312 if (bundle != null && bundle.getBoolean("allowEmoji")) { 313 mConverterEN.setFilter(null); 314 } else { 315 mFilter.setFilter(CandidateFilter.FILTER_EMOJI); 316 mConverterEN.setFilter(mFilter); 317 } 318 } else { 319 mConverterEN.setFilter(null); 320 } 321 322 /* doesn't learn any word if it is not prediction mode */ 323 if (!mOptPrediction) { 324 mOptLearning = false; 325 } 326 327 if (mComposingText != null) { 328 mComposingText.clear(); 329 } 330 /* initialize the engine's state */ 331 fitInputType(pref, attribute); 332 } 333 334 /** @see jp.co.omronsoft.openwnn.OpenWnn#onComputeInsets */ 335 @Override public void onComputeInsets(InputMethodService.Insets outInsets) { 336 /* use default value. means; 337 * outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_VISIBLE; 338 */ 339 } 340 341 /** @see jp.co.omronsoft.openwnn.OpenWnn#onUpdateSelection */ 342 @Override public void onUpdateSelection(int oldSelStart, int oldSelEnd, 343 int newSelStart, int newSelEnd, int candidatesStart, 344 int candidatesEnd) { 345 if (mComposingText.size(1) != 0) { 346 updateComposingText(1); 347 } 348 } 349 350 /** @see jp.co.omronsoft.openwnn.OpenWnn#onConfigurationChanged */ 351 @Override public void onConfigurationChanged(Configuration newConfig) { 352 try { 353 super.onConfigurationChanged(newConfig); 354 if (mInputConnection != null) { 355 updateComposingText(1); 356 } 357 } catch (Exception ex) { 358 } 359 } 360 361 /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateFullscreenMode */ 362 @Override public boolean onEvaluateFullscreenMode() { 363 return false; 364 } 365 366 /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateInputViewShown */ 367 @Override public boolean onEvaluateInputViewShown() { 368 return true; 369 } 370 371 /*********************************************************************** 372 * OpenWnn 373 ***********************************************************************/ 374 /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvent */ 375 @Override synchronized public boolean onEvent(OpenWnnEvent ev) { 376 /* handling events which are valid when InputConnection is not active. */ 377 switch (ev.code) { 378 379 case OpenWnnEvent.KEYUP: 380 onKeyUpEvent(ev.keyEvent); 381 return true; 382 383 case OpenWnnEvent.INITIALIZE_LEARNING_DICTIONARY: 384 return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_LEARN ); 385 386 case OpenWnnEvent.INITIALIZE_USER_DICTIONARY: 387 return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_USER ); 388 389 case OpenWnnEvent.LIST_WORDS_IN_USER_DICTIONARY: 390 mUserDictionaryWords = mConverterEN.getUserDictionaryWords( ); 391 return true; 392 393 case OpenWnnEvent.GET_WORD: 394 if( mUserDictionaryWords != null ) { 395 ev.word = mUserDictionaryWords[ 0 ]; 396 for( int i = 0 ; i < mUserDictionaryWords.length-1 ; i++ ) { 397 mUserDictionaryWords[ i ] = mUserDictionaryWords[ i + 1 ]; 398 } 399 mUserDictionaryWords[ mUserDictionaryWords.length-1 ] = null; 400 if( mUserDictionaryWords[ 0 ] == null ) { 401 mUserDictionaryWords = null; 402 } 403 return true; 404 } 405 break; 406 407 case OpenWnnEvent.ADD_WORD: 408 mConverterEN.addWord(ev.word); 409 return true; 410 411 case OpenWnnEvent.DELETE_WORD: 412 mConverterEN.deleteWord(ev.word); 413 return true; 414 415 case OpenWnnEvent.CHANGE_MODE: 416 return false; 417 418 case OpenWnnEvent.CHANGE_INPUT_VIEW: 419 setInputView(onCreateInputView()); 420 return true; 421 422 case OpenWnnEvent.CANDIDATE_VIEW_TOUCH: 423 boolean ret; 424 ret = ((TextCandidatesViewManager)mCandidatesViewManager).onTouchSync(); 425 return ret; 426 427 default: 428 break; 429 } 430 431 dismissPopupKeyboard(); 432 KeyEvent keyEvent = ev.keyEvent; 433 int keyCode = 0; 434 if (keyEvent != null) { 435 keyCode = keyEvent.getKeyCode(); 436 } 437 if (mDirectInputMode) { 438 if (ev.code == OpenWnnEvent.INPUT_SOFT_KEY && mInputConnection != null) { 439 mInputConnection.sendKeyEvent(keyEvent); 440 mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, 441 keyEvent.getKeyCode())); 442 } 443 return false; 444 } 445 446 if (ev.code == OpenWnnEvent.LIST_CANDIDATES_FULL) { 447 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_FULL); 448 return true; 449 } 450 451 boolean ret = false; 452 switch (ev.code) { 453 case OpenWnnEvent.INPUT_CHAR: 454 ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false); 455 EditorInfo edit = getCurrentInputEditorInfo(); 456 if( edit.inputType == EditorInfo.TYPE_CLASS_PHONE){ 457 commitText(new String(ev.chars)); 458 }else{ 459 setSymbolMode(null); 460 insertCharToComposingText(ev.chars); 461 ret = true; 462 mPreviousEventCode = ev.code; 463 } 464 break; 465 466 case OpenWnnEvent.INPUT_KEY: 467 keyCode = ev.keyEvent.getKeyCode(); 468 /* update shift/alt state */ 469 switch (keyCode) { 470 case KeyEvent.KEYCODE_ALT_LEFT: 471 case KeyEvent.KEYCODE_ALT_RIGHT: 472 if (ev.keyEvent.getRepeatCount() == 0) { 473 if (++mHardAlt > 2) { mHardAlt = 0; } 474 } 475 mAltPressing = true; 476 updateMetaKeyStateDisplay(); 477 return true; 478 479 case KeyEvent.KEYCODE_SHIFT_LEFT: 480 case KeyEvent.KEYCODE_SHIFT_RIGHT: 481 if (ev.keyEvent.getRepeatCount() == 0) { 482 if (++mHardShift > 2) { mHardShift = 0; } 483 } 484 mShiftPressing = true; 485 updateMetaKeyStateDisplay(); 486 return true; 487 } 488 setSymbolMode(null); 489 updateComposingText(1); 490 /* handle other key event */ 491 ret = processKeyEvent(ev.keyEvent); 492 mPreviousEventCode = ev.code; 493 break; 494 495 case OpenWnnEvent.INPUT_SOFT_KEY: 496 setSymbolMode(null); 497 updateComposingText(1); 498 ret = processKeyEvent(ev.keyEvent); 499 if (!ret) { 500 mInputConnection.sendKeyEvent(ev.keyEvent); 501 mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, ev.keyEvent.getKeyCode())); 502 ret = true; 503 } 504 mPreviousEventCode = ev.code; 505 break; 506 507 case OpenWnnEvent.SELECT_CANDIDATE: 508 if (mSymbolMode) { 509 commitText(ev.word, false); 510 } else { 511 if (mWordSeparators.contains(ev.word.candidate) && 512 mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) { 513 mInputConnection.deleteSurroundingText(1, 0); 514 } 515 commitText(ev.word, true); 516 } 517 mComposingText.clear(); 518 mPreviousEventCode = ev.code; 519 updateComposingText(1); 520 break; 521 522 case OpenWnnEvent.LIST_SYMBOLS: 523 commitText(1); 524 mComposingText.clear(); 525 setSymbolMode(SymbolList.SYMBOL_ENGLISH); 526 updateComposingText(1); 527 break; 528 529 default: 530 break; 531 } 532 533 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) { 534 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL); 535 } 536 537 return ret; 538 } 539 540 /*********************************************************************** 541 * OpenWnnEN 542 ***********************************************************************/ 543 /** 544 * Handling KeyEvent 545 * <br> 546 * This method is called from {@link #onEvent()}. 547 * 548 * @param ev A key event 549 * @return {@code true} if the event is processed in this method; {@code false} if the event is not processed in this method 550 */ 551 private boolean processKeyEvent(KeyEvent ev) { 552 553 int key = ev.getKeyCode(); 554 EditorInfo edit = getCurrentInputEditorInfo(); 555 /* keys which produce a glyph */ 556 if (ev.isPrintingKey()) { 557 /* do nothing if the character is not able to display or the character is dead key */ 558 if ((mHardShift > 0 && mHardAlt > 0) || (ev.isAltPressed() && ev.isShiftPressed())) { 559 int charCode = ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON | MetaKeyKeyListener.META_ALT_ON); 560 if (charCode == 0 || (charCode & KeyCharacterMap.COMBINING_ACCENT) != 0 || charCode == PRIVATE_AREA_CODE) { 561 if(mHardShift == 1){ 562 mShiftPressing = false; 563 } 564 if(mHardAlt == 1){ 565 mAltPressing = false; 566 } 567 if(!ev.isAltPressed()){ 568 if (mHardAlt == 1) { 569 mHardAlt = 0; 570 } 571 } 572 if(!ev.isShiftPressed()){ 573 if (mHardShift == 1) { 574 mHardShift = 0; 575 } 576 } 577 if(!ev.isShiftPressed() && !ev.isAltPressed()){ 578 updateMetaKeyStateDisplay(); 579 } 580 return true; 581 } 582 } 583 584 ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false); 585 586 /* get the key character */ 587 if (mHardShift== 0 && mHardAlt == 0) { 588 /* no meta key is locked */ 589 int shift = (mAutoCaps) ? getShiftKeyState(edit) : 0; 590 if (shift != mHardShift && (key >= KeyEvent.KEYCODE_A && key <= KeyEvent.KEYCODE_Z)) { 591 /* handling auto caps for a alphabet character */ 592 insertCharToComposingText(ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON)); 593 } else { 594 insertCharToComposingText(ev.getUnicodeChar()); 595 } 596 } else { 597 insertCharToComposingText(ev.getUnicodeChar(mShiftKeyToggle[mHardShift] 598 | mAltKeyToggle[mHardAlt])); 599 if(mHardShift == 1){ 600 mShiftPressing = false; 601 } 602 if(mHardAlt == 1){ 603 mAltPressing = false; 604 } 605 /* back to 0 (off) if 1 (on/not locked) */ 606 if(!ev.isAltPressed()){ 607 if (mHardAlt == 1) { 608 mHardAlt = 0; 609 } 610 } 611 if(!ev.isShiftPressed()){ 612 if (mHardShift == 1) { 613 mHardShift = 0; 614 } 615 } 616 if(!ev.isShiftPressed() && !ev.isAltPressed()){ 617 updateMetaKeyStateDisplay(); 618 } 619 } 620 621 if (edit.inputType == EditorInfo.TYPE_CLASS_PHONE) { 622 commitText(1); 623 mComposingText.clear(); 624 return true; 625 } 626 return true; 627 628 } else if (key == KeyEvent.KEYCODE_SPACE) { 629 if (ev.isAltPressed()) { 630 /* display the symbol list (G1 specific. same as KEYCODE_SYM) */ 631 commitText(1); 632 mComposingText.clear(); 633 setSymbolMode(SymbolList.SYMBOL_ENGLISH); 634 updateComposingText(1); 635 mHardAlt = 0; 636 updateMetaKeyStateDisplay(); 637 } else { 638 insertCharToComposingText(SPACE); 639 } 640 return true; 641 } else if (key == KeyEvent.KEYCODE_SYM) { 642 /* display the symbol list */ 643 commitText(1); 644 mComposingText.clear(); 645 setSymbolMode(SymbolList.SYMBOL_ENGLISH); 646 updateComposingText(1); 647 mHardAlt = 0; 648 updateMetaKeyStateDisplay(); 649 } 650 651 652 /* Functional key */ 653 if (mComposingText.size(1) > 0) { 654 switch (key) { 655 case KeyEvent.KEYCODE_DEL: 656 mComposingText.delete(1, false); 657 updateComposingText(1); 658 return true; 659 660 case KeyEvent.KEYCODE_BACK: 661 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) { 662 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL); 663 } else { 664 mComposingText.clear(); 665 updateComposingText(1); 666 } 667 return true; 668 669 case KeyEvent.KEYCODE_DPAD_LEFT: 670 mComposingText.moveCursor(1, -1); 671 updateComposingText(1); 672 return true; 673 674 case KeyEvent.KEYCODE_DPAD_RIGHT: 675 mComposingText.moveCursor(1, 1); 676 updateComposingText(1); 677 return true; 678 679 case KeyEvent.KEYCODE_ENTER: 680 case KeyEvent.KEYCODE_DPAD_CENTER: 681 commitText(1); 682 mComposingText.clear(); 683 return true; 684 685 default: 686 break; 687 } 688 } 689 690 return false; 691 } 692 693 /** 694 * Runnable for a thread getting and displaying candidates. 695 */ 696 private final Runnable updatePredictionRunnable = new Runnable() { 697 public void run() { 698 int candidates = 0; 699 if (mConverter != null) { 700 /* normal prediction */ 701 candidates = mConverter.predict(mComposingText, 0, -1); 702 } 703 /* update the candidates view */ 704 if (candidates > 0) { 705 mCandidatesViewManager.displayCandidates(mConverter); 706 } else { 707 mCandidatesViewManager.clearCandidates(); 708 } 709 } 710 }; 711 712 /** 713 * Update the composing text. 714 * 715 * @param layer {@link mComposingText}'s layer to display 716 */ 717 private void updateComposingText(int layer) { 718 /* update the candidates view */ 719 if (!mOptPrediction) { 720 commitText(1); 721 mComposingText.clear(); 722 if (mSymbolMode) { 723 mDelayUpdateHandler.removeCallbacks(updatePredictionRunnable); 724 mDelayUpdateHandler.postDelayed(updatePredictionRunnable, 0); 725 } 726 } else { 727 if (mComposingText.size(1) != 0) { 728 mDelayUpdateHandler.removeCallbacks(updatePredictionRunnable); 729 mDelayUpdateHandler.postDelayed(updatePredictionRunnable, 250); 730 } else { 731 mDelayUpdateHandler.removeCallbacks(updatePredictionRunnable); 732 mDelayUpdateHandler.postDelayed(updatePredictionRunnable, 0); 733 } 734 735 /* notice to the input view */ 736 this.mInputViewManager.onUpdateState(this); 737 738 /* set the candidates view to the normal size */ 739 if (mCandidatesViewManager.getViewType() != CandidatesViewManager.VIEW_TYPE_NORMAL) { 740 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL); 741 } 742 /* set the text for displaying as the composing text */ 743 SpannableStringBuilder disp = mDisplayText; 744 disp.clear(); 745 disp.insert(0, mComposingText.toString(layer)); 746 747 /* add decoration to the text */ 748 int cursor = mComposingText.getCursor(layer); 749 if (disp.length() != 0) { 750 if (cursor > 0 && cursor < disp.length()) { 751 disp.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor, 752 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 753 } 754 if (cursor < disp.length()) { 755 mDisplayText.setSpan(SPAN_REMAIN_BGCOLOR_HL, cursor, disp.length(), 756 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 757 } 758 759 disp.setSpan(SPAN_UNDERLINE, 0, disp.length(), 760 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 761 } 762 763 int displayCursor = cursor; 764 if (FIX_CURSOR_TEXT_END) { 765 displayCursor = (cursor == 0) ? 0 : 1; 766 } 767 /* update the composing text on the EditView */ 768 mInputConnection.setComposingText(disp, displayCursor); 769 } 770 } 771 772 /** 773 * Commit the composing text. 774 * 775 * @param layer {@link mComposingText}'s layer to commit. 776 */ 777 private void commitText(int layer) { 778 String tmp = mComposingText.toString(layer); 779 780 if (mOptLearning && mConverter != null && tmp.length() > 0) { 781 WnnWord word = new WnnWord(tmp, tmp); 782 mConverter.learn(word); 783 } 784 785 mInputConnection.commitText(tmp, (FIX_CURSOR_TEXT_END ? 1 : tmp.length())); 786 mCandidatesViewManager.clearCandidates(); 787 } 788 789 /** 790 * Commit a word 791 * 792 * @param word A word to commit 793 * @param withSpace Append a space after the word if {@code true}. 794 */ 795 private void commitText(WnnWord word, boolean withSpace) { 796 797 if (mOptLearning && mConverter != null) { 798 mConverter.learn(word); 799 } 800 801 mInputConnection.commitText(word.candidate, (FIX_CURSOR_TEXT_END ? 1 : word.candidate.length())); 802 803 if (withSpace) { 804 commitText(" "); 805 } 806 } 807 808 /** 809 * Commit a string 810 * <br> 811 * The string is not registered into the learning dictionary. 812 * 813 * @param str A string to commit 814 */ 815 private void commitText(String str) { 816 mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length())); 817 mCandidatesViewManager.clearCandidates(); 818 } 819 820 /** 821 * Dismiss the pop-up keyboard 822 */ 823 protected void dismissPopupKeyboard() { 824 DefaultSoftKeyboardEN kbd = (DefaultSoftKeyboardEN)mInputViewManager; 825 if (kbd != null) { 826 kbd.dismissPopupKeyboard(); 827 } 828 } 829 830 /** 831 * Display current meta-key state. 832 */ 833 private void updateMetaKeyStateDisplay() { 834 int mode = 0; 835 if(mHardShift == 0 && mHardAlt == 0){ 836 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF; 837 }else if(mHardShift == 1 && mHardAlt == 0){ 838 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_OFF; 839 }else if(mHardShift == 2 && mHardAlt == 0){ 840 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_OFF; 841 }else if(mHardShift == 0 && mHardAlt == 1){ 842 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_ON; 843 }else if(mHardShift == 0 && mHardAlt == 2){ 844 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_LOCK; 845 }else if(mHardShift == 1 && mHardAlt == 1){ 846 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_ON; 847 }else if(mHardShift == 1 && mHardAlt == 2){ 848 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_LOCK; 849 }else if(mHardShift == 2 && mHardAlt == 1){ 850 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_ON; 851 }else if(mHardShift == 2 && mHardAlt == 2){ 852 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK; 853 }else{ 854 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF; 855 } 856 857 ((DefaultSoftKeyboard) mInputViewManager).updateIndicator(mode); 858 } 859 860 /** 861 * Handling KeyEvent(KEYUP) 862 * <br> 863 * This method is called from {@link #onEvent()}. 864 * 865 * @param ev An up key event 866 */ 867 private void onKeyUpEvent(KeyEvent ev) { 868 int key = ev.getKeyCode(); 869 if(!mShiftPressing){ 870 if(key == KeyEvent.KEYCODE_SHIFT_LEFT || key == KeyEvent.KEYCODE_SHIFT_RIGHT){ 871 mHardShift = 0; 872 mShiftPressing = true; 873 updateMetaKeyStateDisplay(); 874 } 875 } 876 if(!mAltPressing ){ 877 if(key == KeyEvent.KEYCODE_ALT_LEFT || key == KeyEvent.KEYCODE_ALT_RIGHT){ 878 mHardAlt = 0; 879 mAltPressing = true; 880 updateMetaKeyStateDisplay(); 881 } 882 } 883 } 884 /** 885 * Fits an editor info. 886 * 887 * @param preferences The preference data. 888 * @param info The editor info. 889 */ 890 private void fitInputType(SharedPreferences preference, EditorInfo info) { 891 if (info.inputType == EditorInfo.TYPE_NULL) { 892 mDirectInputMode = true; 893 return; 894 } 895 } 896} 897