KeyboardSwitcher.java revision dd01a0bb1ddca5c3506efe7098441da9896efdbf
1/* 2 * Copyright (C) 2008 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; 18 19import android.content.Context; 20import android.content.SharedPreferences; 21import android.content.res.Resources; 22import android.preference.PreferenceManager; 23import android.util.Log; 24import android.view.ContextThemeWrapper; 25import android.view.LayoutInflater; 26import android.view.View; 27import android.view.inputmethod.EditorInfo; 28 29import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; 30import com.android.inputmethod.compat.InputMethodServiceCompatUtils; 31import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException; 32import com.android.inputmethod.keyboard.internal.KeyboardState; 33import com.android.inputmethod.keyboard.internal.KeyboardTextsSet; 34import com.android.inputmethod.latin.InputView; 35import com.android.inputmethod.latin.LatinIME; 36import com.android.inputmethod.latin.LatinImeLogger; 37import com.android.inputmethod.latin.R; 38import com.android.inputmethod.latin.RichInputMethodManager; 39import com.android.inputmethod.latin.SubtypeSwitcher; 40import com.android.inputmethod.latin.WordComposer; 41import com.android.inputmethod.latin.settings.Settings; 42import com.android.inputmethod.latin.settings.SettingsValues; 43import com.android.inputmethod.latin.utils.ResourceUtils; 44 45public final class KeyboardSwitcher implements KeyboardState.SwitchActions { 46 private static final String TAG = KeyboardSwitcher.class.getSimpleName(); 47 48 public static final class KeyboardTheme { 49 public final int mThemeId; 50 public final int mStyleId; 51 52 // Note: The themeId should be aligned with "themeId" attribute of Keyboard style 53 // in values/style.xml. 54 public KeyboardTheme(final int themeId, final int styleId) { 55 mThemeId = themeId; 56 mStyleId = styleId; 57 } 58 } 59 60 public static final int THEME_INDEX_ICS = 0; 61 public static final int THEME_INDEX_GB = 1; 62 public static final int THEME_INDEX_KLP = 2; 63 public static final int THEME_INDEX_DEFAULT = THEME_INDEX_KLP; 64 public static final KeyboardTheme[] KEYBOARD_THEMES = { 65 new KeyboardTheme(THEME_INDEX_ICS, R.style.KeyboardTheme_ICS), 66 new KeyboardTheme(THEME_INDEX_GB, R.style.KeyboardTheme_GB), 67 new KeyboardTheme(THEME_INDEX_KLP, R.style.KeyboardTheme_KLP), 68 }; 69 70 private SubtypeSwitcher mSubtypeSwitcher; 71 private SharedPreferences mPrefs; 72 73 private InputView mCurrentInputView; 74 private View mMainKeyboardFrame; 75 private MainKeyboardView mKeyboardView; 76 private EmojiPalettesView mEmojiPalettesView; 77 private LatinIME mLatinIME; 78 private boolean mIsHardwareAcceleratedDrawingEnabled; 79 80 private KeyboardState mState; 81 82 private KeyboardLayoutSet mKeyboardLayoutSet; 83 // TODO: The following {@link KeyboardTextsSet} should be in {@link KeyboardLayoutSet}. 84 private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet(); 85 private SettingsValues mCurrentSettingsValues; 86 87 /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of 88 * what user actually typed. */ 89 private boolean mIsAutoCorrectionActive; 90 91 private KeyboardTheme mKeyboardTheme = KEYBOARD_THEMES[THEME_INDEX_DEFAULT]; 92 private Context mThemeContext; 93 94 private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); 95 96 public static KeyboardSwitcher getInstance() { 97 return sInstance; 98 } 99 100 private KeyboardSwitcher() { 101 // Intentional empty constructor for singleton. 102 } 103 104 public static void init(final LatinIME latinIme) { 105 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme); 106 sInstance.initInternal(latinIme, prefs); 107 } 108 109 private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) { 110 mLatinIME = latinIme; 111 mPrefs = prefs; 112 mSubtypeSwitcher = SubtypeSwitcher.getInstance(); 113 mState = new KeyboardState(this); 114 mIsHardwareAcceleratedDrawingEnabled = 115 InputMethodServiceCompatUtils.enableHardwareAcceleration(mLatinIME); 116 } 117 118 public void updateKeyboardTheme() { 119 final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper( 120 mLatinIME, getKeyboardTheme(mLatinIME, mPrefs)); 121 if (themeUpdated && mKeyboardView != null) { 122 mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled)); 123 } 124 } 125 126 private static KeyboardTheme getKeyboardTheme(final Context context, 127 final SharedPreferences prefs) { 128 final Resources res = context.getResources(); 129 final int index = Settings.readKeyboardThemeIndex(prefs, res); 130 if (index >= 0 && index < KEYBOARD_THEMES.length) { 131 return KEYBOARD_THEMES[index]; 132 } 133 final int defaultThemeIndex = Settings.resetAndGetDefaultKeyboardThemeIndex(prefs, res); 134 Log.w(TAG, "Illegal keyboard theme in preference: " + index + ", default to " 135 + defaultThemeIndex); 136 return KEYBOARD_THEMES[defaultThemeIndex]; 137 } 138 139 private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context, 140 final KeyboardTheme keyboardTheme) { 141 if (mThemeContext == null || mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) { 142 mKeyboardTheme = keyboardTheme; 143 mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId); 144 KeyboardLayoutSet.clearKeyboardCache(); 145 return true; 146 } 147 return false; 148 } 149 150 public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues) { 151 final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( 152 mThemeContext, editorInfo); 153 final Resources res = mThemeContext.getResources(); 154 final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); 155 final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); 156 builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); 157 builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); 158 builder.setOptions( 159 mSubtypeSwitcher.isShortcutImeEnabled(), 160 settingsValues.mShowsVoiceInputKey, 161 settingsValues.isLanguageSwitchKeyEnabled()); 162 mKeyboardLayoutSet = builder.build(); 163 mCurrentSettingsValues = settingsValues; 164 try { 165 mState.onLoadKeyboard(); 166 mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale()); 167 } catch (KeyboardLayoutSetException e) { 168 Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause()); 169 LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause()); 170 return; 171 } 172 } 173 174 public void saveKeyboardState() { 175 if (getKeyboard() != null || isShowingEmojiPalettes()) { 176 mState.onSaveKeyboardState(); 177 } 178 } 179 180 public void onFinishInputView() { 181 mIsAutoCorrectionActive = false; 182 } 183 184 public void onHideWindow() { 185 mIsAutoCorrectionActive = false; 186 } 187 188 private void setKeyboard(final Keyboard keyboard) { 189 // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}. 190 setMainKeyboardFrame(); 191 final MainKeyboardView keyboardView = mKeyboardView; 192 final Keyboard oldKeyboard = keyboardView.getKeyboard(); 193 keyboardView.setKeyboard(keyboard); 194 mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding); 195 keyboardView.setKeyPreviewPopupEnabled( 196 mCurrentSettingsValues.mKeyPreviewPopupOn, 197 mCurrentSettingsValues.mKeyPreviewPopupDismissDelay); 198 keyboardView.setKeyPreviewAnimationParams( 199 mCurrentSettingsValues.mKeyPreviewShowUpStartScale, 200 mCurrentSettingsValues.mKeyPreviewShowUpDuration, 201 mCurrentSettingsValues.mKeyPreviewDismissEndScale, 202 mCurrentSettingsValues.mKeyPreviewDismissDuration); 203 keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive); 204 keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); 205 final boolean subtypeChanged = (oldKeyboard == null) 206 || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); 207 final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage( 208 keyboard.mId.mLocale); 209 keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage, 210 RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true)); 211 } 212 213 public Keyboard getKeyboard() { 214 if (mKeyboardView != null) { 215 return mKeyboardView.getKeyboard(); 216 } 217 return null; 218 } 219 220 /** 221 * Update keyboard shift state triggered by connected EditText status change. 222 */ 223 public void updateShiftState() { 224 mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), 225 mLatinIME.getCurrentRecapitalizeState()); 226 } 227 228 // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout 229 // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal(). 230 public void resetKeyboardStateToAlphabet() { 231 mState.onResetKeyboardStateToAlphabet(); 232 } 233 234 public void onPressKey(final int code, final boolean isSinglePointer) { 235 mState.onPressKey(code, isSinglePointer, mLatinIME.getCurrentAutoCapsState()); 236 } 237 238 public void onReleaseKey(final int code, final boolean withSliding) { 239 mState.onReleaseKey(code, withSliding); 240 } 241 242 public void onFinishSlidingInput() { 243 mState.onFinishSlidingInput(); 244 } 245 246 // Implements {@link KeyboardState.SwitchActions}. 247 @Override 248 public void setAlphabetKeyboard() { 249 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET)); 250 } 251 252 // Implements {@link KeyboardState.SwitchActions}. 253 @Override 254 public void setAlphabetManualShiftedKeyboard() { 255 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED)); 256 } 257 258 // Implements {@link KeyboardState.SwitchActions}. 259 @Override 260 public void setAlphabetAutomaticShiftedKeyboard() { 261 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)); 262 } 263 264 // Implements {@link KeyboardState.SwitchActions}. 265 @Override 266 public void setAlphabetShiftLockedKeyboard() { 267 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED)); 268 } 269 270 // Implements {@link KeyboardState.SwitchActions}. 271 @Override 272 public void setAlphabetShiftLockShiftedKeyboard() { 273 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)); 274 } 275 276 // Implements {@link KeyboardState.SwitchActions}. 277 @Override 278 public void setSymbolsKeyboard() { 279 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); 280 } 281 282 private void setMainKeyboardFrame() { 283 mMainKeyboardFrame.setVisibility(View.VISIBLE); 284 mEmojiPalettesView.setVisibility(View.GONE); 285 mEmojiPalettesView.stopEmojiPalettes(); 286 } 287 288 // Implements {@link KeyboardState.SwitchActions}. 289 @Override 290 public void setEmojiKeyboard() { 291 mMainKeyboardFrame.setVisibility(View.GONE); 292 mEmojiPalettesView.startEmojiPalettes( 293 mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL), 294 mKeyboardView.getKeyVisualAttribute()); 295 mEmojiPalettesView.setVisibility(View.VISIBLE); 296 } 297 298 // Implements {@link KeyboardState.SwitchActions}. 299 @Override 300 public void setSymbolsShiftedKeyboard() { 301 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)); 302 } 303 304 // Implements {@link KeyboardState.SwitchActions}. 305 @Override 306 public void requestUpdatingShiftState() { 307 mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), 308 mLatinIME.getCurrentRecapitalizeState()); 309 } 310 311 // Implements {@link KeyboardState.SwitchActions}. 312 @Override 313 public void startDoubleTapShiftKeyTimer() { 314 final MainKeyboardView keyboardView = getMainKeyboardView(); 315 if (keyboardView != null) { 316 keyboardView.startDoubleTapShiftKeyTimer(); 317 } 318 } 319 320 // Implements {@link KeyboardState.SwitchActions}. 321 @Override 322 public void cancelDoubleTapShiftKeyTimer() { 323 final MainKeyboardView keyboardView = getMainKeyboardView(); 324 if (keyboardView != null) { 325 keyboardView.cancelDoubleTapShiftKeyTimer(); 326 } 327 } 328 329 // Implements {@link KeyboardState.SwitchActions}. 330 @Override 331 public boolean isInDoubleTapShiftKeyTimeout() { 332 final MainKeyboardView keyboardView = getMainKeyboardView(); 333 return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout(); 334 } 335 336 /** 337 * Updates state machine to figure out when to automatically switch back to the previous mode. 338 */ 339 public void onCodeInput(final int code) { 340 mState.onCodeInput(code, mLatinIME.getCurrentAutoCapsState()); 341 } 342 343 private boolean isShowingMainKeyboard() { 344 return null != mKeyboardView && mKeyboardView.isShown(); 345 } 346 347 public boolean isShowingEmojiPalettes() { 348 return mEmojiPalettesView != null && mEmojiPalettesView.isShown(); 349 } 350 351 public boolean isShowingMoreKeysPanel() { 352 if (isShowingEmojiPalettes()) { 353 return false; 354 } 355 return mKeyboardView.isShowingMoreKeysPanel(); 356 } 357 358 public View getVisibleKeyboardView() { 359 if (isShowingEmojiPalettes()) { 360 return mEmojiPalettesView; 361 } 362 return mKeyboardView; 363 } 364 365 public MainKeyboardView getMainKeyboardView() { 366 return mKeyboardView; 367 } 368 369 public void deallocateMemory() { 370 if (mKeyboardView != null) { 371 mKeyboardView.cancelAllOngoingEvents(); 372 mKeyboardView.deallocateMemory(); 373 } 374 if (mEmojiPalettesView != null) { 375 mEmojiPalettesView.stopEmojiPalettes(); 376 } 377 } 378 379 public boolean isShowingMainKeyboardOrEmojiPalettes() { 380 return isShowingMainKeyboard() || isShowingEmojiPalettes(); 381 } 382 383 public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) { 384 if (mKeyboardView != null) { 385 mKeyboardView.closing(); 386 } 387 388 updateKeyboardThemeAndContextThemeWrapper(mLatinIME, mKeyboardTheme); 389 mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( 390 R.layout.input_view, null); 391 mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame); 392 mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById( 393 R.id.emoji_keyboard_view); 394 395 mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); 396 mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled); 397 mKeyboardView.setKeyboardActionListener(mLatinIME); 398 mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled( 399 isHardwareAcceleratedDrawingEnabled); 400 mEmojiPalettesView.setKeyboardActionListener(mLatinIME); 401 402 // This always needs to be set since the accessibility state can 403 // potentially change without the input view being re-created. 404 AccessibleKeyboardViewProxy.getInstance().setView(mKeyboardView); 405 406 return mCurrentInputView; 407 } 408 409 public void onNetworkStateChanged() { 410 if (mKeyboardView != null) { 411 mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); 412 } 413 } 414 415 public void onAutoCorrectionStateChanged(final boolean isAutoCorrection) { 416 if (mIsAutoCorrectionActive != isAutoCorrection) { 417 mIsAutoCorrectionActive = isAutoCorrection; 418 if (mKeyboardView != null) { 419 mKeyboardView.updateAutoCorrectionState(isAutoCorrection); 420 } 421 } 422 } 423 424 public int getKeyboardShiftMode() { 425 final Keyboard keyboard = getKeyboard(); 426 if (keyboard == null) { 427 return WordComposer.CAPS_MODE_OFF; 428 } 429 switch (keyboard.mId.mElementId) { 430 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: 431 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: 432 return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED; 433 case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: 434 return WordComposer.CAPS_MODE_MANUAL_SHIFTED; 435 case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: 436 return WordComposer.CAPS_MODE_AUTO_SHIFTED; 437 default: 438 return WordComposer.CAPS_MODE_OFF; 439 } 440 } 441} 442