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