KeyboardState.java revision 9552badf3c24d2098d227b0ddca0721b928a10b1
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.inputmethod.keyboard.internal; 18 19import android.text.TextUtils; 20import android.util.Log; 21 22import com.android.inputmethod.latin.Constants; 23import com.android.inputmethod.latin.RecapitalizeStatus; 24 25/** 26 * Keyboard state machine. 27 * 28 * This class contains all keyboard state transition logic. 29 * 30 * The input events are {@link #onLoadKeyboard()}, {@link #onSaveKeyboardState()}, 31 * {@link #onPressKey(int,boolean,int)}, {@link #onReleaseKey(int,boolean)}, 32 * {@link #onCodeInput(int,int)}, {@link #onFinishSlidingInput()}, {@link #onCancelInput()}, 33 * {@link #onUpdateShiftState(int,int)}. 34 * 35 * The actions are {@link SwitchActions}'s methods. 36 */ 37public final class KeyboardState { 38 private static final String TAG = KeyboardState.class.getSimpleName(); 39 private static final boolean DEBUG_EVENT = false; 40 private static final boolean DEBUG_ACTION = false; 41 42 public interface SwitchActions { 43 public void setAlphabetKeyboard(); 44 public void setAlphabetManualShiftedKeyboard(); 45 public void setAlphabetAutomaticShiftedKeyboard(); 46 public void setAlphabetShiftLockedKeyboard(); 47 public void setAlphabetShiftLockShiftedKeyboard(); 48 public void setSymbolsKeyboard(); 49 public void setSymbolsShiftedKeyboard(); 50 51 /** 52 * Request to call back {@link KeyboardState#onUpdateShiftState(int, int)}. 53 */ 54 public void requestUpdatingShiftState(); 55 56 public void startDoubleTapShiftKeyTimer(); 57 public boolean isInDoubleTapShiftKeyTimeout(); 58 public void cancelDoubleTapShiftKeyTimer(); 59 } 60 61 private final SwitchActions mSwitchActions; 62 63 private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift"); 64 private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol"); 65 66 // TODO: Merge {@link #mSwitchState}, {@link #mIsAlphabetMode}, {@link #mAlphabetShiftState}, 67 // {@link #mIsSymbolShifted}, {@link #mPrevMainKeyboardWasShiftLocked}, and 68 // {@link #mPrevSymbolsKeyboardWasShifted} into single state variable. 69 private static final int SWITCH_STATE_ALPHA = 0; 70 private static final int SWITCH_STATE_SYMBOL_BEGIN = 1; 71 private static final int SWITCH_STATE_SYMBOL = 2; 72 private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3; 73 private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4; 74 private static final int SWITCH_STATE_MOMENTARY_ALPHA_SHIFT = 5; 75 private int mSwitchState = SWITCH_STATE_ALPHA; 76 77 private boolean mIsAlphabetMode; 78 private AlphabetShiftState mAlphabetShiftState = new AlphabetShiftState(); 79 private boolean mIsSymbolShifted; 80 private boolean mPrevMainKeyboardWasShiftLocked; 81 private boolean mPrevSymbolsKeyboardWasShifted; 82 private int mRecapitalizeMode; 83 84 // For handling long press. 85 private boolean mLongPressShiftLockFired; 86 87 // For handling double tap. 88 private boolean mIsInAlphabetUnshiftedFromShifted; 89 private boolean mIsInDoubleTapShiftKey; 90 91 private final SavedKeyboardState mSavedKeyboardState = new SavedKeyboardState(); 92 93 static final class SavedKeyboardState { 94 public boolean mIsValid; 95 public boolean mIsAlphabetMode; 96 public boolean mIsAlphabetShiftLocked; 97 public int mShiftMode; 98 99 @Override 100 public String toString() { 101 if (!mIsValid) return "INVALID"; 102 if (mIsAlphabetMode) { 103 if (mIsAlphabetShiftLocked) return "ALPHABET_SHIFT_LOCKED"; 104 return "ALPHABET_" + shiftModeToString(mShiftMode); 105 } else { 106 return "SYMBOLS_" + shiftModeToString(mShiftMode); 107 } 108 } 109 } 110 111 public KeyboardState(final SwitchActions switchActions) { 112 mSwitchActions = switchActions; 113 mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; 114 } 115 116 public void onLoadKeyboard() { 117 if (DEBUG_EVENT) { 118 Log.d(TAG, "onLoadKeyboard: " + this); 119 } 120 // Reset alphabet shift state. 121 mAlphabetShiftState.setShiftLocked(false); 122 mPrevMainKeyboardWasShiftLocked = false; 123 mPrevSymbolsKeyboardWasShifted = false; 124 mShiftKeyState.onRelease(); 125 mSymbolKeyState.onRelease(); 126 onRestoreKeyboardState(); 127 } 128 129 private static final int UNSHIFT = 0; 130 private static final int MANUAL_SHIFT = 1; 131 private static final int AUTOMATIC_SHIFT = 2; 132 private static final int SHIFT_LOCK_SHIFTED = 3; 133 134 public void onSaveKeyboardState() { 135 final SavedKeyboardState state = mSavedKeyboardState; 136 state.mIsAlphabetMode = mIsAlphabetMode; 137 if (mIsAlphabetMode) { 138 state.mIsAlphabetShiftLocked = mAlphabetShiftState.isShiftLocked(); 139 state.mShiftMode = mAlphabetShiftState.isAutomaticShifted() ? AUTOMATIC_SHIFT 140 : (mAlphabetShiftState.isShiftedOrShiftLocked() ? MANUAL_SHIFT : UNSHIFT); 141 } else { 142 state.mIsAlphabetShiftLocked = mPrevMainKeyboardWasShiftLocked; 143 state.mShiftMode = mIsSymbolShifted ? MANUAL_SHIFT : UNSHIFT; 144 } 145 state.mIsValid = true; 146 if (DEBUG_EVENT) { 147 Log.d(TAG, "onSaveKeyboardState: saved=" + state + " " + this); 148 } 149 } 150 151 private void onRestoreKeyboardState() { 152 final SavedKeyboardState state = mSavedKeyboardState; 153 if (DEBUG_EVENT) { 154 Log.d(TAG, "onRestoreKeyboardState: saved=" + state + " " + this); 155 } 156 if (!state.mIsValid || state.mIsAlphabetMode) { 157 setAlphabetKeyboard(); 158 } else { 159 if (state.mShiftMode == MANUAL_SHIFT) { 160 setSymbolsShiftedKeyboard(); 161 } else { 162 setSymbolsKeyboard(); 163 } 164 } 165 166 if (!state.mIsValid) return; 167 state.mIsValid = false; 168 169 if (state.mIsAlphabetMode) { 170 setShiftLocked(state.mIsAlphabetShiftLocked); 171 if (!state.mIsAlphabetShiftLocked) { 172 setShifted(state.mShiftMode); 173 } 174 } else { 175 mPrevMainKeyboardWasShiftLocked = state.mIsAlphabetShiftLocked; 176 } 177 } 178 179 private void setShifted(final int shiftMode) { 180 if (DEBUG_ACTION) { 181 Log.d(TAG, "setShifted: shiftMode=" + shiftModeToString(shiftMode) + " " + this); 182 } 183 if (!mIsAlphabetMode) return; 184 final int prevShiftMode; 185 if (mAlphabetShiftState.isAutomaticShifted()) { 186 prevShiftMode = AUTOMATIC_SHIFT; 187 } else if (mAlphabetShiftState.isManualShifted()) { 188 prevShiftMode = MANUAL_SHIFT; 189 } else { 190 prevShiftMode = UNSHIFT; 191 } 192 switch (shiftMode) { 193 case AUTOMATIC_SHIFT: 194 mAlphabetShiftState.setAutomaticShifted(); 195 if (shiftMode != prevShiftMode) { 196 mSwitchActions.setAlphabetAutomaticShiftedKeyboard(); 197 } 198 break; 199 case MANUAL_SHIFT: 200 mAlphabetShiftState.setShifted(true); 201 if (shiftMode != prevShiftMode) { 202 mSwitchActions.setAlphabetManualShiftedKeyboard(); 203 } 204 break; 205 case UNSHIFT: 206 mAlphabetShiftState.setShifted(false); 207 if (shiftMode != prevShiftMode) { 208 mSwitchActions.setAlphabetKeyboard(); 209 } 210 break; 211 case SHIFT_LOCK_SHIFTED: 212 mAlphabetShiftState.setShifted(true); 213 mSwitchActions.setAlphabetShiftLockShiftedKeyboard(); 214 break; 215 } 216 } 217 218 private void setShiftLocked(final boolean shiftLocked) { 219 if (DEBUG_ACTION) { 220 Log.d(TAG, "setShiftLocked: shiftLocked=" + shiftLocked + " " + this); 221 } 222 if (!mIsAlphabetMode) return; 223 if (shiftLocked && (!mAlphabetShiftState.isShiftLocked() 224 || mAlphabetShiftState.isShiftLockShifted())) { 225 mSwitchActions.setAlphabetShiftLockedKeyboard(); 226 } 227 if (!shiftLocked && mAlphabetShiftState.isShiftLocked()) { 228 mSwitchActions.setAlphabetKeyboard(); 229 } 230 mAlphabetShiftState.setShiftLocked(shiftLocked); 231 } 232 233 private void toggleAlphabetAndSymbols() { 234 if (DEBUG_ACTION) { 235 Log.d(TAG, "toggleAlphabetAndSymbols: " + this); 236 } 237 if (mIsAlphabetMode) { 238 mPrevMainKeyboardWasShiftLocked = mAlphabetShiftState.isShiftLocked(); 239 if (mPrevSymbolsKeyboardWasShifted) { 240 setSymbolsShiftedKeyboard(); 241 } else { 242 setSymbolsKeyboard(); 243 } 244 mPrevSymbolsKeyboardWasShifted = false; 245 } else { 246 mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted; 247 setAlphabetKeyboard(); 248 if (mPrevMainKeyboardWasShiftLocked) { 249 setShiftLocked(true); 250 } 251 mPrevMainKeyboardWasShiftLocked = false; 252 } 253 } 254 255 // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout 256 // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal(). 257 private void resetKeyboardStateToAlphabet() { 258 if (DEBUG_ACTION) { 259 Log.d(TAG, "resetKeyboardStateToAlphabet: " + this); 260 } 261 if (mIsAlphabetMode) return; 262 263 mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted; 264 setAlphabetKeyboard(); 265 if (mPrevMainKeyboardWasShiftLocked) { 266 setShiftLocked(true); 267 } 268 mPrevMainKeyboardWasShiftLocked = false; 269 } 270 271 private void toggleShiftInSymbols() { 272 if (mIsSymbolShifted) { 273 setSymbolsKeyboard(); 274 } else { 275 setSymbolsShiftedKeyboard(); 276 } 277 } 278 279 private void setAlphabetKeyboard() { 280 if (DEBUG_ACTION) { 281 Log.d(TAG, "setAlphabetKeyboard"); 282 } 283 284 mSwitchActions.setAlphabetKeyboard(); 285 mIsAlphabetMode = true; 286 mIsSymbolShifted = false; 287 mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; 288 mSwitchState = SWITCH_STATE_ALPHA; 289 mSwitchActions.requestUpdatingShiftState(); 290 } 291 292 private void setSymbolsKeyboard() { 293 if (DEBUG_ACTION) { 294 Log.d(TAG, "setSymbolsKeyboard"); 295 } 296 mSwitchActions.setSymbolsKeyboard(); 297 mIsAlphabetMode = false; 298 mIsSymbolShifted = false; 299 // Reset alphabet shift state. 300 mAlphabetShiftState.setShiftLocked(false); 301 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; 302 } 303 304 private void setSymbolsShiftedKeyboard() { 305 if (DEBUG_ACTION) { 306 Log.d(TAG, "setSymbolsShiftedKeyboard"); 307 } 308 mSwitchActions.setSymbolsShiftedKeyboard(); 309 mIsAlphabetMode = false; 310 mIsSymbolShifted = true; 311 // Reset alphabet shift state. 312 mAlphabetShiftState.setShiftLocked(false); 313 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; 314 } 315 316 public void onPressKey(final int code, final boolean isSinglePointer, final int autoCaps) { 317 if (DEBUG_EVENT) { 318 Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code) 319 + " single=" + isSinglePointer + " autoCaps=" + autoCaps + " " + this); 320 } 321 if (code != Constants.CODE_SHIFT) { 322 // Because the double tap shift key timer is to detect two consecutive shift key press, 323 // it should be canceled when a non-shift key is pressed. 324 mSwitchActions.cancelDoubleTapShiftKeyTimer(); 325 } 326 if (code == Constants.CODE_SHIFT) { 327 onPressShift(); 328 } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { 329 onPressSymbol(); 330 } else { 331 mLongPressShiftLockFired = false; 332 mShiftKeyState.onOtherKeyPressed(); 333 mSymbolKeyState.onOtherKeyPressed(); 334 // It is required to reset the auto caps state when all of the following conditions 335 // are met: 336 // 1) two or more fingers are in action 337 // 2) in alphabet layout 338 // 3) not in all characters caps mode 339 // As for #3, please note that it's required to check even when the auto caps mode is 340 // off because, for example, we may be in the #1 state within the manual temporary 341 // shifted mode. 342 if (!isSinglePointer && mIsAlphabetMode && autoCaps != TextUtils.CAP_MODE_CHARACTERS) { 343 final boolean needsToResetAutoCaps = mAlphabetShiftState.isAutomaticShifted() 344 || (mAlphabetShiftState.isManualShifted() && mShiftKeyState.isReleasing()); 345 if (needsToResetAutoCaps) { 346 mSwitchActions.setAlphabetKeyboard(); 347 } 348 } 349 } 350 } 351 352 public void onReleaseKey(final int code, final boolean withSliding) { 353 if (DEBUG_EVENT) { 354 Log.d(TAG, "onReleaseKey: code=" + Constants.printableCode(code) 355 + " sliding=" + withSliding + " " + this); 356 } 357 if (code == Constants.CODE_SHIFT) { 358 onReleaseShift(withSliding); 359 } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { 360 onReleaseSymbol(withSliding); 361 } 362 } 363 364 private void onPressSymbol() { 365 toggleAlphabetAndSymbols(); 366 mSymbolKeyState.onPress(); 367 mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL; 368 } 369 370 private void onReleaseSymbol(final boolean withSliding) { 371 if (mSymbolKeyState.isChording()) { 372 // Switch back to the previous keyboard mode if the user chords the mode change key and 373 // another key, then releases the mode change key. 374 toggleAlphabetAndSymbols(); 375 } else if (!withSliding) { 376 // If the mode change key is being released without sliding, we should forget the 377 // previous symbols keyboard shift state and simply switch back to symbols layout 378 // (never symbols shifted) next time the mode gets changed to symbols layout. 379 mPrevSymbolsKeyboardWasShifted = false; 380 } 381 mSymbolKeyState.onRelease(); 382 } 383 384 public void onUpdateShiftState(final int autoCaps, final int recapitalizeMode) { 385 if (DEBUG_EVENT) { 386 Log.d(TAG, "onUpdateShiftState: autoCaps=" + autoCaps + ", recapitalizeMode=" 387 + recapitalizeMode + " " + this); 388 } 389 mRecapitalizeMode = recapitalizeMode; 390 updateAlphabetShiftState(autoCaps, recapitalizeMode); 391 } 392 393 // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout 394 // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal(). 395 public void onResetKeyboardStateToAlphabet() { 396 if (DEBUG_EVENT) { 397 Log.d(TAG, "onResetKeyboardStateToAlphabet: " + this); 398 } 399 resetKeyboardStateToAlphabet(); 400 } 401 402 private void updateShiftStateForRecapitalize(final int recapitalizeMode) { 403 switch (recapitalizeMode) { 404 case RecapitalizeStatus.CAPS_MODE_ALL_UPPER: 405 setShifted(SHIFT_LOCK_SHIFTED); 406 break; 407 case RecapitalizeStatus.CAPS_MODE_FIRST_WORD_UPPER: 408 setShifted(AUTOMATIC_SHIFT); 409 break; 410 case RecapitalizeStatus.CAPS_MODE_ALL_LOWER: 411 case RecapitalizeStatus.CAPS_MODE_ORIGINAL_MIXED_CASE: 412 default: 413 setShifted(UNSHIFT); 414 } 415 } 416 417 private void updateAlphabetShiftState(final int autoCaps, final int recapitalizeMode) { 418 if (!mIsAlphabetMode) return; 419 if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != recapitalizeMode) { 420 // We are recapitalizing. Match the keyboard to the current recapitalize state. 421 updateShiftStateForRecapitalize(recapitalizeMode); 422 return; 423 } 424 if (!mShiftKeyState.isReleasing()) { 425 // Ignore update shift state event while the shift key is being pressed (including 426 // chording). 427 return; 428 } 429 if (!mAlphabetShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) { 430 if (mShiftKeyState.isReleasing() && autoCaps != Constants.TextUtils.CAP_MODE_OFF) { 431 // Only when shift key is releasing, automatic temporary upper case will be set. 432 setShifted(AUTOMATIC_SHIFT); 433 } else { 434 setShifted(mShiftKeyState.isChording() ? MANUAL_SHIFT : UNSHIFT); 435 } 436 } 437 } 438 439 private void onPressShift() { 440 mLongPressShiftLockFired = false; 441 // If we are recapitalizing, we don't do any of the normal processing, including 442 // importantly the double tap timer. 443 if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) { 444 return; 445 } 446 if (mIsAlphabetMode) { 447 mIsInDoubleTapShiftKey = mSwitchActions.isInDoubleTapShiftKeyTimeout(); 448 if (!mIsInDoubleTapShiftKey) { 449 // This is first tap. 450 mSwitchActions.startDoubleTapShiftKeyTimer(); 451 } 452 if (mIsInDoubleTapShiftKey) { 453 if (mAlphabetShiftState.isManualShifted() || mIsInAlphabetUnshiftedFromShifted) { 454 // Shift key has been double tapped while in manual shifted or automatic 455 // shifted state. 456 setShiftLocked(true); 457 } else { 458 // Shift key has been double tapped while in normal state. This is the second 459 // tap to disable shift locked state, so just ignore this. 460 } 461 } else { 462 if (mAlphabetShiftState.isShiftLocked()) { 463 // Shift key is pressed while shift locked state, we will treat this state as 464 // shift lock shifted state and mark as if shift key pressed while normal state. 465 setShifted(SHIFT_LOCK_SHIFTED); 466 mShiftKeyState.onPress(); 467 } else if (mAlphabetShiftState.isAutomaticShifted()) { 468 // Shift key is pressed while automatic shifted, we have to move to manual 469 // shifted. 470 setShifted(MANUAL_SHIFT); 471 mShiftKeyState.onPress(); 472 } else if (mAlphabetShiftState.isShiftedOrShiftLocked()) { 473 // In manual shifted state, we just record shift key has been pressing while 474 // shifted state. 475 mShiftKeyState.onPressOnShifted(); 476 } else { 477 // In base layout, chording or manual shifted mode is started. 478 setShifted(MANUAL_SHIFT); 479 mShiftKeyState.onPress(); 480 } 481 } 482 } else { 483 // In symbol mode, just toggle symbol and symbol more keyboard. 484 toggleShiftInSymbols(); 485 mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE; 486 mShiftKeyState.onPress(); 487 } 488 } 489 490 private void onReleaseShift(final boolean withSliding) { 491 if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) { 492 // We are recapitalizing. We should match the keyboard state to the recapitalize 493 // state in priority. 494 updateShiftStateForRecapitalize(mRecapitalizeMode); 495 } else if (mIsAlphabetMode) { 496 final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked(); 497 mIsInAlphabetUnshiftedFromShifted = false; 498 if (mIsInDoubleTapShiftKey) { 499 // Double tap shift key has been handled in {@link #onPressShift}, so that just 500 // ignore this release shift key here. 501 mIsInDoubleTapShiftKey = false; 502 } else if (mLongPressShiftLockFired) { 503 setShiftLocked(!mAlphabetShiftState.isShiftLocked()); 504 } else if (mShiftKeyState.isChording()) { 505 if (mAlphabetShiftState.isShiftLockShifted()) { 506 // After chording input while shift locked state. 507 setShiftLocked(true); 508 } else { 509 // After chording input while normal state. 510 setShifted(UNSHIFT); 511 } 512 // After chording input, automatic shift state may have been changed depending on 513 // what characters were input. 514 mShiftKeyState.onRelease(); 515 mSwitchActions.requestUpdatingShiftState(); 516 return; 517 } else if (mAlphabetShiftState.isShiftLockShifted() && withSliding) { 518 // In shift locked state, shift has been pressed and slid out to other key. 519 setShiftLocked(true); 520 } else if (mAlphabetShiftState.isManualShifted() && withSliding) { 521 // Shift has been pressed and slid out to other key. 522 mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_SHIFT; 523 } else if (isShiftLocked && !mAlphabetShiftState.isShiftLockShifted() 524 && (mShiftKeyState.isPressing() || mShiftKeyState.isPressingOnShifted()) 525 && !withSliding) { 526 // Shift has been long pressed, ignore this release. 527 } else if (isShiftLocked && !mShiftKeyState.isIgnoring() && !withSliding) { 528 // Shift has been pressed without chording while shift locked state. 529 setShiftLocked(false); 530 } else if (mAlphabetShiftState.isShiftedOrShiftLocked() 531 && mShiftKeyState.isPressingOnShifted() && !withSliding) { 532 // Shift has been pressed without chording while shifted state. 533 setShifted(UNSHIFT); 534 mIsInAlphabetUnshiftedFromShifted = true; 535 } else if (mAlphabetShiftState.isManualShiftedFromAutomaticShifted() 536 && mShiftKeyState.isPressing() && !withSliding) { 537 // Shift has been pressed without chording while manual shifted transited from 538 // automatic shifted 539 setShifted(UNSHIFT); 540 mIsInAlphabetUnshiftedFromShifted = true; 541 } 542 } else { 543 // In symbol mode, switch back to the previous keyboard mode if the user chords the 544 // shift key and another key, then releases the shift key. 545 if (mShiftKeyState.isChording()) { 546 toggleShiftInSymbols(); 547 } 548 } 549 mShiftKeyState.onRelease(); 550 } 551 552 public void onFinishSlidingInput() { 553 if (DEBUG_EVENT) { 554 Log.d(TAG, "onFinishSlidingInput: " + this); 555 } 556 // Switch back to the previous keyboard mode if the user cancels sliding input. 557 switch (mSwitchState) { 558 case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: 559 toggleAlphabetAndSymbols(); 560 break; 561 case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: 562 toggleShiftInSymbols(); 563 break; 564 case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT: 565 setAlphabetKeyboard(); 566 break; 567 } 568 } 569 570 private static boolean isSpaceCharacter(final int c) { 571 return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER; 572 } 573 574 public void onCodeInput(final int code, final int autoCaps) { 575 if (DEBUG_EVENT) { 576 Log.d(TAG, "onCodeInput: code=" + Constants.printableCode(code) 577 + " autoCaps=" + autoCaps + " " + this); 578 } 579 580 switch (mSwitchState) { 581 case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: 582 if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { 583 // Detected only the mode change key has been pressed, and then released. 584 if (mIsAlphabetMode) { 585 mSwitchState = SWITCH_STATE_ALPHA; 586 } else { 587 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; 588 } 589 } 590 break; 591 case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: 592 if (code == Constants.CODE_SHIFT) { 593 // Detected only the shift key has been pressed on symbol layout, and then released. 594 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; 595 } 596 break; 597 case SWITCH_STATE_SYMBOL_BEGIN: 598 if (!isSpaceCharacter(code) && (Constants.isLetterCode(code) 599 || code == Constants.CODE_OUTPUT_TEXT)) { 600 mSwitchState = SWITCH_STATE_SYMBOL; 601 } 602 break; 603 case SWITCH_STATE_SYMBOL: 604 // Switch back to alpha keyboard mode if user types one or more non-space/enter 605 // characters followed by a space/enter. 606 if (isSpaceCharacter(code)) { 607 toggleAlphabetAndSymbols(); 608 mPrevSymbolsKeyboardWasShifted = false; 609 } 610 break; 611 } 612 613 if (code == Constants.CODE_CAPSLOCK) { 614 // Changing shift lock state will be handled at {@link #onPressShift()} when the shift 615 // key is released. 616 mLongPressShiftLockFired = true; 617 } 618 619 // If the code is a letter, update keyboard shift state. 620 if (Constants.isLetterCode(code)) { 621 updateAlphabetShiftState(autoCaps, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); 622 } 623 } 624 625 static String shiftModeToString(final int shiftMode) { 626 switch (shiftMode) { 627 case UNSHIFT: return "UNSHIFT"; 628 case MANUAL_SHIFT: return "MANUAL"; 629 case AUTOMATIC_SHIFT: return "AUTOMATIC"; 630 default: return null; 631 } 632 } 633 634 private static String switchStateToString(final int switchState) { 635 switch (switchState) { 636 case SWITCH_STATE_ALPHA: return "ALPHA"; 637 case SWITCH_STATE_SYMBOL_BEGIN: return "SYMBOL-BEGIN"; 638 case SWITCH_STATE_SYMBOL: return "SYMBOL"; 639 case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: return "MOMENTARY-ALPHA-SYMBOL"; 640 case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: return "MOMENTARY-SYMBOL-MORE"; 641 case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT: return "MOMENTARY-ALPHA_SHIFT"; 642 default: return null; 643 } 644 } 645 646 @Override 647 public String toString() { 648 return "[keyboard=" + (mIsAlphabetMode ? mAlphabetShiftState.toString() 649 : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS")) 650 + " shift=" + mShiftKeyState 651 + " symbol=" + mSymbolKeyState 652 + " switch=" + switchStateToString(mSwitchState) + "]"; 653 } 654} 655