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