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