KeyboardSwitcher.java revision 8bec4aa912c193135bebacfc75dc15f06c5dce6e
1/* 2 * Copyright (C) 2008 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 17package com.android.inputmethod.keyboard; 18 19import com.android.inputmethod.latin.LatinIME; 20import com.android.inputmethod.latin.Settings; 21import com.android.inputmethod.latin.Utils; 22import com.android.inputmethod.latin.LatinImeLogger; 23import com.android.inputmethod.latin.R; 24import com.android.inputmethod.latin.SubtypeSwitcher; 25 26import android.content.Context; 27import android.content.SharedPreferences; 28import android.content.res.Resources; 29import android.util.Log; 30import android.view.InflateException; 31import android.view.inputmethod.InputMethodManager; 32 33import java.lang.ref.SoftReference; 34import java.util.HashMap; 35import java.util.Locale; 36 37public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener { 38 private static final String TAG = "KeyboardSwitcher"; 39 private static final boolean DEBUG = false; 40 public static final boolean DEBUG_STATE = false; 41 42 private static String sConfigDefaultKeyboardThemeId; 43 public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902"; 44 private static final int[] KEYBOARD_THEMES = { 45 R.layout.input_basic, 46 R.layout.input_basic_highcontrast, 47 R.layout.input_stone_normal, 48 R.layout.input_stone_bold, 49 R.layout.input_gingerbread, 50 R.layout.input_honeycomb, 51 }; 52 53 private SubtypeSwitcher mSubtypeSwitcher; 54 private SharedPreferences mPrefs; 55 56 private LatinKeyboardView mInputView; 57 private LatinIME mInputMethodService; 58 59 private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift"); 60 private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol"); 61 62 private KeyboardId mSymbolsId; 63 private KeyboardId mSymbolsShiftedId; 64 65 private KeyboardId mCurrentId; 66 private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache = 67 new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); 68 69 private int mMode = KeyboardId.MODE_TEXT; /* default value */ 70 private int mImeOptions; 71 private boolean mIsSymbols; 72 /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of 73 * what user actually typed. */ 74 private boolean mIsAutoCorrectionActive; 75 private boolean mVoiceKeyEnabled; 76 private boolean mVoiceButtonOnPrimary; 77 78 private static final int AUTO_MODE_SWITCH_STATE_ALPHA = 0; 79 private static final int AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN = 1; 80 private static final int AUTO_MODE_SWITCH_STATE_SYMBOL = 2; 81 // The following states are used only on the distinct multi-touch panel devices. 82 private static final int AUTO_MODE_SWITCH_STATE_MOMENTARY = 3; 83 private static final int AUTO_MODE_SWITCH_STATE_CHORDING = 4; 84 private int mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; 85 86 // Indicates whether or not we have the settings key 87 private boolean mHasSettingsKey; 88 private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto; 89 private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW = 90 R.string.settings_key_mode_always_show; 91 // NOTE: No need to have SETTINGS_KEY_MODE_ALWAYS_HIDE here because it's not being referred to 92 // in the source code now. 93 // Default is SETTINGS_KEY_MODE_AUTO. 94 private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO; 95 96 private int mLayoutId; 97 98 private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); 99 100 public static KeyboardSwitcher getInstance() { 101 return sInstance; 102 } 103 104 private KeyboardSwitcher() { 105 // Intentional empty constructor for singleton. 106 } 107 108 public static void init(LatinIME ims, SharedPreferences prefs) { 109 sInstance.mInputMethodService = ims; 110 sInstance.mPrefs = prefs; 111 sInstance.mSubtypeSwitcher = SubtypeSwitcher.getInstance(); 112 113 try { 114 sConfigDefaultKeyboardThemeId = ims.getString( 115 R.string.config_default_keyboard_theme_id); 116 sInstance.mLayoutId = Integer.valueOf( 117 prefs.getString(PREF_KEYBOARD_LAYOUT, sConfigDefaultKeyboardThemeId)); 118 } catch (NumberFormatException e) { 119 sConfigDefaultKeyboardThemeId = "0"; 120 sInstance.mLayoutId = 0; 121 } 122 prefs.registerOnSharedPreferenceChangeListener(sInstance); 123 } 124 125 private void makeSymbolsKeyboardIds() { 126 final Locale locale = mSubtypeSwitcher.getInputLocale(); 127 final Resources res = mInputMethodService.getResources(); 128 final int orientation = res.getConfiguration().orientation; 129 final int mode = mMode; 130 final int colorScheme = getColorScheme(); 131 final boolean hasSettingsKey = mHasSettingsKey; 132 final boolean voiceKeyEnabled = mVoiceKeyEnabled; 133 final boolean hasVoiceKey = voiceKeyEnabled && !mVoiceButtonOnPrimary; 134 final int imeOptions = mImeOptions; 135 // Note: This comment is only applied for phone number keyboard layout. 136 // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch 137 // between "phone keyboard" and "phone symbols keyboard". But on xlarge device, 138 // "@integer/key_shift" key code is used for that purpose in order to properly display 139 // "more" and "locked more" key labels. To achieve these behavior, we should initialize 140 // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard" 141 // respectively here for xlarge device's layout switching. 142 int xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols; 143 mSymbolsId = new KeyboardId( 144 res.getResourceEntryName(xmlId), xmlId, locale, orientation, mode, colorScheme, 145 hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true); 146 xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift; 147 mSymbolsShiftedId = new KeyboardId( 148 res.getResourceEntryName(xmlId), xmlId, locale, orientation, mode, colorScheme, 149 hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true); 150 } 151 152 private boolean hasVoiceKey(boolean isSymbols) { 153 return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary); 154 } 155 156 public void loadKeyboard(int mode, int imeOptions, boolean voiceKeyEnabled, 157 boolean voiceButtonOnPrimary) { 158 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; 159 try { 160 if (mInputView == null) return; 161 final Keyboard oldKeyboard = mInputView.getKeyboard(); 162 loadKeyboardInternal(mode, imeOptions, voiceKeyEnabled, voiceButtonOnPrimary, false); 163 final Keyboard newKeyboard = mInputView.getKeyboard(); 164 if (newKeyboard.isAlphaKeyboard() && (oldKeyboard == null 165 || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale))) { 166 mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(); 167 } 168 } catch (RuntimeException e) { 169 Log.w(TAG, e); 170 LatinImeLogger.logOnException(mode + "," + imeOptions, e); 171 } 172 } 173 174 private void loadKeyboardInternal(int mode, int imeOptions, boolean voiceButtonEnabled, 175 boolean voiceButtonOnPrimary, boolean isSymbols) { 176 if (mInputView == null) return; 177 178 mMode = mode; 179 mImeOptions = imeOptions; 180 mVoiceKeyEnabled = voiceButtonEnabled; 181 mVoiceButtonOnPrimary = voiceButtonOnPrimary; 182 mIsSymbols = isSymbols; 183 // Update the settings key state because number of enabled IMEs could have been changed 184 mHasSettingsKey = getSettingsKeyMode(mPrefs, mInputMethodService); 185 final KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols); 186 187 final Keyboard oldKeyboard = mInputView.getKeyboard(); 188 if (oldKeyboard != null && oldKeyboard.mId.equals(id)) 189 return; 190 191 makeSymbolsKeyboardIds(); 192 mCurrentId = id; 193 mInputView.setPreviewEnabled(mInputMethodService.getPopupOn()); 194 mInputView.setKeyboard(getKeyboard(id)); 195 } 196 197 private LatinKeyboard getKeyboard(KeyboardId id) { 198 final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id); 199 LatinKeyboard keyboard = (ref == null) ? null : ref.get(); 200 if (keyboard == null) { 201 final Locale savedLocale = mSubtypeSwitcher.changeSystemLocale( 202 mSubtypeSwitcher.getInputLocale()); 203 204 keyboard = new LatinKeyboard(mInputMethodService, id); 205 206 if (id.mEnableShiftLock) { 207 keyboard.enableShiftLock(); 208 } 209 210 mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard)); 211 if (DEBUG) 212 Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": " 213 + ((ref == null) ? "LOAD" : "GCed") + " id=" + id); 214 215 mSubtypeSwitcher.changeSystemLocale(savedLocale); 216 } else if (DEBUG) { 217 Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT id=" + id); 218 } 219 220 keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive); 221 keyboard.setShifted(false); 222 // If the cached keyboard had been switched to another keyboard while the language was 223 // displayed on its spacebar, it might have had arbitrary text fade factor. In such case, 224 // we should reset the text fade factor. 225 keyboard.setSpacebarTextFadeFactor(0.0f, null); 226 return keyboard; 227 } 228 229 private KeyboardId getKeyboardId(int mode, int imeOptions, boolean isSymbols) { 230 final boolean hasVoiceKey = hasVoiceKey(isSymbols); 231 final int charColorId = getColorScheme(); 232 final int xmlId; 233 final boolean enableShiftLock; 234 235 if (isSymbols) { 236 if (mode == KeyboardId.MODE_PHONE) { 237 xmlId = R.xml.kbd_phone_symbols; 238 } else if (mode == KeyboardId.MODE_NUMBER) { 239 // Note: MODE_NUMBER keyboard layout has no "switch alpha symbol" key. 240 xmlId = R.xml.kbd_number; 241 } else { 242 xmlId = R.xml.kbd_symbols; 243 } 244 enableShiftLock = false; 245 } else { 246 if (mode == KeyboardId.MODE_PHONE) { 247 xmlId = R.xml.kbd_phone; 248 enableShiftLock = false; 249 } else if (mode == KeyboardId.MODE_NUMBER) { 250 xmlId = R.xml.kbd_number; 251 enableShiftLock = false; 252 } else { 253 xmlId = R.xml.kbd_qwerty; 254 enableShiftLock = true; 255 } 256 } 257 final Resources res = mInputMethodService.getResources(); 258 final int orientation = res.getConfiguration().orientation; 259 final Locale locale = mSubtypeSwitcher.getInputLocale(); 260 return new KeyboardId( 261 res.getResourceEntryName(xmlId), xmlId, locale, orientation, mode, charColorId, 262 mHasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, imeOptions, enableShiftLock); 263 } 264 265 public int getKeyboardMode() { 266 return mMode; 267 } 268 269 public boolean isAlphabetMode() { 270 return mCurrentId != null && mCurrentId.isAlphabetKeyboard(); 271 } 272 273 public boolean isInputViewShown() { 274 return mInputView != null && mInputView.isShown(); 275 } 276 277 public boolean isKeyboardAvailable() { 278 if (mInputView != null) 279 return mInputView.getLatinKeyboard() != null; 280 return false; 281 } 282 283 private LatinKeyboard getLatinKeyboard() { 284 if (mInputView != null) 285 return mInputView.getLatinKeyboard(); 286 return null; 287 } 288 289 public void setPreferredLetters(int[] frequencies) { 290 LatinKeyboard latinKeyboard = getLatinKeyboard(); 291 if (latinKeyboard != null) 292 latinKeyboard.setPreferredLetters(frequencies); 293 } 294 295 public void keyReleased() { 296 LatinKeyboard latinKeyboard = getLatinKeyboard(); 297 if (latinKeyboard != null) 298 latinKeyboard.keyReleased(); 299 } 300 301 public boolean isShiftedOrShiftLocked() { 302 LatinKeyboard latinKeyboard = getLatinKeyboard(); 303 if (latinKeyboard != null) 304 return latinKeyboard.isShiftedOrShiftLocked(); 305 return false; 306 } 307 308 public boolean isShiftLocked() { 309 LatinKeyboard latinKeyboard = getLatinKeyboard(); 310 if (latinKeyboard != null) 311 return latinKeyboard.isShiftLocked(); 312 return false; 313 } 314 315 public boolean isAutomaticTemporaryUpperCase() { 316 LatinKeyboard latinKeyboard = getLatinKeyboard(); 317 if (latinKeyboard != null) 318 return latinKeyboard.isAutomaticTemporaryUpperCase(); 319 return false; 320 } 321 322 public boolean isManualTemporaryUpperCase() { 323 LatinKeyboard latinKeyboard = getLatinKeyboard(); 324 if (latinKeyboard != null) 325 return latinKeyboard.isManualTemporaryUpperCase(); 326 return false; 327 } 328 329 private void setManualTemporaryUpperCase(boolean shifted) { 330 LatinKeyboard latinKeyboard = getLatinKeyboard(); 331 if (latinKeyboard != null) { 332 // On non-distinct multi touch panel device, we should also turn off the shift locked 333 // state when shift key is pressed to go to normal mode. 334 // On the other hand, on distinct multi touch panel device, turning off the shift locked 335 // state with shift key pressing is handled by onReleaseShift(). 336 if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) { 337 latinKeyboard.setShiftLocked(false); 338 } 339 if (latinKeyboard.setShifted(shifted)) { 340 mInputView.invalidateAllKeys(); 341 } 342 } 343 } 344 345 private void setShiftLocked(boolean shiftLocked) { 346 LatinKeyboard latinKeyboard = getLatinKeyboard(); 347 if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) { 348 mInputView.invalidateAllKeys(); 349 } 350 } 351 352 /** 353 * Toggle keyboard shift state triggered by user touch event. 354 */ 355 public void toggleShift() { 356 mInputMethodService.mHandler.cancelUpdateShiftState(); 357 if (DEBUG_STATE) 358 Log.d(TAG, "toggleShift:" 359 + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() 360 + " shiftKeyState=" + mShiftKeyState); 361 if (isAlphabetMode()) { 362 setManualTemporaryUpperCase(!isShiftedOrShiftLocked()); 363 } else { 364 toggleShiftInSymbol(); 365 } 366 } 367 368 public void toggleCapsLock() { 369 mInputMethodService.mHandler.cancelUpdateShiftState(); 370 if (DEBUG_STATE) 371 Log.d(TAG, "toggleCapsLock:" 372 + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() 373 + " shiftKeyState=" + mShiftKeyState); 374 if (isAlphabetMode()) { 375 if (isShiftLocked()) { 376 // Shift key is long pressed while caps lock state, we will toggle back to normal 377 // state. And mark as if shift key is released. 378 setShiftLocked(false); 379 mShiftKeyState.onRelease(); 380 } else { 381 setShiftLocked(true); 382 } 383 } 384 } 385 386 private void setAutomaticTemporaryUpperCase() { 387 LatinKeyboard latinKeyboard = getLatinKeyboard(); 388 if (latinKeyboard != null) { 389 latinKeyboard.setAutomaticTemporaryUpperCase(); 390 mInputView.invalidateAllKeys(); 391 } 392 } 393 394 /** 395 * Update keyboard shift state triggered by connected EditText status change. 396 */ 397 public void updateShiftState() { 398 final ShiftKeyState shiftKeyState = mShiftKeyState; 399 if (DEBUG_STATE) 400 Log.d(TAG, "updateShiftState:" 401 + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState() 402 + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() 403 + " shiftKeyState=" + shiftKeyState); 404 if (isAlphabetMode()) { 405 if (!isShiftLocked() && !shiftKeyState.isIgnoring()) { 406 if (shiftKeyState.isReleasing() && mInputMethodService.getCurrentAutoCapsState()) { 407 // Only when shift key is releasing, automatic temporary upper case will be set. 408 setAutomaticTemporaryUpperCase(); 409 } else { 410 setManualTemporaryUpperCase(shiftKeyState.isMomentary()); 411 } 412 } 413 } else { 414 // In symbol keyboard mode, we should clear shift key state because only alphabet 415 // keyboard has shift key. 416 shiftKeyState.onRelease(); 417 } 418 } 419 420 public void changeKeyboardMode() { 421 if (DEBUG_STATE) 422 Log.d(TAG, "changeKeyboardMode:" 423 + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() 424 + " shiftKeyState=" + mShiftKeyState); 425 toggleKeyboardMode(); 426 if (isShiftLocked() && isAlphabetMode()) 427 setShiftLocked(true); 428 updateShiftState(); 429 } 430 431 public void onPressShift() { 432 if (!isKeyboardAvailable()) 433 return; 434 ShiftKeyState shiftKeyState = mShiftKeyState; 435 if (DEBUG_STATE) 436 Log.d(TAG, "onPressShift:" 437 + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() 438 + " shiftKeyState=" + shiftKeyState); 439 if (isAlphabetMode()) { 440 if (isShiftLocked()) { 441 // Shift key is pressed while caps lock state, we will treat this state as shifted 442 // caps lock state and mark as if shift key pressed while normal state. 443 shiftKeyState.onPress(); 444 setManualTemporaryUpperCase(true); 445 } else if (isAutomaticTemporaryUpperCase()) { 446 // Shift key is pressed while automatic temporary upper case, we have to move to 447 // manual temporary upper case. 448 shiftKeyState.onPress(); 449 setManualTemporaryUpperCase(true); 450 } else if (isShiftedOrShiftLocked()) { 451 // In manual upper case state, we just record shift key has been pressing while 452 // shifted state. 453 shiftKeyState.onPressOnShifted(); 454 } else { 455 // In base layout, chording or manual temporary upper case mode is started. 456 shiftKeyState.onPress(); 457 toggleShift(); 458 } 459 } else { 460 // In symbol mode, just toggle symbol and symbol more keyboard. 461 shiftKeyState.onPress(); 462 toggleShift(); 463 } 464 } 465 466 public void onReleaseShift() { 467 if (!isKeyboardAvailable()) 468 return; 469 ShiftKeyState shiftKeyState = mShiftKeyState; 470 if (DEBUG_STATE) 471 Log.d(TAG, "onReleaseShift:" 472 + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() 473 + " shiftKeyState=" + shiftKeyState); 474 if (isAlphabetMode()) { 475 if (shiftKeyState.isMomentary()) { 476 // After chording input while normal state. 477 toggleShift(); 478 } else if (isShiftLocked() && !shiftKeyState.isIgnoring()) { 479 // Shift has been pressed without chording while caps lock state. 480 toggleCapsLock(); 481 } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()) { 482 // Shift has been pressed without chording while shifted state. 483 toggleShift(); 484 } 485 } 486 shiftKeyState.onRelease(); 487 } 488 489 public void onPressSymbol() { 490 if (DEBUG_STATE) 491 Log.d(TAG, "onPressSymbol:" 492 + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() 493 + " symbolKeyState=" + mSymbolKeyState); 494 changeKeyboardMode(); 495 mSymbolKeyState.onPress(); 496 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_MOMENTARY; 497 } 498 499 public void onReleaseSymbol() { 500 if (DEBUG_STATE) 501 Log.d(TAG, "onReleaseSymbol:" 502 + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() 503 + " symbolKeyState=" + mSymbolKeyState); 504 // Snap back to the previous keyboard mode if the user chords the mode change key and 505 // other key, then released the mode change key. 506 if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_CHORDING) 507 changeKeyboardMode(); 508 mSymbolKeyState.onRelease(); 509 } 510 511 public void onOtherKeyPressed() { 512 if (DEBUG_STATE) 513 Log.d(TAG, "onOtherKeyPressed:" 514 + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() 515 + " shiftKeyState=" + mShiftKeyState 516 + " symbolKeyState=" + mSymbolKeyState); 517 mShiftKeyState.onOtherKeyPressed(); 518 mSymbolKeyState.onOtherKeyPressed(); 519 } 520 521 public void onCancelInput() { 522 // Snap back to the previous keyboard mode if the user cancels sliding input. 523 if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY && getPointerCount() == 1) 524 changeKeyboardMode(); 525 } 526 527 private void toggleShiftInSymbol() { 528 if (isAlphabetMode()) 529 return; 530 final LatinKeyboard keyboard; 531 if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) { 532 mCurrentId = mSymbolsShiftedId; 533 keyboard = getKeyboard(mCurrentId); 534 // Symbol shifted keyboard has an ALT key that has a caps lock style indicator. To 535 // enable the indicator, we need to call enableShiftLock() and setShiftLocked(true). 536 // Thus we can keep the ALT key's Key.on value true while LatinKey.onRelease() is 537 // called. 538 keyboard.setShiftLocked(true); 539 } else { 540 mCurrentId = mSymbolsId; 541 keyboard = getKeyboard(mCurrentId); 542 // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the 543 // indicator, we need to call enableShiftLock() and setShiftLocked(false). 544 keyboard.setShifted(false); 545 } 546 mInputView.setKeyboard(keyboard); 547 } 548 549 public boolean isInMomentaryAutoModeSwitchState() { 550 return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY; 551 } 552 553 public boolean isVibrateAndSoundFeedbackRequired() { 554 return mInputView == null || !mInputView.isInSlidingKeyInput(); 555 } 556 557 private int getPointerCount() { 558 return mInputView == null ? 0 : mInputView.getPointerCount(); 559 } 560 561 private void toggleKeyboardMode() { 562 loadKeyboardInternal(mMode, mImeOptions, mVoiceKeyEnabled, mVoiceButtonOnPrimary, 563 !mIsSymbols); 564 if (mIsSymbols) { 565 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN; 566 } else { 567 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; 568 } 569 } 570 571 public boolean hasDistinctMultitouch() { 572 return mInputView != null && mInputView.hasDistinctMultitouch(); 573 } 574 575 /** 576 * Updates state machine to figure out when to automatically snap back to the previous mode. 577 */ 578 public void onKey(int key) { 579 if (DEBUG_STATE) 580 Log.d(TAG, "onKey: code=" + key + " autoModeSwitchState=" + mAutoModeSwitchState 581 + " pointers=" + getPointerCount()); 582 switch (mAutoModeSwitchState) { 583 case AUTO_MODE_SWITCH_STATE_MOMENTARY: 584 // Only distinct multi touch devices can be in this state. 585 // On non-distinct multi touch devices, mode change key is handled by 586 // {@link LatinIME#onCodeInput}, not by {@link LatinIME#onPress} and 587 // {@link LatinIME#onRelease}. So, on such devices, {@link #mAutoModeSwitchState} starts 588 // from {@link #AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN}, or 589 // {@link #AUTO_MODE_SWITCH_STATE_ALPHA}, not from 590 // {@link #AUTO_MODE_SWITCH_STATE_MOMENTARY}. 591 if (key == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { 592 // Detected only the mode change key has been pressed, and then released. 593 if (mIsSymbols) { 594 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN; 595 } else { 596 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; 597 } 598 } else if (getPointerCount() == 1) { 599 // Snap back to the previous keyboard mode if the user pressed the mode change key 600 // and slid to other key, then released the finger. 601 // If the user cancels the sliding input, snapping back to the previous keyboard 602 // mode is handled by {@link #onCancelInput}. 603 changeKeyboardMode(); 604 } else { 605 // Chording input is being started. The keyboard mode will be snapped back to the 606 // previous mode in {@link onReleaseSymbol} when the mode change key is released. 607 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_CHORDING; 608 } 609 break; 610 case AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN: 611 if (key != Keyboard.CODE_SPACE && key != Keyboard.CODE_ENTER && key >= 0) { 612 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL; 613 } 614 break; 615 case AUTO_MODE_SWITCH_STATE_SYMBOL: 616 // Snap back to alpha keyboard mode if user types one or more non-space/enter 617 // characters followed by a space/enter. 618 if (key == Keyboard.CODE_ENTER || key == Keyboard.CODE_SPACE) { 619 changeKeyboardMode(); 620 } 621 break; 622 } 623 } 624 625 public LatinKeyboardView getInputView() { 626 return mInputView; 627 } 628 629 public LatinKeyboardView onCreateInputView() { 630 createInputViewInternal(mLayoutId, true); 631 return mInputView; 632 } 633 634 private void createInputViewInternal(int newLayout, boolean forceReset) { 635 int layoutId = newLayout; 636 if (mLayoutId != layoutId || mInputView == null || forceReset) { 637 if (mInputView != null) { 638 mInputView.closing(); 639 } 640 if (KEYBOARD_THEMES.length <= layoutId) { 641 layoutId = Integer.valueOf(sConfigDefaultKeyboardThemeId); 642 } 643 644 Utils.GCUtils.getInstance().reset(); 645 boolean tryGC = true; 646 for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { 647 try { 648 mInputView = (LatinKeyboardView) mInputMethodService.getLayoutInflater( 649 ).inflate(KEYBOARD_THEMES[layoutId], null); 650 tryGC = false; 651 } catch (OutOfMemoryError e) { 652 Log.w(TAG, "load keyboard failed: " + e); 653 tryGC = Utils.GCUtils.getInstance().tryGCOrWait( 654 mLayoutId + "," + layoutId, e); 655 } catch (InflateException e) { 656 Log.w(TAG, "load keyboard failed: " + e); 657 tryGC = Utils.GCUtils.getInstance().tryGCOrWait( 658 mLayoutId + "," + layoutId, e); 659 } 660 } 661 mInputView.setOnKeyboardActionListener(mInputMethodService); 662 mLayoutId = layoutId; 663 } 664 } 665 666 private void postSetInputView() { 667 mInputMethodService.mHandler.post(new Runnable() { 668 @Override 669 public void run() { 670 if (mInputView != null) { 671 mInputMethodService.setInputView(mInputView); 672 } 673 mInputMethodService.updateInputViewShown(); 674 } 675 }); 676 } 677 678 @Override 679 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 680 if (PREF_KEYBOARD_LAYOUT.equals(key)) { 681 final int layoutId = Integer.valueOf( 682 sharedPreferences.getString(key, sConfigDefaultKeyboardThemeId)); 683 createInputViewInternal(layoutId, false); 684 postSetInputView(); 685 } else if (Settings.PREF_SETTINGS_KEY.equals(key)) { 686 mHasSettingsKey = getSettingsKeyMode(sharedPreferences, mInputMethodService); 687 createInputViewInternal(mLayoutId, true); 688 postSetInputView(); 689 } 690 } 691 692 private int getColorScheme() { 693 return (mInputView != null) 694 ? mInputView.getColorScheme() : KeyboardView.COLOR_SCHEME_WHITE; 695 } 696 697 public void onAutoCorrectionStateChanged(boolean isAutoCorrection) { 698 if (isAutoCorrection != mIsAutoCorrectionActive) { 699 LatinKeyboardView keyboardView = getInputView(); 700 mIsAutoCorrectionActive = isAutoCorrection; 701 keyboardView.invalidateKey(((LatinKeyboard) keyboardView.getKeyboard()) 702 .onAutoCorrectionStateChanged(isAutoCorrection)); 703 } 704 } 705 706 private static boolean getSettingsKeyMode(SharedPreferences prefs, Context context) { 707 Resources resources = context.getResources(); 708 final boolean showSettingsKeyOption = resources.getBoolean( 709 R.bool.config_enable_show_settings_key_option); 710 if (showSettingsKeyOption) { 711 final String settingsKeyMode = prefs.getString(Settings.PREF_SETTINGS_KEY, 712 resources.getString(DEFAULT_SETTINGS_KEY_MODE)); 713 // We show the settings key when 1) SETTINGS_KEY_MODE_ALWAYS_SHOW or 714 // 2) SETTINGS_KEY_MODE_AUTO and there are two or more enabled IMEs on the system 715 if (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW)) 716 || (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_AUTO)) 717 && Utils.hasMultipleEnabledIMEsOrSubtypes( 718 ((InputMethodManager) context.getSystemService( 719 Context.INPUT_METHOD_SERVICE))))) { 720 return true; 721 } 722 } 723 return false; 724 } 725} 726