KeyboardSwitcher.java revision 3897a43940eca9c400e542c76d2c790f2c48eb8c
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.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException; 31import com.android.inputmethod.keyboard.internal.KeyboardState; 32import com.android.inputmethod.latin.InputView; 33import com.android.inputmethod.latin.LatinIME; 34import com.android.inputmethod.latin.LatinImeLogger; 35import com.android.inputmethod.latin.R; 36import com.android.inputmethod.latin.RichInputMethodManager; 37import com.android.inputmethod.latin.SubtypeSwitcher; 38import com.android.inputmethod.latin.WordComposer; 39import com.android.inputmethod.latin.settings.Settings; 40import com.android.inputmethod.latin.settings.SettingsValues; 41import com.android.inputmethod.latin.utils.ResourceUtils; 42 43public final class KeyboardSwitcher implements KeyboardState.SwitchActions { 44 private static final String TAG = KeyboardSwitcher.class.getSimpleName(); 45 46 public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916"; 47 48 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 private static final KeyboardTheme[] KEYBOARD_THEMES = { 61 new KeyboardTheme(0, R.style.KeyboardTheme), 62 new KeyboardTheme(1, R.style.KeyboardTheme_HighContrast), 63 new KeyboardTheme(6, R.style.KeyboardTheme_Stone), 64 new KeyboardTheme(7, R.style.KeyboardTheme_Stone_Bold), 65 new KeyboardTheme(8, R.style.KeyboardTheme_Gingerbread), 66 new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich), 67 }; 68 69 private SubtypeSwitcher mSubtypeSwitcher; 70 private SharedPreferences mPrefs; 71 72 private InputView mCurrentInputView; 73 private MainKeyboardView mKeyboardView; 74 private LatinIME mLatinIME; 75 private Resources mResources; 76 77 private KeyboardState mState; 78 79 private KeyboardLayoutSet mKeyboardLayoutSet; 80 81 /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of 82 * what user actually typed. */ 83 private boolean mIsAutoCorrectionActive; 84 85 private KeyboardTheme mKeyboardTheme = KEYBOARD_THEMES[0]; 86 private Context mThemeContext; 87 88 private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); 89 90 public static KeyboardSwitcher getInstance() { 91 return sInstance; 92 } 93 94 private KeyboardSwitcher() { 95 // Intentional empty constructor for singleton. 96 } 97 98 public static void init(final LatinIME latinIme) { 99 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme); 100 sInstance.initInternal(latinIme, prefs); 101 } 102 103 private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) { 104 mLatinIME = latinIme; 105 mResources = latinIme.getResources(); 106 mPrefs = prefs; 107 mSubtypeSwitcher = SubtypeSwitcher.getInstance(); 108 mState = new KeyboardState(this); 109 setContextThemeWrapper(latinIme, getKeyboardTheme(latinIme, prefs)); 110 } 111 112 private static KeyboardTheme getKeyboardTheme(final Context context, 113 final SharedPreferences prefs) { 114 final String defaultIndex = context.getString(R.string.config_default_keyboard_theme_index); 115 final String themeIndex = prefs.getString(PREF_KEYBOARD_LAYOUT, defaultIndex); 116 try { 117 final int index = Integer.valueOf(themeIndex); 118 if (index >= 0 && index < KEYBOARD_THEMES.length) { 119 return KEYBOARD_THEMES[index]; 120 } 121 } catch (NumberFormatException e) { 122 // Format error, keyboard theme is default to 0. 123 } 124 Log.w(TAG, "Illegal keyboard theme in preference: " + themeIndex + ", default to 0"); 125 return KEYBOARD_THEMES[0]; 126 } 127 128 private void setContextThemeWrapper(final Context context, final KeyboardTheme keyboardTheme) { 129 if (mThemeContext == null || mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) { 130 mKeyboardTheme = keyboardTheme; 131 mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId); 132 KeyboardLayoutSet.clearKeyboardCache(); 133 } 134 } 135 136 public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues) { 137 final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( 138 mThemeContext, editorInfo); 139 final Resources res = mThemeContext.getResources(); 140 final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); 141 final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); 142 builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); 143 builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); 144 builder.setOptions( 145 settingsValues.isVoiceKeyEnabled(editorInfo), 146 settingsValues.isVoiceKeyOnMain(), 147 settingsValues.isLanguageSwitchKeyEnabled()); 148 mKeyboardLayoutSet = builder.build(); 149 try { 150 mState.onLoadKeyboard(); 151 } catch (KeyboardLayoutSetException e) { 152 Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause()); 153 LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause()); 154 return; 155 } 156 } 157 158 public void saveKeyboardState() { 159 if (getKeyboard() != null) { 160 mState.onSaveKeyboardState(); 161 } 162 } 163 164 public void onFinishInputView() { 165 mIsAutoCorrectionActive = false; 166 } 167 168 public void onHideWindow() { 169 mIsAutoCorrectionActive = false; 170 } 171 172 private void setKeyboard(final Keyboard keyboard) { 173 final MainKeyboardView keyboardView = mKeyboardView; 174 final Keyboard oldKeyboard = keyboardView.getKeyboard(); 175 keyboardView.setKeyboard(keyboard); 176 mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding); 177 keyboardView.setKeyPreviewPopupEnabled( 178 Settings.readKeyPreviewPopupEnabled(mPrefs, mResources), 179 Settings.readKeyPreviewPopupDismissDelay(mPrefs, mResources)); 180 keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive); 181 keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); 182 final boolean subtypeChanged = (oldKeyboard == null) 183 || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); 184 final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage( 185 keyboard.mId.mLocale); 186 keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage, 187 RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true)); 188 } 189 190 public Keyboard getKeyboard() { 191 if (mKeyboardView != null) { 192 return mKeyboardView.getKeyboard(); 193 } 194 return null; 195 } 196 197 /** 198 * Update keyboard shift state triggered by connected EditText status change. 199 */ 200 public void updateShiftState() { 201 mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), 202 mLatinIME.getCurrentRecapitalizeState()); 203 } 204 205 // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout 206 // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal(). 207 public void resetKeyboardStateToAlphabet() { 208 mState.onResetKeyboardStateToAlphabet(); 209 } 210 211 public void onPressKey(final int code, final boolean isSinglePointer) { 212 mState.onPressKey(code, isSinglePointer, mLatinIME.getCurrentAutoCapsState()); 213 } 214 215 public void onReleaseKey(final int code, final boolean withSliding) { 216 mState.onReleaseKey(code, withSliding); 217 } 218 219 public void onFinishSlidingInput() { 220 mState.onFinishSlidingInput(); 221 } 222 223 // Implements {@link KeyboardState.SwitchActions}. 224 @Override 225 public void setAlphabetKeyboard() { 226 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET)); 227 } 228 229 // Implements {@link KeyboardState.SwitchActions}. 230 @Override 231 public void setAlphabetManualShiftedKeyboard() { 232 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED)); 233 } 234 235 // Implements {@link KeyboardState.SwitchActions}. 236 @Override 237 public void setAlphabetAutomaticShiftedKeyboard() { 238 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)); 239 } 240 241 // Implements {@link KeyboardState.SwitchActions}. 242 @Override 243 public void setAlphabetShiftLockedKeyboard() { 244 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED)); 245 } 246 247 // Implements {@link KeyboardState.SwitchActions}. 248 @Override 249 public void setAlphabetShiftLockShiftedKeyboard() { 250 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)); 251 } 252 253 // Implements {@link KeyboardState.SwitchActions}. 254 @Override 255 public void setSymbolsKeyboard() { 256 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); 257 } 258 259 // Implements {@link KeyboardState.SwitchActions}. 260 @Override 261 public void setSymbolsShiftedKeyboard() { 262 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)); 263 } 264 265 // Implements {@link KeyboardState.SwitchActions}. 266 @Override 267 public void requestUpdatingShiftState() { 268 mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), 269 mLatinIME.getCurrentRecapitalizeState()); 270 } 271 272 // Implements {@link KeyboardState.SwitchActions}. 273 @Override 274 public void startDoubleTapShiftKeyTimer() { 275 final MainKeyboardView keyboardView = getMainKeyboardView(); 276 if (keyboardView != null) { 277 keyboardView.startDoubleTapShiftKeyTimer(); 278 } 279 } 280 281 // Implements {@link KeyboardState.SwitchActions}. 282 @Override 283 public void cancelDoubleTapShiftKeyTimer() { 284 final MainKeyboardView keyboardView = getMainKeyboardView(); 285 if (keyboardView != null) { 286 keyboardView.cancelDoubleTapShiftKeyTimer(); 287 } 288 } 289 290 // Implements {@link KeyboardState.SwitchActions}. 291 @Override 292 public boolean isInDoubleTapShiftKeyTimeout() { 293 final MainKeyboardView keyboardView = getMainKeyboardView(); 294 return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout(); 295 } 296 297 /** 298 * Updates state machine to figure out when to automatically switch back to the previous mode. 299 */ 300 public void onCodeInput(final int code) { 301 mState.onCodeInput(code, mLatinIME.getCurrentAutoCapsState()); 302 } 303 304 public MainKeyboardView getMainKeyboardView() { 305 return mKeyboardView; 306 } 307 308 public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) { 309 if (mKeyboardView != null) { 310 mKeyboardView.closing(); 311 } 312 313 setContextThemeWrapper(mLatinIME, mKeyboardTheme); 314 mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( 315 R.layout.input_view, null); 316 317 mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); 318 mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled); 319 mKeyboardView.setKeyboardActionListener(mLatinIME); 320 321 // This always needs to be set since the accessibility state can 322 // potentially change without the input view being re-created. 323 AccessibleKeyboardViewProxy.getInstance().setView(mKeyboardView); 324 325 return mCurrentInputView; 326 } 327 328 public void onNetworkStateChanged() { 329 if (mKeyboardView != null) { 330 mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); 331 } 332 } 333 334 public void onAutoCorrectionStateChanged(final boolean isAutoCorrection) { 335 if (mIsAutoCorrectionActive != isAutoCorrection) { 336 mIsAutoCorrectionActive = isAutoCorrection; 337 if (mKeyboardView != null) { 338 mKeyboardView.updateAutoCorrectionState(isAutoCorrection); 339 } 340 } 341 } 342 343 public int getKeyboardShiftMode() { 344 final Keyboard keyboard = getKeyboard(); 345 if (keyboard == null) { 346 return WordComposer.CAPS_MODE_OFF; 347 } 348 switch (keyboard.mId.mElementId) { 349 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: 350 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: 351 return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED; 352 case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: 353 return WordComposer.CAPS_MODE_MANUAL_SHIFTED; 354 case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: 355 return WordComposer.CAPS_MODE_AUTO_SHIFTED; 356 default: 357 return WordComposer.CAPS_MODE_OFF; 358 } 359 } 360} 361