OpenWnnEN.java revision 3feb3d26d59be1626c59b72654020179e2b2ebe0
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 260 mHardShift = 0; 261 mHardAlt = 0; 262 updateMetaKeyStateDisplay(); 263 264 /* load preferences */ 265 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); 266 267 /* auto caps mode */ 268 mAutoCaps = pref.getBoolean("auto_caps", true); 269 270 /* set TextCandidatesViewManager's option */ 271 ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false); 272 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 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) { 337 outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_FRAME; 338 } else { 339 super.onComputeInsets(outInsets); 340 } 341 } 342 343 /** @see jp.co.omronsoft.openwnn.OpenWnn#isFullscreenMode */ 344 @Override public boolean isFullscreenMode() { 345 boolean ret; 346 if (mInputViewManager == null) { 347 ret = (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL); 348 } else { 349 ret = false; 350 } 351 return ret; 352 } 353 354 /** @see jp.co.omronsoft.openwnn.OpenWnn#onUpdateSelection */ 355 @Override public void onUpdateSelection(int oldSelStart, int oldSelEnd, 356 int newSelStart, int newSelEnd, int candidatesStart, 357 int candidatesEnd) { 358 if (mComposingText.size(1) != 0) { 359 updateComposingText(1); 360 } 361 } 362 363 /** @see jp.co.omronsoft.openwnn.OpenWnn#onConfigurationChanged */ 364 @Override public void onConfigurationChanged(Configuration newConfig) { 365 try { 366 super.onConfigurationChanged(newConfig); 367 if (mInputConnection != null) { 368 updateComposingText(1); 369 } 370 } catch (Exception ex) { 371 } 372 } 373 374 /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateFullscreenMode */ 375 @Override public boolean onEvaluateFullscreenMode() { 376 return false; 377 } 378 379 /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateInputViewShown */ 380 @Override public boolean onEvaluateInputViewShown() { 381 return true; 382 } 383 384 /*********************************************************************** 385 * OpenWnn 386 ***********************************************************************/ 387 /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvent */ 388 @Override synchronized public boolean onEvent(OpenWnnEvent ev) { 389 /* handling events which are valid when InputConnection is not active. */ 390 switch (ev.code) { 391 392 case OpenWnnEvent.KEYUP: 393 onKeyUpEvent(ev.keyEvent); 394 return true; 395 396 case OpenWnnEvent.INITIALIZE_LEARNING_DICTIONARY: 397 return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_LEARN ); 398 399 case OpenWnnEvent.INITIALIZE_USER_DICTIONARY: 400 return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_USER ); 401 402 case OpenWnnEvent.LIST_WORDS_IN_USER_DICTIONARY: 403 mUserDictionaryWords = mConverterEN.getUserDictionaryWords( ); 404 return true; 405 406 case OpenWnnEvent.GET_WORD: 407 if( mUserDictionaryWords != null ) { 408 ev.word = mUserDictionaryWords[ 0 ]; 409 for( int i = 0 ; i < mUserDictionaryWords.length-1 ; i++ ) { 410 mUserDictionaryWords[ i ] = mUserDictionaryWords[ i + 1 ]; 411 } 412 mUserDictionaryWords[ mUserDictionaryWords.length-1 ] = null; 413 if( mUserDictionaryWords[ 0 ] == null ) { 414 mUserDictionaryWords = null; 415 } 416 return true; 417 } 418 break; 419 420 case OpenWnnEvent.ADD_WORD: 421 mConverterEN.addWord(ev.word); 422 return true; 423 424 case OpenWnnEvent.DELETE_WORD: 425 mConverterEN.deleteWord(ev.word); 426 return true; 427 428 case OpenWnnEvent.CHANGE_MODE: 429 return false; 430 431 case OpenWnnEvent.CHANGE_INPUT_VIEW: 432 setInputView(onCreateInputView()); 433 return true; 434 435 case OpenWnnEvent.CANDIDATE_VIEW_TOUCH: 436 boolean ret; 437 ret = ((TextCandidatesViewManager)mCandidatesViewManager).onTouchSync(); 438 return ret; 439 440 default: 441 break; 442 } 443 444 dismissPopupKeyboard(); 445 KeyEvent keyEvent = ev.keyEvent; 446 int keyCode = 0; 447 if (keyEvent != null) { 448 keyCode = keyEvent.getKeyCode(); 449 } 450 if (mDirectInputMode) { 451 if (ev.code == OpenWnnEvent.INPUT_SOFT_KEY && mInputConnection != null) { 452 mInputConnection.sendKeyEvent(keyEvent); 453 mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, 454 keyEvent.getKeyCode())); 455 } 456 return false; 457 } 458 459 if (ev.code == OpenWnnEvent.LIST_CANDIDATES_FULL) { 460 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_FULL); 461 return true; 462 } 463 464 boolean ret = false; 465 switch (ev.code) { 466 case OpenWnnEvent.INPUT_CHAR: 467 EditorInfo edit = getCurrentInputEditorInfo(); 468 if( edit.inputType == EditorInfo.TYPE_CLASS_PHONE){ 469 commitText(new String(ev.chars)); 470 }else{ 471 setSymbolMode(null); 472 insertCharToComposingText(ev.chars); 473 ret = true; 474 mPreviousEventCode = ev.code; 475 } 476 break; 477 478 case OpenWnnEvent.INPUT_KEY: 479 keyCode = ev.keyEvent.getKeyCode(); 480 /* update shift/alt state */ 481 switch (keyCode) { 482 case KeyEvent.KEYCODE_ALT_LEFT: 483 case KeyEvent.KEYCODE_ALT_RIGHT: 484 if (ev.keyEvent.getRepeatCount() == 0) { 485 if (++mHardAlt > 2) { mHardAlt = 0; } 486 } 487 mAltPressing = true; 488 updateMetaKeyStateDisplay(); 489 return true; 490 491 case KeyEvent.KEYCODE_SHIFT_LEFT: 492 case KeyEvent.KEYCODE_SHIFT_RIGHT: 493 if (ev.keyEvent.getRepeatCount() == 0) { 494 if (++mHardShift > 2) { mHardShift = 0; } 495 } 496 mShiftPressing = true; 497 updateMetaKeyStateDisplay(); 498 return true; 499 } 500 setSymbolMode(null); 501 updateComposingText(1); 502 /* handle other key event */ 503 ret = processKeyEvent(ev.keyEvent); 504 mPreviousEventCode = ev.code; 505 break; 506 507 case OpenWnnEvent.INPUT_SOFT_KEY: 508 setSymbolMode(null); 509 updateComposingText(1); 510 ret = processKeyEvent(ev.keyEvent); 511 if (!ret) { 512 mInputConnection.sendKeyEvent(ev.keyEvent); 513 mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, ev.keyEvent.getKeyCode())); 514 ret = true; 515 } 516 mPreviousEventCode = ev.code; 517 break; 518 519 case OpenWnnEvent.SELECT_CANDIDATE: 520 if (mSymbolMode) { 521 commitText(ev.word, false); 522 } else { 523 if (mWordSeparators.contains(ev.word.candidate) && 524 mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) { 525 mInputConnection.deleteSurroundingText(1, 0); 526 } 527 commitText(ev.word, true); 528 } 529 mComposingText.clear(); 530 mPreviousEventCode = ev.code; 531 updateComposingText(1); 532 break; 533 534 case OpenWnnEvent.LIST_SYMBOLS: 535 commitText(1); 536 mComposingText.clear(); 537 setSymbolMode(SymbolList.SYMBOL_ENGLISH); 538 updateComposingText(1); 539 break; 540 541 default: 542 break; 543 } 544 545 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) { 546 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL); 547 } 548 549 return ret; 550 } 551 552 /*********************************************************************** 553 * OpenWnnEN 554 ***********************************************************************/ 555 /** 556 * Handling KeyEvent 557 * <br> 558 * This method is called from {@link #onEvent()}. 559 * 560 * @param ev A key event 561 * @return {@code true} if the event is processed in this method; {@code false} if the event is not processed in this method 562 */ 563 private boolean processKeyEvent(KeyEvent ev) { 564 565 int key = ev.getKeyCode(); 566 EditorInfo edit = getCurrentInputEditorInfo(); 567 /* keys which produce a glyph */ 568 if (ev.isPrintingKey()) { 569 /* do nothing if the character is not able to display or the character is dead key */ 570 if ((mHardShift > 0 && mHardAlt > 0) || (ev.isAltPressed() && ev.isShiftPressed())) { 571 int charCode = ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON | MetaKeyKeyListener.META_ALT_ON); 572 if (charCode == 0 || (charCode & KeyCharacterMap.COMBINING_ACCENT) != 0 || charCode == PRIVATE_AREA_CODE) { 573 if(mHardShift == 1){ 574 mShiftPressing = false; 575 } 576 if(mHardAlt == 1){ 577 mAltPressing = false; 578 } 579 if(!ev.isAltPressed()){ 580 if (mHardAlt == 1) { 581 mHardAlt = 0; 582 } 583 } 584 if(!ev.isShiftPressed()){ 585 if (mHardShift == 1) { 586 mHardShift = 0; 587 } 588 } 589 if(!ev.isShiftPressed() && !ev.isAltPressed()){ 590 updateMetaKeyStateDisplay(); 591 } 592 return true; 593 } 594 } 595 596 /* get the key character */ 597 if (mHardShift== 0 && mHardAlt == 0) { 598 /* no meta key is locked */ 599 int shift = (mAutoCaps) ? getShiftKeyState(edit) : 0; 600 if (shift != mHardShift && (key >= KeyEvent.KEYCODE_A && key <= KeyEvent.KEYCODE_Z)) { 601 /* handling auto caps for a alphabet character */ 602 insertCharToComposingText(ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON)); 603 } else { 604 insertCharToComposingText(ev.getUnicodeChar()); 605 } 606 } else { 607 insertCharToComposingText(ev.getUnicodeChar(mShiftKeyToggle[mHardShift] 608 | mAltKeyToggle[mHardAlt])); 609 if(mHardShift == 1){ 610 mShiftPressing = false; 611 } 612 if(mHardAlt == 1){ 613 mAltPressing = false; 614 } 615 /* back to 0 (off) if 1 (on/not locked) */ 616 if(!ev.isAltPressed()){ 617 if (mHardAlt == 1) { 618 mHardAlt = 0; 619 } 620 } 621 if(!ev.isShiftPressed()){ 622 if (mHardShift == 1) { 623 mHardShift = 0; 624 } 625 } 626 if(!ev.isShiftPressed() && !ev.isAltPressed()){ 627 updateMetaKeyStateDisplay(); 628 } 629 } 630 631 if (edit.inputType == EditorInfo.TYPE_CLASS_PHONE) { 632 commitText(1); 633 mComposingText.clear(); 634 return true; 635 } 636 return true; 637 638 } else if (key == KeyEvent.KEYCODE_SPACE) { 639 if (ev.isAltPressed()) { 640 /* display the symbol list (G1 specific. same as KEYCODE_SYM) */ 641 commitText(1); 642 mComposingText.clear(); 643 setSymbolMode(SymbolList.SYMBOL_ENGLISH); 644 updateComposingText(1); 645 mHardAlt = 0; 646 updateMetaKeyStateDisplay(); 647 } else { 648 insertCharToComposingText(SPACE); 649 } 650 return true; 651 } else if (key == KeyEvent.KEYCODE_SYM) { 652 /* display the symbol list */ 653 commitText(1); 654 mComposingText.clear(); 655 setSymbolMode(SymbolList.SYMBOL_ENGLISH); 656 updateComposingText(1); 657 mHardAlt = 0; 658 updateMetaKeyStateDisplay(); 659 } 660 661 662 /* Functional key */ 663 if (mComposingText.size(1) > 0) { 664 switch (key) { 665 case KeyEvent.KEYCODE_DEL: 666 mComposingText.delete(1, false); 667 updateComposingText(1); 668 return true; 669 670 case KeyEvent.KEYCODE_BACK: 671 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) { 672 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL); 673 } else { 674 mComposingText.clear(); 675 updateComposingText(1); 676 } 677 return true; 678 679 case KeyEvent.KEYCODE_DPAD_LEFT: 680 mComposingText.moveCursor(1, -1); 681 updateComposingText(1); 682 return true; 683 684 case KeyEvent.KEYCODE_DPAD_RIGHT: 685 mComposingText.moveCursor(1, 1); 686 updateComposingText(1); 687 return true; 688 689 case KeyEvent.KEYCODE_ENTER: 690 case KeyEvent.KEYCODE_DPAD_CENTER: 691 commitText(1); 692 mComposingText.clear(); 693 return true; 694 695 default: 696 break; 697 } 698 } 699 700 return false; 701 } 702 703 /** 704 * Runnable for a thread getting and displaying candidates. 705 */ 706 private final Runnable updatePredictionRunnable = new Runnable() { 707 public void run() { 708 int candidates = 0; 709 if (mConverter != null) { 710 /* normal prediction */ 711 candidates = mConverter.predict(mComposingText, 0, -1); 712 } 713 /* update the candidates view */ 714 if (candidates > 0) { 715 mCandidatesViewManager.displayCandidates(mConverter); 716 } else { 717 mCandidatesViewManager.clearCandidates(); 718 } 719 } 720 }; 721 722 /** 723 * Update the composing text. 724 * 725 * @param layer {@link mComposingText}'s layer to display 726 */ 727 private void updateComposingText(int layer) { 728 /* update the candidates view */ 729 if (!mOptPrediction) { 730 commitText(1); 731 mComposingText.clear(); 732 if (mSymbolMode) { 733 mDelayUpdateHandler.removeCallbacks(updatePredictionRunnable); 734 mDelayUpdateHandler.postDelayed(updatePredictionRunnable, 0); 735 } 736 } else { 737 if (mComposingText.size(1) != 0) { 738 mDelayUpdateHandler.removeCallbacks(updatePredictionRunnable); 739 mDelayUpdateHandler.postDelayed(updatePredictionRunnable, 250); 740 } else { 741 mDelayUpdateHandler.removeCallbacks(updatePredictionRunnable); 742 mDelayUpdateHandler.postDelayed(updatePredictionRunnable, 0); 743 } 744 745 /* notice to the input view */ 746 this.mInputViewManager.onUpdateState(this); 747 748 /* set the candidates view to the normal size */ 749 if (mCandidatesViewManager.getViewType() != CandidatesViewManager.VIEW_TYPE_NORMAL) { 750 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL); 751 } 752 /* set the text for displaying as the composing text */ 753 SpannableStringBuilder disp = mDisplayText; 754 disp.clear(); 755 disp.insert(0, mComposingText.toString(layer)); 756 757 /* add decoration to the text */ 758 int cursor = mComposingText.getCursor(layer); 759 if (disp.length() != 0) { 760 if (cursor > 0 && cursor < disp.length()) { 761 disp.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor, 762 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 763 } 764 if (cursor < disp.length()) { 765 mDisplayText.setSpan(SPAN_REMAIN_BGCOLOR_HL, cursor, disp.length(), 766 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 767 } 768 769 disp.setSpan(SPAN_UNDERLINE, 0, disp.length(), 770 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 771 } 772 773 int displayCursor = cursor; 774 if (FIX_CURSOR_TEXT_END) { 775 displayCursor = (cursor == 0) ? 0 : 1; 776 } 777 /* update the composing text on the EditView */ 778 mInputConnection.setComposingText(disp, displayCursor); 779 } 780 } 781 782 /** 783 * Commit the composing text. 784 * 785 * @param layer {@link mComposingText}'s layer to commit. 786 */ 787 private void commitText(int layer) { 788 String tmp = mComposingText.toString(layer); 789 790 if (mOptLearning && mConverter != null && tmp.length() > 0) { 791 WnnWord word = new WnnWord(tmp, tmp); 792 mConverter.learn(word); 793 } 794 795 mInputConnection.commitText(tmp, (FIX_CURSOR_TEXT_END ? 1 : tmp.length())); 796 mCandidatesViewManager.clearCandidates(); 797 } 798 799 /** 800 * Commit a word 801 * 802 * @param word A word to commit 803 * @param withSpace Append a space after the word if {@code true}. 804 */ 805 private void commitText(WnnWord word, boolean withSpace) { 806 807 if (mOptLearning && mConverter != null) { 808 mConverter.learn(word); 809 } 810 811 mInputConnection.commitText(word.candidate, (FIX_CURSOR_TEXT_END ? 1 : word.candidate.length())); 812 813 if (withSpace) { 814 commitText(" "); 815 } 816 } 817 818 /** 819 * Commit a string 820 * <br> 821 * The string is not registered into the learning dictionary. 822 * 823 * @param str A string to commit 824 */ 825 private void commitText(String str) { 826 mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length())); 827 mCandidatesViewManager.clearCandidates(); 828 } 829 830 /** 831 * Dismiss the pop-up keyboard 832 */ 833 protected void dismissPopupKeyboard() { 834 DefaultSoftKeyboardEN kbd = (DefaultSoftKeyboardEN)mInputViewManager; 835 if (kbd != null) { 836 kbd.dismissPopupKeyboard(); 837 } 838 } 839 840 /** 841 * Display current meta-key state. 842 */ 843 private void updateMetaKeyStateDisplay() { 844 int mode = 0; 845 if(mHardShift == 0 && mHardAlt == 0){ 846 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF; 847 }else if(mHardShift == 1 && mHardAlt == 0){ 848 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_OFF; 849 }else if(mHardShift == 2 && mHardAlt == 0){ 850 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_OFF; 851 }else if(mHardShift == 0 && mHardAlt == 1){ 852 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_ON; 853 }else if(mHardShift == 0 && mHardAlt == 2){ 854 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_LOCK; 855 }else if(mHardShift == 1 && mHardAlt == 1){ 856 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_ON; 857 }else if(mHardShift == 1 && mHardAlt == 2){ 858 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_LOCK; 859 }else if(mHardShift == 2 && mHardAlt == 1){ 860 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_ON; 861 }else if(mHardShift == 2 && mHardAlt == 2){ 862 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK; 863 }else{ 864 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF; 865 } 866 867 ((DefaultSoftKeyboard) mInputViewManager).updateIndicator(mode); 868 } 869 870 /** 871 * Handling KeyEvent(KEYUP) 872 * <br> 873 * This method is called from {@link #onEvent()}. 874 * 875 * @param ev An up key event 876 */ 877 private void onKeyUpEvent(KeyEvent ev) { 878 int key = ev.getKeyCode(); 879 if(!mShiftPressing){ 880 if(key == KeyEvent.KEYCODE_SHIFT_LEFT || key == KeyEvent.KEYCODE_SHIFT_RIGHT){ 881 mHardShift = 0; 882 mShiftPressing = true; 883 updateMetaKeyStateDisplay(); 884 } 885 } 886 if(!mAltPressing ){ 887 if(key == KeyEvent.KEYCODE_ALT_LEFT || key == KeyEvent.KEYCODE_ALT_RIGHT){ 888 mHardAlt = 0; 889 mAltPressing = true; 890 updateMetaKeyStateDisplay(); 891 } 892 } 893 } 894 /** 895 * Fits an editor info. 896 * 897 * @param preferences The preference data. 898 * @param info The editor info. 899 */ 900 private void fitInputType(SharedPreferences preference, EditorInfo info) { 901 if (info.inputType == EditorInfo.TYPE_NULL) { 902 mDirectInputMode = true; 903 return; 904 } 905 } 906} 907 908 909 910 911 912 913 914 915