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 */ OpenWnnEN()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 */ OpenWnnEN(Context context)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 */ getInstance()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 */ insertCharToComposingText(char[] chars)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 */ insertCharToComposingText(int charCode)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 */ getShiftKeyState(EditorInfo editor)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 */ setSymbolMode(String mode)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 */ 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 */ 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 */ onStartInputView(EditorInfo attribute, boolean restarting)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 */ 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 */ onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd)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 */ onConfigurationChanged(Configuration newConfig)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 */ onEvaluateFullscreenMode()378 @Override public boolean onEvaluateFullscreenMode() { 379 return false; 380 } 381 382 /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateInputViewShown */ onEvaluateInputViewShown()383 @Override public boolean onEvaluateInputViewShown() { 384 return true; 385 } 386 387 /*********************************************************************** 388 * OpenWnn 389 ***********************************************************************/ 390 /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvent */ onEvent(OpenWnnEvent ev)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 mInputConnection.sendKeyEvent(ev.keyEvent); 524 mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, ev.keyEvent.getKeyCode())); 525 ret = true; 526 } 527 mPreviousEventCode = ev.code; 528 break; 529 530 case OpenWnnEvent.SELECT_CANDIDATE: 531 if (mSymbolMode) { 532 commitText(ev.word, false); 533 } else { 534 if (mWordSeparators.contains(ev.word.candidate) && 535 mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) { 536 mInputConnection.deleteSurroundingText(1, 0); 537 } 538 commitText(ev.word, true); 539 } 540 mComposingText.clear(); 541 mPreviousEventCode = ev.code; 542 updateComposingText(1); 543 break; 544 545 case OpenWnnEvent.LIST_SYMBOLS: 546 commitText(1); 547 mComposingText.clear(); 548 setSymbolMode(SymbolList.SYMBOL_ENGLISH); 549 updateComposingText(1); 550 break; 551 552 default: 553 break; 554 } 555 556 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) { 557 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL); 558 } 559 560 return ret; 561 } 562 563 /*********************************************************************** 564 * OpenWnnEN 565 ***********************************************************************/ 566 /** 567 * Handling KeyEvent 568 * <br> 569 * This method is called from {@link #onEvent()}. 570 * 571 * @param ev A key event 572 * @return {@code true} if the event is processed in this method; {@code false} if the event is not processed in this method 573 */ processKeyEvent(KeyEvent ev)574 private boolean processKeyEvent(KeyEvent ev) { 575 576 int key = ev.getKeyCode(); 577 EditorInfo edit = getCurrentInputEditorInfo(); 578 /* keys which produce a glyph */ 579 if (ev.isPrintingKey()) { 580 /* do nothing if the character is not able to display or the character is dead key */ 581 if ((mHardShift > 0 && mHardAlt > 0) || (ev.isAltPressed() && ev.isShiftPressed())) { 582 int charCode = ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON | MetaKeyKeyListener.META_ALT_ON); 583 if (charCode == 0 || (charCode & KeyCharacterMap.COMBINING_ACCENT) != 0 || charCode == PRIVATE_AREA_CODE) { 584 if(mHardShift == 1){ 585 mShiftPressing = false; 586 } 587 if(mHardAlt == 1){ 588 mAltPressing = false; 589 } 590 if(!ev.isAltPressed()){ 591 if (mHardAlt == 1) { 592 mHardAlt = 0; 593 } 594 } 595 if(!ev.isShiftPressed()){ 596 if (mHardShift == 1) { 597 mHardShift = 0; 598 } 599 } 600 if(!ev.isShiftPressed() && !ev.isAltPressed()){ 601 updateMetaKeyStateDisplay(); 602 } 603 return true; 604 } 605 } 606 607 ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false); 608 609 /* get the key character */ 610 if (mHardShift== 0 && mHardAlt == 0) { 611 /* no meta key is locked */ 612 int shift = (mAutoCaps) ? getShiftKeyState(edit) : 0; 613 if (shift != mHardShift && (key >= KeyEvent.KEYCODE_A && key <= KeyEvent.KEYCODE_Z)) { 614 /* handling auto caps for a alphabet character */ 615 insertCharToComposingText(ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON)); 616 } else { 617 insertCharToComposingText(ev.getUnicodeChar()); 618 } 619 } else { 620 insertCharToComposingText(ev.getUnicodeChar(mShiftKeyToggle[mHardShift] 621 | mAltKeyToggle[mHardAlt])); 622 if(mHardShift == 1){ 623 mShiftPressing = false; 624 } 625 if(mHardAlt == 1){ 626 mAltPressing = false; 627 } 628 /* back to 0 (off) if 1 (on/not locked) */ 629 if(!ev.isAltPressed()){ 630 if (mHardAlt == 1) { 631 mHardAlt = 0; 632 } 633 } 634 if(!ev.isShiftPressed()){ 635 if (mHardShift == 1) { 636 mHardShift = 0; 637 } 638 } 639 if(!ev.isShiftPressed() && !ev.isAltPressed()){ 640 updateMetaKeyStateDisplay(); 641 } 642 } 643 644 if (edit.inputType == EditorInfo.TYPE_CLASS_PHONE) { 645 commitText(1); 646 mComposingText.clear(); 647 return true; 648 } 649 return true; 650 651 } else if (key == KeyEvent.KEYCODE_SPACE) { 652 if (ev.isAltPressed()) { 653 /* display the symbol list (G1 specific. same as KEYCODE_SYM) */ 654 commitText(1); 655 mComposingText.clear(); 656 setSymbolMode(SymbolList.SYMBOL_ENGLISH); 657 updateComposingText(1); 658 mHardAlt = 0; 659 updateMetaKeyStateDisplay(); 660 } else { 661 insertCharToComposingText(SPACE); 662 } 663 return true; 664 } else if (key == KeyEvent.KEYCODE_SYM) { 665 /* display the symbol list */ 666 commitText(1); 667 mComposingText.clear(); 668 setSymbolMode(SymbolList.SYMBOL_ENGLISH); 669 updateComposingText(1); 670 mHardAlt = 0; 671 updateMetaKeyStateDisplay(); 672 } 673 674 675 /* Functional key */ 676 if (mComposingText.size(1) > 0) { 677 switch (key) { 678 case KeyEvent.KEYCODE_DEL: 679 mComposingText.delete(1, false); 680 updateComposingText(1); 681 return true; 682 683 case KeyEvent.KEYCODE_BACK: 684 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) { 685 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL); 686 } else { 687 mComposingText.clear(); 688 updateComposingText(1); 689 } 690 return true; 691 692 case KeyEvent.KEYCODE_DPAD_LEFT: 693 mComposingText.moveCursor(1, -1); 694 updateComposingText(1); 695 return true; 696 697 case KeyEvent.KEYCODE_DPAD_RIGHT: 698 mComposingText.moveCursor(1, 1); 699 updateComposingText(1); 700 return true; 701 702 case KeyEvent.KEYCODE_ENTER: 703 case KeyEvent.KEYCODE_DPAD_CENTER: 704 commitText(1); 705 mComposingText.clear(); 706 if (mEnableAutoHideKeyboard) { 707 mInputViewManager.closing(); 708 requestHideSelf(0); 709 } 710 return true; 711 712 default: 713 break; 714 } 715 } else { 716 /* if there is no composing string. */ 717 if (mCandidatesViewManager.getCurrentView().isShown()) { 718 if (key == KeyEvent.KEYCODE_BACK) { 719 if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) { 720 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL); 721 } else { 722 mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE); 723 } 724 return true; 725 } 726 } else { 727 switch (key) { 728 case KeyEvent.KEYCODE_DPAD_CENTER: 729 case KeyEvent.KEYCODE_ENTER: 730 if (mEnableAutoHideKeyboard) { 731 mInputViewManager.closing(); 732 requestHideSelf(0); 733 return true; 734 } 735 break; 736 case KeyEvent.KEYCODE_BACK: 737 /* 738 * If 'BACK' key is pressed when the SW-keyboard is shown 739 * and the candidates view is not shown, dismiss the SW-keyboard. 740 */ 741 if (isInputViewShown()) { 742 mInputViewManager.closing(); 743 requestHideSelf(0); 744 return true; 745 } 746 break; 747 default: 748 break; 749 } 750 } 751 } 752 753 return false; 754 } 755 756 /** 757 * Thread for updating the candidates view 758 */ updatePrediction()759 private void updatePrediction() { 760 int candidates = 0; 761 if (mConverter != null) { 762 /* normal prediction */ 763 candidates = mConverter.predict(mComposingText, 0, -1); 764 } 765 /* update the candidates view */ 766 if (candidates > 0) { 767 mCandidatesViewManager.displayCandidates(mConverter); 768 } else { 769 mCandidatesViewManager.clearCandidates(); 770 } 771 } 772 773 /** 774 * Update the composing text. 775 * 776 * @param layer {@link mComposingText}'s layer to display 777 */ updateComposingText(int layer)778 private void updateComposingText(int layer) { 779 /* update the candidates view */ 780 if (!mOptPrediction) { 781 commitText(1); 782 mComposingText.clear(); 783 if (mSymbolMode) { 784 mHandler.removeMessages(MSG_PREDICTION); 785 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0); 786 } 787 } else { 788 if (mComposingText.size(1) != 0) { 789 mHandler.removeMessages(MSG_PREDICTION); 790 if (mCandidatesViewManager.getCurrentView().isShown()) { 791 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 792 PREDICTION_DELAY_MS_SHOWING_CANDIDATE); 793 } else { 794 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 795 PREDICTION_DELAY_MS_1ST); 796 } 797 } else { 798 mHandler.removeMessages(MSG_PREDICTION); 799 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0); 800 } 801 802 /* notice to the input view */ 803 this.mInputViewManager.onUpdateState(this); 804 805 /* set the text for displaying as the composing text */ 806 SpannableStringBuilder disp = mDisplayText; 807 disp.clear(); 808 disp.insert(0, mComposingText.toString(layer)); 809 810 /* add decoration to the text */ 811 int cursor = mComposingText.getCursor(layer); 812 if (disp.length() != 0) { 813 if (cursor > 0 && cursor < disp.length()) { 814 disp.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor, 815 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 816 } 817 if (cursor < disp.length()) { 818 mDisplayText.setSpan(SPAN_REMAIN_BGCOLOR_HL, cursor, disp.length(), 819 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 820 mDisplayText.setSpan(SPAN_TEXTCOLOR, 0, disp.length(), 821 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 822 } 823 824 disp.setSpan(SPAN_UNDERLINE, 0, disp.length(), 825 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 826 } 827 828 int displayCursor = cursor; 829 if (FIX_CURSOR_TEXT_END) { 830 displayCursor = (cursor == 0) ? 0 : 1; 831 } 832 /* update the composing text on the EditView */ 833 mInputConnection.setComposingText(disp, displayCursor); 834 } 835 } 836 837 /** 838 * Commit the composing text. 839 * 840 * @param layer {@link mComposingText}'s layer to commit. 841 */ commitText(int layer)842 private void commitText(int layer) { 843 String tmp = mComposingText.toString(layer); 844 845 if (mOptLearning && mConverter != null && tmp.length() > 0) { 846 WnnWord word = new WnnWord(tmp, tmp); 847 mConverter.learn(word); 848 } 849 850 mInputConnection.commitText(tmp, (FIX_CURSOR_TEXT_END ? 1 : tmp.length())); 851 mCandidatesViewManager.clearCandidates(); 852 } 853 854 /** 855 * Commit a word 856 * 857 * @param word A word to commit 858 * @param withSpace Append a space after the word if {@code true}. 859 */ commitText(WnnWord word, boolean withSpace)860 private void commitText(WnnWord word, boolean withSpace) { 861 862 if (mOptLearning && mConverter != null) { 863 mConverter.learn(word); 864 } 865 866 mInputConnection.commitText(word.candidate, (FIX_CURSOR_TEXT_END ? 1 : word.candidate.length())); 867 868 if (withSpace) { 869 commitText(" "); 870 } 871 } 872 873 /** 874 * Commit a string 875 * <br> 876 * The string is not registered into the learning dictionary. 877 * 878 * @param str A string to commit 879 */ commitText(String str)880 private void commitText(String str) { 881 mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length())); 882 mCandidatesViewManager.clearCandidates(); 883 } 884 885 /** 886 * Dismiss the pop-up keyboard 887 */ dismissPopupKeyboard()888 protected void dismissPopupKeyboard() { 889 DefaultSoftKeyboardEN kbd = (DefaultSoftKeyboardEN)mInputViewManager; 890 if (kbd != null) { 891 kbd.dismissPopupKeyboard(); 892 } 893 } 894 895 /** 896 * Display current meta-key state. 897 */ updateMetaKeyStateDisplay()898 private void updateMetaKeyStateDisplay() { 899 int mode = 0; 900 if(mHardShift == 0 && mHardAlt == 0){ 901 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF; 902 }else if(mHardShift == 1 && mHardAlt == 0){ 903 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_OFF; 904 }else if(mHardShift == 2 && mHardAlt == 0){ 905 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_OFF; 906 }else if(mHardShift == 0 && mHardAlt == 1){ 907 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_ON; 908 }else if(mHardShift == 0 && mHardAlt == 2){ 909 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_LOCK; 910 }else if(mHardShift == 1 && mHardAlt == 1){ 911 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_ON; 912 }else if(mHardShift == 1 && mHardAlt == 2){ 913 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_LOCK; 914 }else if(mHardShift == 2 && mHardAlt == 1){ 915 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_ON; 916 }else if(mHardShift == 2 && mHardAlt == 2){ 917 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK; 918 }else{ 919 mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF; 920 } 921 922 ((DefaultSoftKeyboard) mInputViewManager).updateIndicator(mode); 923 } 924 925 /** 926 * Handling KeyEvent(KEYUP) 927 * <br> 928 * This method is called from {@link #onEvent()}. 929 * 930 * @param ev An up key event 931 */ onKeyUpEvent(KeyEvent ev)932 private void onKeyUpEvent(KeyEvent ev) { 933 int key = ev.getKeyCode(); 934 if(!mShiftPressing){ 935 if(key == KeyEvent.KEYCODE_SHIFT_LEFT || key == KeyEvent.KEYCODE_SHIFT_RIGHT){ 936 mHardShift = 0; 937 mShiftPressing = true; 938 updateMetaKeyStateDisplay(); 939 } 940 } 941 if(!mAltPressing ){ 942 if(key == KeyEvent.KEYCODE_ALT_LEFT || key == KeyEvent.KEYCODE_ALT_RIGHT){ 943 mHardAlt = 0; 944 mAltPressing = true; 945 updateMetaKeyStateDisplay(); 946 } 947 } 948 } 949 /** 950 * Fits an editor info. 951 * 952 * @param preferences The preference data. 953 * @param info The editor info. 954 */ fitInputType(SharedPreferences preference, EditorInfo info)955 private void fitInputType(SharedPreferences preference, EditorInfo info) { 956 if (info.inputType == EditorInfo.TYPE_NULL) { 957 mDirectInputMode = true; 958 return; 959 } 960 961 mEnableAutoHideKeyboard = false; 962 963 /* set prediction & spell correction mode */ 964 mOptPrediction = preference.getBoolean("opt_en_prediction", true); 965 mOptSpellCorrection = preference.getBoolean("opt_en_spell_correction", true); 966 mOptLearning = preference.getBoolean("opt_en_enable_learning", true); 967 968 /* prediction on/off */ 969 switch (info.inputType & EditorInfo.TYPE_MASK_CLASS) { 970 case EditorInfo.TYPE_CLASS_NUMBER: 971 case EditorInfo.TYPE_CLASS_DATETIME: 972 case EditorInfo.TYPE_CLASS_PHONE: 973 mOptPrediction = false; 974 mOptLearning = false; 975 break; 976 977 case EditorInfo.TYPE_CLASS_TEXT: 978 switch (info.inputType & EditorInfo.TYPE_MASK_VARIATION) { 979 case EditorInfo.TYPE_TEXT_VARIATION_PASSWORD: 980 mEnableAutoHideKeyboard = true; 981 mOptLearning = false; 982 mOptPrediction = false; 983 break; 984 985 case EditorInfo.TYPE_TEXT_VARIATION_PHONETIC: 986 mOptLearning = false; 987 mOptPrediction = false; 988 break; 989 default: 990 break; 991 } 992 } 993 994 /* doesn't learn any word if it is not prediction mode */ 995 if (!mOptPrediction) { 996 mOptLearning = false; 997 } 998 999 /* set engine's mode */ 1000 if (mOptSpellCorrection) { 1001 mConverterEN.setDictionary(OpenWnnEngineEN.DICT_FOR_CORRECT_MISTYPE); 1002 } else { 1003 mConverterEN.setDictionary(OpenWnnEngineEN.DICT_DEFAULT); 1004 } 1005 checkTutorial(info.privateImeOptions); 1006 } 1007 1008 /** 1009 * Check and start the tutorial if it is the tutorial mode. 1010 * 1011 * @param privateImeOptions IME's options 1012 */ checkTutorial(String privateImeOptions)1013 private void checkTutorial(String privateImeOptions) { 1014 if (privateImeOptions == null) return; 1015 if (privateImeOptions.equals("com.android.setupwizard:ShowTutorial")) { 1016 if ((mTutorial == null) && mEnableTutorial) startTutorial(); 1017 } else if (privateImeOptions.equals("com.android.setupwizard:HideTutorial")) { 1018 if (mTutorial != null) { 1019 if (mTutorial.close()) { 1020 mTutorial = null; 1021 } 1022 } 1023 } 1024 } 1025 1026 /** 1027 * Start the tutorial 1028 */ startTutorial()1029 private void startTutorial() { 1030 DefaultSoftKeyboardEN inputManager = ((DefaultSoftKeyboardEN) mInputViewManager); 1031 View v = inputManager.getKeyboardView(); 1032 v.setOnTouchListener(new View.OnTouchListener() { 1033 public boolean onTouch(View v, MotionEvent event) { 1034 return true; 1035 }}); 1036 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500); 1037 } 1038 1039 /** 1040 * Close the tutorial 1041 */ tutorialDone()1042 public void tutorialDone() { 1043 mTutorial = null; 1044 } 1045 1046 /** @see OpenWnn#close */ close()1047 @Override protected void close() { 1048 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLOSE), 0); 1049 } 1050 } 1051