KeyboardSwitcher.java revision 12d80ebead6a1d7f704a5a3af3b6fe3313ceab05
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.res.Resources; 21import android.util.Log; 22import android.view.ContextThemeWrapper; 23import android.view.LayoutInflater; 24import android.view.View; 25import android.view.inputmethod.EditorInfo; 26 27import com.android.inputmethod.compat.InputMethodServiceCompatUtils; 28import com.android.inputmethod.event.Event; 29import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException; 30import com.android.inputmethod.keyboard.emoji.EmojiPalettesView; 31import com.android.inputmethod.keyboard.internal.KeyboardState; 32import com.android.inputmethod.keyboard.internal.KeyboardTextsSet; 33import com.android.inputmethod.latin.InputView; 34import com.android.inputmethod.latin.LatinIME; 35import com.android.inputmethod.latin.R; 36import com.android.inputmethod.latin.RichInputMethodManager; 37import com.android.inputmethod.latin.WordComposer; 38import com.android.inputmethod.latin.define.ProductionFlags; 39import com.android.inputmethod.latin.settings.Settings; 40import com.android.inputmethod.latin.settings.SettingsValues; 41import com.android.inputmethod.latin.utils.CapsModeUtils; 42import com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils; 43import com.android.inputmethod.latin.utils.RecapitalizeStatus; 44import com.android.inputmethod.latin.utils.ResourceUtils; 45import com.android.inputmethod.latin.utils.ScriptUtils; 46 47public final class KeyboardSwitcher implements KeyboardState.SwitchActions { 48 private static final String TAG = KeyboardSwitcher.class.getSimpleName(); 49 50 private InputView mCurrentInputView; 51 private View mMainKeyboardFrame; 52 private MainKeyboardView mKeyboardView; 53 private EmojiPalettesView mEmojiPalettesView; 54 private LatinIME mLatinIME; 55 private RichInputMethodManager mRichImm; 56 private boolean mIsHardwareAcceleratedDrawingEnabled; 57 58 private KeyboardState mState; 59 60 private KeyboardLayoutSet mKeyboardLayoutSet; 61 // TODO: The following {@link KeyboardTextsSet} should be in {@link KeyboardLayoutSet}. 62 private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet(); 63 64 private KeyboardTheme mKeyboardTheme; 65 private Context mThemeContext; 66 67 private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); 68 69 public static KeyboardSwitcher getInstance() { 70 return sInstance; 71 } 72 73 private KeyboardSwitcher() { 74 // Intentional empty constructor for singleton. 75 } 76 77 public static void init(final LatinIME latinIme) { 78 sInstance.initInternal(latinIme); 79 } 80 81 private void initInternal(final LatinIME latinIme) { 82 mLatinIME = latinIme; 83 mRichImm = RichInputMethodManager.getInstance(); 84 mState = new KeyboardState(this); 85 mIsHardwareAcceleratedDrawingEnabled = 86 InputMethodServiceCompatUtils.enableHardwareAcceleration(mLatinIME); 87 } 88 89 public void updateKeyboardTheme() { 90 final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper( 91 mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */)); 92 if (themeUpdated && mKeyboardView != null) { 93 mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled)); 94 } 95 } 96 97 private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context, 98 final KeyboardTheme keyboardTheme) { 99 if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) { 100 mKeyboardTheme = keyboardTheme; 101 mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId); 102 KeyboardLayoutSet.onKeyboardThemeChanged(); 103 return true; 104 } 105 return false; 106 } 107 108 public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues, 109 final int currentAutoCapsState, final int currentRecapitalizeState) { 110 final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( 111 mThemeContext, editorInfo); 112 final Resources res = mThemeContext.getResources(); 113 final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); 114 final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues); 115 builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); 116 builder.setSubtype(mRichImm.getCurrentSubtype()); 117 builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey); 118 builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey()); 119 builder.setSplitLayoutEnabledByUser(ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED 120 && settingsValues.mIsSplitKeyboardEnabled); 121 mKeyboardLayoutSet = builder.build(); 122 try { 123 mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState); 124 // TODO: revisit this for multi-lingual input 125 mKeyboardTextsSet.setLocale(mRichImm.getCurrentSubtypeLocales()[0], mThemeContext); 126 } catch (KeyboardLayoutSetException e) { 127 Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause()); 128 } 129 } 130 131 public void saveKeyboardState() { 132 if (getKeyboard() != null || isShowingEmojiPalettes()) { 133 mState.onSaveKeyboardState(); 134 } 135 } 136 137 public void onHideWindow() { 138 if (mKeyboardView != null) { 139 mKeyboardView.onHideWindow(); 140 } 141 } 142 143 private void setKeyboard(final Keyboard keyboard) { 144 // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}. 145 final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent(); 146 setMainKeyboardFrame(currentSettingsValues); 147 // TODO: pass this object to setKeyboard instead of getting the current values. 148 final MainKeyboardView keyboardView = mKeyboardView; 149 final Keyboard oldKeyboard = keyboardView.getKeyboard(); 150 keyboardView.setKeyboard(keyboard); 151 mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding); 152 keyboardView.setKeyPreviewPopupEnabled( 153 currentSettingsValues.mKeyPreviewPopupOn, 154 currentSettingsValues.mKeyPreviewPopupDismissDelay); 155 keyboardView.setKeyPreviewAnimationParams( 156 currentSettingsValues.mHasCustomKeyPreviewAnimationParams, 157 currentSettingsValues.mKeyPreviewShowUpStartXScale, 158 currentSettingsValues.mKeyPreviewShowUpStartYScale, 159 currentSettingsValues.mKeyPreviewShowUpDuration, 160 currentSettingsValues.mKeyPreviewDismissEndXScale, 161 currentSettingsValues.mKeyPreviewDismissEndYScale, 162 currentSettingsValues.mKeyPreviewDismissDuration); 163 keyboardView.updateShortcutKey(mRichImm.isShortcutImeReady()); 164 final boolean subtypeChanged = (oldKeyboard == null) 165 || !keyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype); 166 final int languageOnSpacebarFormatType = LanguageOnSpacebarUtils 167 .getLanguageOnSpacebarFormatType(keyboard.mId.mSubtype); 168 final boolean hasMultipleEnabledIMEsOrSubtypes = mRichImm 169 .hasMultipleEnabledIMEsOrSubtypes(true /* shouldIncludeAuxiliarySubtypes */); 170 keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, languageOnSpacebarFormatType, 171 hasMultipleEnabledIMEsOrSubtypes); 172 } 173 174 public Keyboard getKeyboard() { 175 if (mKeyboardView != null) { 176 return mKeyboardView.getKeyboard(); 177 } 178 return null; 179 } 180 181 // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout 182 // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal(). 183 public void resetKeyboardStateToAlphabet(final int currentAutoCapsState, 184 final int currentRecapitalizeState) { 185 mState.onResetKeyboardStateToAlphabet(currentAutoCapsState, currentRecapitalizeState); 186 } 187 188 public void onPressKey(final int code, final boolean isSinglePointer, 189 final int currentAutoCapsState, final int currentRecapitalizeState) { 190 mState.onPressKey(code, isSinglePointer, currentAutoCapsState, currentRecapitalizeState); 191 } 192 193 public void onReleaseKey(final int code, final boolean withSliding, 194 final int currentAutoCapsState, final int currentRecapitalizeState) { 195 mState.onReleaseKey(code, withSliding, currentAutoCapsState, currentRecapitalizeState); 196 } 197 198 public void onFinishSlidingInput(final int currentAutoCapsState, 199 final int currentRecapitalizeState) { 200 mState.onFinishSlidingInput(currentAutoCapsState, currentRecapitalizeState); 201 } 202 203 // Implements {@link KeyboardState.SwitchActions}. 204 @Override 205 public void setAlphabetKeyboard() { 206 if (DEBUG_ACTION) { 207 Log.d(TAG, "setAlphabetKeyboard"); 208 } 209 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET)); 210 } 211 212 // Implements {@link KeyboardState.SwitchActions}. 213 @Override 214 public void setAlphabetManualShiftedKeyboard() { 215 if (DEBUG_ACTION) { 216 Log.d(TAG, "setAlphabetManualShiftedKeyboard"); 217 } 218 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED)); 219 } 220 221 // Implements {@link KeyboardState.SwitchActions}. 222 @Override 223 public void setAlphabetAutomaticShiftedKeyboard() { 224 if (DEBUG_ACTION) { 225 Log.d(TAG, "setAlphabetAutomaticShiftedKeyboard"); 226 } 227 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)); 228 } 229 230 // Implements {@link KeyboardState.SwitchActions}. 231 @Override 232 public void setAlphabetShiftLockedKeyboard() { 233 if (DEBUG_ACTION) { 234 Log.d(TAG, "setAlphabetShiftLockedKeyboard"); 235 } 236 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED)); 237 } 238 239 // Implements {@link KeyboardState.SwitchActions}. 240 @Override 241 public void setAlphabetShiftLockShiftedKeyboard() { 242 if (DEBUG_ACTION) { 243 Log.d(TAG, "setAlphabetShiftLockShiftedKeyboard"); 244 } 245 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)); 246 } 247 248 // Implements {@link KeyboardState.SwitchActions}. 249 @Override 250 public void setSymbolsKeyboard() { 251 if (DEBUG_ACTION) { 252 Log.d(TAG, "setSymbolsKeyboard"); 253 } 254 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); 255 } 256 257 private void setMainKeyboardFrame(final SettingsValues settingsValues) { 258 final int visibility = settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE; 259 mKeyboardView.setVisibility(visibility); 260 // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}. 261 // @see #getVisibleKeyboardView() and 262 // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets) 263 mMainKeyboardFrame.setVisibility(visibility); 264 mEmojiPalettesView.setVisibility(View.GONE); 265 mEmojiPalettesView.stopEmojiPalettes(); 266 } 267 268 // Implements {@link KeyboardState.SwitchActions}. 269 @Override 270 public void setEmojiKeyboard() { 271 if (DEBUG_ACTION) { 272 Log.d(TAG, "setEmojiKeyboard"); 273 } 274 final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); 275 mMainKeyboardFrame.setVisibility(View.GONE); 276 // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}. 277 // @see #getVisibleKeyboardView() and 278 // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets) 279 mKeyboardView.setVisibility(View.GONE); 280 mEmojiPalettesView.startEmojiPalettes( 281 mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL), 282 mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet); 283 mEmojiPalettesView.setVisibility(View.VISIBLE); 284 } 285 286 public void onToggleEmojiKeyboard() { 287 final boolean needsToLoadKeyboard = (mKeyboardLayoutSet == null); 288 if (needsToLoadKeyboard || !isShowingEmojiPalettes()) { 289 mLatinIME.startShowingInputView(needsToLoadKeyboard); 290 setEmojiKeyboard(); 291 } else { 292 mLatinIME.stopShowingInputView(); 293 setAlphabetKeyboard(); 294 } 295 } 296 297 // Implements {@link KeyboardState.SwitchActions}. 298 @Override 299 public void setSymbolsShiftedKeyboard() { 300 if (DEBUG_ACTION) { 301 Log.d(TAG, "setSymbolsShiftedKeyboard"); 302 } 303 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)); 304 } 305 306 // Future method for requesting an updating to the shift state. 307 @Override 308 public void requestUpdatingShiftState(final int autoCapsFlags, final int recapitalizeMode) { 309 if (DEBUG_ACTION) { 310 Log.d(TAG, "requestUpdatingShiftState: " 311 + " autoCapsFlags=" + CapsModeUtils.flagsToString(autoCapsFlags) 312 + " recapitalizeMode=" + RecapitalizeStatus.modeToString(recapitalizeMode)); 313 } 314 mState.onUpdateShiftState(autoCapsFlags, recapitalizeMode); 315 } 316 317 // Implements {@link KeyboardState.SwitchActions}. 318 @Override 319 public void startDoubleTapShiftKeyTimer() { 320 if (DEBUG_TIMER_ACTION) { 321 Log.d(TAG, "startDoubleTapShiftKeyTimer"); 322 } 323 final MainKeyboardView keyboardView = getMainKeyboardView(); 324 if (keyboardView != null) { 325 keyboardView.startDoubleTapShiftKeyTimer(); 326 } 327 } 328 329 // Implements {@link KeyboardState.SwitchActions}. 330 @Override 331 public void cancelDoubleTapShiftKeyTimer() { 332 if (DEBUG_TIMER_ACTION) { 333 Log.d(TAG, "setAlphabetKeyboard"); 334 } 335 final MainKeyboardView keyboardView = getMainKeyboardView(); 336 if (keyboardView != null) { 337 keyboardView.cancelDoubleTapShiftKeyTimer(); 338 } 339 } 340 341 // Implements {@link KeyboardState.SwitchActions}. 342 @Override 343 public boolean isInDoubleTapShiftKeyTimeout() { 344 if (DEBUG_TIMER_ACTION) { 345 Log.d(TAG, "isInDoubleTapShiftKeyTimeout"); 346 } 347 final MainKeyboardView keyboardView = getMainKeyboardView(); 348 return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout(); 349 } 350 351 /** 352 * Updates state machine to figure out when to automatically switch back to the previous mode. 353 */ 354 public void onEvent(final Event event, final int currentAutoCapsState, 355 final int currentRecapitalizeState) { 356 mState.onEvent(event, currentAutoCapsState, currentRecapitalizeState); 357 } 358 359 public boolean isShowingEmojiPalettes() { 360 return mEmojiPalettesView != null && mEmojiPalettesView.isShown(); 361 } 362 363 public boolean isShowingMoreKeysPanel() { 364 if (isShowingEmojiPalettes()) { 365 return false; 366 } 367 return mKeyboardView.isShowingMoreKeysPanel(); 368 } 369 370 public View getVisibleKeyboardView() { 371 if (isShowingEmojiPalettes()) { 372 return mEmojiPalettesView; 373 } 374 return mKeyboardView; 375 } 376 377 public MainKeyboardView getMainKeyboardView() { 378 return mKeyboardView; 379 } 380 381 public void deallocateMemory() { 382 if (mKeyboardView != null) { 383 mKeyboardView.cancelAllOngoingEvents(); 384 mKeyboardView.deallocateMemory(); 385 } 386 if (mEmojiPalettesView != null) { 387 mEmojiPalettesView.stopEmojiPalettes(); 388 } 389 } 390 391 public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) { 392 if (mKeyboardView != null) { 393 mKeyboardView.closing(); 394 } 395 396 updateKeyboardThemeAndContextThemeWrapper( 397 mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */)); 398 mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( 399 R.layout.input_view, null); 400 mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame); 401 mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById( 402 R.id.emoji_palettes_view); 403 404 mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); 405 mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled); 406 mKeyboardView.setKeyboardActionListener(mLatinIME); 407 mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled( 408 isHardwareAcceleratedDrawingEnabled); 409 mEmojiPalettesView.setKeyboardActionListener(mLatinIME); 410 return mCurrentInputView; 411 } 412 413 public int getKeyboardShiftMode() { 414 final Keyboard keyboard = getKeyboard(); 415 if (keyboard == null) { 416 return WordComposer.CAPS_MODE_OFF; 417 } 418 switch (keyboard.mId.mElementId) { 419 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: 420 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: 421 return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED; 422 case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: 423 return WordComposer.CAPS_MODE_MANUAL_SHIFTED; 424 case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: 425 return WordComposer.CAPS_MODE_AUTO_SHIFTED; 426 default: 427 return WordComposer.CAPS_MODE_OFF; 428 } 429 } 430 431 public int getCurrentKeyboardScriptId() { 432 if (null == mKeyboardLayoutSet) { 433 return ScriptUtils.SCRIPT_UNKNOWN; 434 } 435 return mKeyboardLayoutSet.getScriptId(); 436 } 437} 438