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