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