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