KeyboardState.java revision 93445b4821e9e8ecc7dd52f1a5d5316c7eec2654
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.utils.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()}, 33 * {@link #onUpdateShiftState(int,int)}, {@link #onResetKeyboardStateToAlphabet()}. 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 double tap. 85 private boolean mIsInAlphabetUnshiftedFromShifted; 86 private boolean mIsInDoubleTapShiftKey; 87 88 private final SavedKeyboardState mSavedKeyboardState = new SavedKeyboardState(); 89 90 static final class SavedKeyboardState { 91 public boolean mIsValid; 92 public boolean mIsAlphabetMode; 93 public boolean mIsAlphabetShiftLocked; 94 public int mShiftMode; 95 96 @Override 97 public String toString() { 98 if (!mIsValid) return "INVALID"; 99 if (mIsAlphabetMode) { 100 if (mIsAlphabetShiftLocked) return "ALPHABET_SHIFT_LOCKED"; 101 return "ALPHABET_" + shiftModeToString(mShiftMode); 102 } else { 103 return "SYMBOLS_" + shiftModeToString(mShiftMode); 104 } 105 } 106 } 107 108 public KeyboardState(final SwitchActions switchActions) { 109 mSwitchActions = switchActions; 110 mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; 111 } 112 113 public void onLoadKeyboard() { 114 if (DEBUG_EVENT) { 115 Log.d(TAG, "onLoadKeyboard: " + this); 116 } 117 // Reset alphabet shift state. 118 mAlphabetShiftState.setShiftLocked(false); 119 mPrevMainKeyboardWasShiftLocked = false; 120 mPrevSymbolsKeyboardWasShifted = false; 121 mShiftKeyState.onRelease(); 122 mSymbolKeyState.onRelease(); 123 onRestoreKeyboardState(); 124 } 125 126 private static final int UNSHIFT = 0; 127 private static final int MANUAL_SHIFT = 1; 128 private static final int AUTOMATIC_SHIFT = 2; 129 private static final int SHIFT_LOCK_SHIFTED = 3; 130 131 public void onSaveKeyboardState() { 132 final SavedKeyboardState state = mSavedKeyboardState; 133 state.mIsAlphabetMode = mIsAlphabetMode; 134 if (mIsAlphabetMode) { 135 state.mIsAlphabetShiftLocked = mAlphabetShiftState.isShiftLocked(); 136 state.mShiftMode = mAlphabetShiftState.isAutomaticShifted() ? AUTOMATIC_SHIFT 137 : (mAlphabetShiftState.isShiftedOrShiftLocked() ? MANUAL_SHIFT : UNSHIFT); 138 } else { 139 state.mIsAlphabetShiftLocked = mPrevMainKeyboardWasShiftLocked; 140 state.mShiftMode = mIsSymbolShifted ? MANUAL_SHIFT : UNSHIFT; 141 } 142 state.mIsValid = true; 143 if (DEBUG_EVENT) { 144 Log.d(TAG, "onSaveKeyboardState: saved=" + state + " " + this); 145 } 146 } 147 148 private void onRestoreKeyboardState() { 149 final SavedKeyboardState state = mSavedKeyboardState; 150 if (DEBUG_EVENT) { 151 Log.d(TAG, "onRestoreKeyboardState: saved=" + state + " " + this); 152 } 153 if (!state.mIsValid || state.mIsAlphabetMode) { 154 setAlphabetKeyboard(); 155 } else { 156 if (state.mShiftMode == MANUAL_SHIFT) { 157 setSymbolsShiftedKeyboard(); 158 } else { 159 setSymbolsKeyboard(); 160 } 161 } 162 163 if (!state.mIsValid) return; 164 state.mIsValid = false; 165 166 if (state.mIsAlphabetMode) { 167 setShiftLocked(state.mIsAlphabetShiftLocked); 168 if (!state.mIsAlphabetShiftLocked) { 169 setShifted(state.mShiftMode); 170 } 171 } else { 172 mPrevMainKeyboardWasShiftLocked = state.mIsAlphabetShiftLocked; 173 } 174 } 175 176 private void setShifted(final int shiftMode) { 177 if (DEBUG_ACTION) { 178 Log.d(TAG, "setShifted: shiftMode=" + shiftModeToString(shiftMode) + " " + this); 179 } 180 if (!mIsAlphabetMode) return; 181 final int prevShiftMode; 182 if (mAlphabetShiftState.isAutomaticShifted()) { 183 prevShiftMode = AUTOMATIC_SHIFT; 184 } else if (mAlphabetShiftState.isManualShifted()) { 185 prevShiftMode = MANUAL_SHIFT; 186 } else { 187 prevShiftMode = UNSHIFT; 188 } 189 switch (shiftMode) { 190 case AUTOMATIC_SHIFT: 191 mAlphabetShiftState.setAutomaticShifted(); 192 if (shiftMode != prevShiftMode) { 193 mSwitchActions.setAlphabetAutomaticShiftedKeyboard(); 194 } 195 break; 196 case MANUAL_SHIFT: 197 mAlphabetShiftState.setShifted(true); 198 if (shiftMode != prevShiftMode) { 199 mSwitchActions.setAlphabetManualShiftedKeyboard(); 200 } 201 break; 202 case UNSHIFT: 203 mAlphabetShiftState.setShifted(false); 204 if (shiftMode != prevShiftMode) { 205 mSwitchActions.setAlphabetKeyboard(); 206 } 207 break; 208 case SHIFT_LOCK_SHIFTED: 209 mAlphabetShiftState.setShifted(true); 210 mSwitchActions.setAlphabetShiftLockShiftedKeyboard(); 211 break; 212 } 213 } 214 215 private void setShiftLocked(final boolean shiftLocked) { 216 if (DEBUG_ACTION) { 217 Log.d(TAG, "setShiftLocked: shiftLocked=" + shiftLocked + " " + this); 218 } 219 if (!mIsAlphabetMode) return; 220 if (shiftLocked && (!mAlphabetShiftState.isShiftLocked() 221 || mAlphabetShiftState.isShiftLockShifted())) { 222 mSwitchActions.setAlphabetShiftLockedKeyboard(); 223 } 224 if (!shiftLocked && mAlphabetShiftState.isShiftLocked()) { 225 mSwitchActions.setAlphabetKeyboard(); 226 } 227 mAlphabetShiftState.setShiftLocked(shiftLocked); 228 } 229 230 private void toggleAlphabetAndSymbols() { 231 if (DEBUG_ACTION) { 232 Log.d(TAG, "toggleAlphabetAndSymbols: " + this); 233 } 234 if (mIsAlphabetMode) { 235 mPrevMainKeyboardWasShiftLocked = mAlphabetShiftState.isShiftLocked(); 236 if (mPrevSymbolsKeyboardWasShifted) { 237 setSymbolsShiftedKeyboard(); 238 } else { 239 setSymbolsKeyboard(); 240 } 241 mPrevSymbolsKeyboardWasShifted = false; 242 } else { 243 mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted; 244 setAlphabetKeyboard(); 245 if (mPrevMainKeyboardWasShiftLocked) { 246 setShiftLocked(true); 247 } 248 mPrevMainKeyboardWasShiftLocked = false; 249 } 250 } 251 252 // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout 253 // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal(). 254 private void resetKeyboardStateToAlphabet() { 255 if (DEBUG_ACTION) { 256 Log.d(TAG, "resetKeyboardStateToAlphabet: " + this); 257 } 258 if (mIsAlphabetMode) return; 259 260 mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted; 261 setAlphabetKeyboard(); 262 if (mPrevMainKeyboardWasShiftLocked) { 263 setShiftLocked(true); 264 } 265 mPrevMainKeyboardWasShiftLocked = false; 266 } 267 268 private void toggleShiftInSymbols() { 269 if (mIsSymbolShifted) { 270 setSymbolsKeyboard(); 271 } else { 272 setSymbolsShiftedKeyboard(); 273 } 274 } 275 276 private void setAlphabetKeyboard() { 277 if (DEBUG_ACTION) { 278 Log.d(TAG, "setAlphabetKeyboard"); 279 } 280 281 mSwitchActions.setAlphabetKeyboard(); 282 mIsAlphabetMode = true; 283 mIsSymbolShifted = false; 284 mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; 285 mSwitchState = SWITCH_STATE_ALPHA; 286 mSwitchActions.requestUpdatingShiftState(); 287 } 288 289 private void setSymbolsKeyboard() { 290 if (DEBUG_ACTION) { 291 Log.d(TAG, "setSymbolsKeyboard"); 292 } 293 mSwitchActions.setSymbolsKeyboard(); 294 mIsAlphabetMode = false; 295 mIsSymbolShifted = false; 296 // Reset alphabet shift state. 297 mAlphabetShiftState.setShiftLocked(false); 298 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; 299 } 300 301 private void setSymbolsShiftedKeyboard() { 302 if (DEBUG_ACTION) { 303 Log.d(TAG, "setSymbolsShiftedKeyboard"); 304 } 305 mSwitchActions.setSymbolsShiftedKeyboard(); 306 mIsAlphabetMode = false; 307 mIsSymbolShifted = true; 308 // Reset alphabet shift state. 309 mAlphabetShiftState.setShiftLocked(false); 310 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; 311 } 312 313 public void onPressKey(final int code, final boolean isSinglePointer, final int autoCaps) { 314 if (DEBUG_EVENT) { 315 Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code) 316 + " single=" + isSinglePointer + " autoCaps=" + autoCaps + " " + this); 317 } 318 if (code != Constants.CODE_SHIFT) { 319 // Because the double tap shift key timer is to detect two consecutive shift key press, 320 // it should be canceled when a non-shift key is pressed. 321 mSwitchActions.cancelDoubleTapShiftKeyTimer(); 322 } 323 if (code == Constants.CODE_SHIFT) { 324 onPressShift(); 325 } else if (code == Constants.CODE_CAPSLOCK) { 326 // Nothing to do here. See {@link #onReleaseKey(int,boolean)}. 327 } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { 328 onPressSymbol(); 329 } else { 330 mShiftKeyState.onOtherKeyPressed(); 331 mSymbolKeyState.onOtherKeyPressed(); 332 // It is required to reset the auto caps state when all of the following conditions 333 // are met: 334 // 1) two or more fingers are in action 335 // 2) in alphabet layout 336 // 3) not in all characters caps mode 337 // As for #3, please note that it's required to check even when the auto caps mode is 338 // off because, for example, we may be in the #1 state within the manual temporary 339 // shifted mode. 340 if (!isSinglePointer && mIsAlphabetMode && autoCaps != TextUtils.CAP_MODE_CHARACTERS) { 341 final boolean needsToResetAutoCaps = mAlphabetShiftState.isAutomaticShifted() 342 || (mAlphabetShiftState.isManualShifted() && mShiftKeyState.isReleasing()); 343 if (needsToResetAutoCaps) { 344 mSwitchActions.setAlphabetKeyboard(); 345 } 346 } 347 } 348 } 349 350 public void onReleaseKey(final int code, final boolean withSliding) { 351 if (DEBUG_EVENT) { 352 Log.d(TAG, "onReleaseKey: code=" + Constants.printableCode(code) 353 + " sliding=" + withSliding + " " + this); 354 } 355 if (code == Constants.CODE_SHIFT) { 356 onReleaseShift(withSliding); 357 } else if (code == Constants.CODE_CAPSLOCK) { 358 setShiftLocked(!mAlphabetShiftState.isShiftLocked()); 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 // If we are recapitalizing, we don't do any of the normal processing, including 441 // importantly the double tap timer. 442 if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) { 443 return; 444 } 445 if (mIsAlphabetMode) { 446 mIsInDoubleTapShiftKey = mSwitchActions.isInDoubleTapShiftKeyTimeout(); 447 if (!mIsInDoubleTapShiftKey) { 448 // This is first tap. 449 mSwitchActions.startDoubleTapShiftKeyTimer(); 450 } 451 if (mIsInDoubleTapShiftKey) { 452 if (mAlphabetShiftState.isManualShifted() || mIsInAlphabetUnshiftedFromShifted) { 453 // Shift key has been double tapped while in manual shifted or automatic 454 // shifted state. 455 setShiftLocked(true); 456 } else { 457 // Shift key has been double tapped while in normal state. This is the second 458 // tap to disable shift locked state, so just ignore this. 459 } 460 } else { 461 if (mAlphabetShiftState.isShiftLocked()) { 462 // Shift key is pressed while shift locked state, we will treat this state as 463 // shift lock shifted state and mark as if shift key pressed while normal state. 464 setShifted(SHIFT_LOCK_SHIFTED); 465 mShiftKeyState.onPress(); 466 } else if (mAlphabetShiftState.isAutomaticShifted()) { 467 // Shift key is pressed while automatic shifted, we have to move to manual 468 // shifted. 469 setShifted(MANUAL_SHIFT); 470 mShiftKeyState.onPress(); 471 } else if (mAlphabetShiftState.isShiftedOrShiftLocked()) { 472 // In manual shifted state, we just record shift key has been pressing while 473 // shifted state. 474 mShiftKeyState.onPressOnShifted(); 475 } else { 476 // In base layout, chording or manual shifted mode is started. 477 setShifted(MANUAL_SHIFT); 478 mShiftKeyState.onPress(); 479 } 480 } 481 } else { 482 // In symbol mode, just toggle symbol and symbol more keyboard. 483 toggleShiftInSymbols(); 484 mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE; 485 mShiftKeyState.onPress(); 486 } 487 } 488 489 private void onReleaseShift(final boolean withSliding) { 490 if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) { 491 // We are recapitalizing. We should match the keyboard state to the recapitalize 492 // state in priority. 493 updateShiftStateForRecapitalize(mRecapitalizeMode); 494 } else if (mIsAlphabetMode) { 495 final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked(); 496 mIsInAlphabetUnshiftedFromShifted = false; 497 if (mIsInDoubleTapShiftKey) { 498 // Double tap shift key has been handled in {@link #onPressShift}, so that just 499 // ignore this release shift key here. 500 mIsInDoubleTapShiftKey = false; 501 } else if (mShiftKeyState.isChording()) { 502 if (mAlphabetShiftState.isShiftLockShifted()) { 503 // After chording input while shift locked state. 504 setShiftLocked(true); 505 } else { 506 // After chording input while normal state. 507 setShifted(UNSHIFT); 508 } 509 // After chording input, automatic shift state may have been changed depending on 510 // what characters were input. 511 mShiftKeyState.onRelease(); 512 mSwitchActions.requestUpdatingShiftState(); 513 return; 514 } else if (mAlphabetShiftState.isShiftLockShifted() && withSliding) { 515 // In shift locked state, shift has been pressed and slid out to other key. 516 setShiftLocked(true); 517 } else if (mAlphabetShiftState.isManualShifted() && withSliding) { 518 // Shift has been pressed and slid out to other key. 519 mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_SHIFT; 520 } else if (isShiftLocked && !mAlphabetShiftState.isShiftLockShifted() 521 && (mShiftKeyState.isPressing() || mShiftKeyState.isPressingOnShifted()) 522 && !withSliding) { 523 // Shift has been long pressed, ignore this release. 524 } else if (isShiftLocked && !mShiftKeyState.isIgnoring() && !withSliding) { 525 // Shift has been pressed without chording while shift locked state. 526 setShiftLocked(false); 527 } else if (mAlphabetShiftState.isShiftedOrShiftLocked() 528 && mShiftKeyState.isPressingOnShifted() && !withSliding) { 529 // Shift has been pressed without chording while shifted state. 530 setShifted(UNSHIFT); 531 mIsInAlphabetUnshiftedFromShifted = true; 532 } else if (mAlphabetShiftState.isManualShiftedFromAutomaticShifted() 533 && mShiftKeyState.isPressing() && !withSliding) { 534 // Shift has been pressed without chording while manual shifted transited from 535 // automatic shifted 536 setShifted(UNSHIFT); 537 mIsInAlphabetUnshiftedFromShifted = true; 538 } 539 } else { 540 // In symbol mode, switch back to the previous keyboard mode if the user chords the 541 // shift key and another key, then releases the shift key. 542 if (mShiftKeyState.isChording()) { 543 toggleShiftInSymbols(); 544 } 545 } 546 mShiftKeyState.onRelease(); 547 } 548 549 public void onFinishSlidingInput() { 550 if (DEBUG_EVENT) { 551 Log.d(TAG, "onFinishSlidingInput: " + this); 552 } 553 // Switch back to the previous keyboard mode if the user cancels sliding input. 554 switch (mSwitchState) { 555 case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: 556 toggleAlphabetAndSymbols(); 557 break; 558 case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: 559 toggleShiftInSymbols(); 560 break; 561 case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT: 562 setAlphabetKeyboard(); 563 break; 564 } 565 } 566 567 private static boolean isSpaceCharacter(final int c) { 568 return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER; 569 } 570 571 public void onCodeInput(final int code, final int autoCaps) { 572 if (DEBUG_EVENT) { 573 Log.d(TAG, "onCodeInput: code=" + Constants.printableCode(code) 574 + " autoCaps=" + autoCaps + " " + this); 575 } 576 577 switch (mSwitchState) { 578 case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: 579 if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) { 580 // Detected only the mode change key has been pressed, and then released. 581 if (mIsAlphabetMode) { 582 mSwitchState = SWITCH_STATE_ALPHA; 583 } else { 584 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; 585 } 586 } 587 break; 588 case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: 589 if (code == Constants.CODE_SHIFT) { 590 // Detected only the shift key has been pressed on symbol layout, and then released. 591 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; 592 } 593 break; 594 case SWITCH_STATE_SYMBOL_BEGIN: 595 if (!isSpaceCharacter(code) && (Constants.isLetterCode(code) 596 || code == Constants.CODE_OUTPUT_TEXT)) { 597 mSwitchState = SWITCH_STATE_SYMBOL; 598 } 599 break; 600 case SWITCH_STATE_SYMBOL: 601 // Switch back to alpha keyboard mode if user types one or more non-space/enter 602 // characters followed by a space/enter. 603 if (isSpaceCharacter(code)) { 604 toggleAlphabetAndSymbols(); 605 mPrevSymbolsKeyboardWasShifted = false; 606 } 607 break; 608 } 609 610 // If the code is a letter, update keyboard shift state. 611 if (Constants.isLetterCode(code)) { 612 updateAlphabetShiftState(autoCaps, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); 613 } 614 } 615 616 static String shiftModeToString(final int shiftMode) { 617 switch (shiftMode) { 618 case UNSHIFT: return "UNSHIFT"; 619 case MANUAL_SHIFT: return "MANUAL"; 620 case AUTOMATIC_SHIFT: return "AUTOMATIC"; 621 default: return null; 622 } 623 } 624 625 private static String switchStateToString(final int switchState) { 626 switch (switchState) { 627 case SWITCH_STATE_ALPHA: return "ALPHA"; 628 case SWITCH_STATE_SYMBOL_BEGIN: return "SYMBOL-BEGIN"; 629 case SWITCH_STATE_SYMBOL: return "SYMBOL"; 630 case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: return "MOMENTARY-ALPHA-SYMBOL"; 631 case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: return "MOMENTARY-SYMBOL-MORE"; 632 case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT: return "MOMENTARY-ALPHA_SHIFT"; 633 default: return null; 634 } 635 } 636 637 @Override 638 public String toString() { 639 return "[keyboard=" + (mIsAlphabetMode ? mAlphabetShiftState.toString() 640 : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS")) 641 + " shift=" + mShiftKeyState 642 + " symbol=" + mSymbolKeyState 643 + " switch=" + switchStateToString(mSwitchState) + "]"; 644 } 645} 646