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 17 package com.android.inputmethod.keyboard; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.content.res.Resources; 22 import android.preference.PreferenceManager; 23 import android.util.Log; 24 import android.view.ContextThemeWrapper; 25 import android.view.LayoutInflater; 26 import android.view.View; 27 import android.view.inputmethod.EditorInfo; 28 29 import com.android.inputmethod.compat.InputMethodServiceCompatUtils; 30 import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException; 31 import com.android.inputmethod.keyboard.emoji.EmojiPalettesView; 32 import com.android.inputmethod.keyboard.internal.KeyboardState; 33 import com.android.inputmethod.keyboard.internal.KeyboardTextsSet; 34 import com.android.inputmethod.latin.InputView; 35 import com.android.inputmethod.latin.LatinIME; 36 import com.android.inputmethod.latin.R; 37 import com.android.inputmethod.latin.RichInputMethodManager; 38 import com.android.inputmethod.latin.SubtypeSwitcher; 39 import com.android.inputmethod.latin.WordComposer; 40 import com.android.inputmethod.latin.settings.Settings; 41 import com.android.inputmethod.latin.settings.SettingsValues; 42 import com.android.inputmethod.latin.utils.ResourceUtils; 43 import com.android.inputmethod.latin.utils.ScriptUtils; 44 45 public final class KeyboardSwitcher implements KeyboardState.SwitchActions { 46 private static final String TAG = KeyboardSwitcher.class.getSimpleName(); 47 48 private SubtypeSwitcher mSubtypeSwitcher; 49 private SharedPreferences mPrefs; 50 51 private InputView mCurrentInputView; 52 private View mMainKeyboardFrame; 53 private MainKeyboardView mKeyboardView; 54 private EmojiPalettesView mEmojiPalettesView; 55 private LatinIME mLatinIME; 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 getInstance()69 public static KeyboardSwitcher getInstance() { 70 return sInstance; 71 } 72 KeyboardSwitcher()73 private KeyboardSwitcher() { 74 // Intentional empty constructor for singleton. 75 } 76 init(final LatinIME latinIme)77 public static void init(final LatinIME latinIme) { 78 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme); 79 sInstance.initInternal(latinIme, prefs); 80 } 81 initInternal(final LatinIME latinIme, final SharedPreferences prefs)82 private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) { 83 mLatinIME = latinIme; 84 mPrefs = prefs; 85 mSubtypeSwitcher = SubtypeSwitcher.getInstance(); 86 mState = new KeyboardState(this); 87 mIsHardwareAcceleratedDrawingEnabled = 88 InputMethodServiceCompatUtils.enableHardwareAcceleration(mLatinIME); 89 } 90 updateKeyboardTheme()91 public void updateKeyboardTheme() { 92 final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper( 93 mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs)); 94 if (themeUpdated && mKeyboardView != null) { 95 mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled)); 96 } 97 } 98 updateKeyboardThemeAndContextThemeWrapper(final Context context, final KeyboardTheme keyboardTheme)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 loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues, final int currentAutoCapsState, final int currentRecapitalizeState)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.getDefaultKeyboardHeight(res); 117 builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); 118 builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); 119 builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey); 120 builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey()); 121 mKeyboardLayoutSet = builder.build(); 122 try { 123 mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState); 124 mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale(), mThemeContext); 125 } catch (KeyboardLayoutSetException e) { 126 Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause()); 127 return; 128 } 129 } 130 saveKeyboardState()131 public void saveKeyboardState() { 132 if (getKeyboard() != null || isShowingEmojiPalettes()) { 133 mState.onSaveKeyboardState(); 134 } 135 } 136 onHideWindow()137 public void onHideWindow() { 138 if (mKeyboardView != null) { 139 mKeyboardView.onHideWindow(); 140 } 141 } 142 setKeyboard(final Keyboard keyboard)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(mSubtypeSwitcher.isShortcutImeReady()); 164 final boolean subtypeChanged = (oldKeyboard == null) 165 || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); 166 final int languageOnSpacebarFormatType = mSubtypeSwitcher.getLanguageOnSpacebarFormatType( 167 keyboard.mId.mSubtype); 168 final boolean hasMultipleEnabledIMEsOrSubtypes = RichInputMethodManager.getInstance() 169 .hasMultipleEnabledIMEsOrSubtypes(true /* shouldIncludeAuxiliarySubtypes */); 170 keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, languageOnSpacebarFormatType, 171 hasMultipleEnabledIMEsOrSubtypes); 172 } 173 getKeyboard()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(). resetKeyboardStateToAlphabet(final int currentAutoCapsState, final int currentRecapitalizeState)183 public void resetKeyboardStateToAlphabet(final int currentAutoCapsState, 184 final int currentRecapitalizeState) { 185 mState.onResetKeyboardStateToAlphabet(currentAutoCapsState, currentRecapitalizeState); 186 } 187 onPressKey(final int code, final boolean isSinglePointer, final int currentAutoCapsState, final int currentRecapitalizeState)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 onReleaseKey(final int code, final boolean withSliding, final int currentAutoCapsState, final int currentRecapitalizeState)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 onFinishSlidingInput(final int currentAutoCapsState, final int currentRecapitalizeState)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 setAlphabetKeyboard()205 public void setAlphabetKeyboard() { 206 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET)); 207 } 208 209 // Implements {@link KeyboardState.SwitchActions}. 210 @Override setAlphabetManualShiftedKeyboard()211 public void setAlphabetManualShiftedKeyboard() { 212 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED)); 213 } 214 215 // Implements {@link KeyboardState.SwitchActions}. 216 @Override setAlphabetAutomaticShiftedKeyboard()217 public void setAlphabetAutomaticShiftedKeyboard() { 218 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)); 219 } 220 221 // Implements {@link KeyboardState.SwitchActions}. 222 @Override setAlphabetShiftLockedKeyboard()223 public void setAlphabetShiftLockedKeyboard() { 224 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED)); 225 } 226 227 // Implements {@link KeyboardState.SwitchActions}. 228 @Override setAlphabetShiftLockShiftedKeyboard()229 public void setAlphabetShiftLockShiftedKeyboard() { 230 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)); 231 } 232 233 // Implements {@link KeyboardState.SwitchActions}. 234 @Override setSymbolsKeyboard()235 public void setSymbolsKeyboard() { 236 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); 237 } 238 setMainKeyboardFrame(final SettingsValues settingsValues)239 private void setMainKeyboardFrame(final SettingsValues settingsValues) { 240 mMainKeyboardFrame.setVisibility( 241 settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE); 242 mEmojiPalettesView.setVisibility(View.GONE); 243 mEmojiPalettesView.stopEmojiPalettes(); 244 } 245 246 // Implements {@link KeyboardState.SwitchActions}. 247 @Override setEmojiKeyboard()248 public void setEmojiKeyboard() { 249 final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); 250 mMainKeyboardFrame.setVisibility(View.GONE); 251 mEmojiPalettesView.startEmojiPalettes( 252 mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL), 253 mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet); 254 mEmojiPalettesView.setVisibility(View.VISIBLE); 255 } 256 onToggleEmojiKeyboard()257 public void onToggleEmojiKeyboard() { 258 if (mKeyboardLayoutSet == null || !isShowingEmojiPalettes()) { 259 mLatinIME.startShowingInputView(); 260 setEmojiKeyboard(); 261 } else { 262 mLatinIME.stopShowingInputView(); 263 setAlphabetKeyboard(); 264 } 265 } 266 267 // Implements {@link KeyboardState.SwitchActions}. 268 @Override setSymbolsShiftedKeyboard()269 public void setSymbolsShiftedKeyboard() { 270 setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)); 271 } 272 273 // Future method for requesting an updating to the shift state. requestUpdatingShiftState(final int currentAutoCapsState, final int currentRecapitalizeState)274 public void requestUpdatingShiftState(final int currentAutoCapsState, 275 final int currentRecapitalizeState) { 276 mState.onUpdateShiftState(currentAutoCapsState, currentRecapitalizeState); 277 } 278 279 // Implements {@link KeyboardState.SwitchActions}. 280 @Override startDoubleTapShiftKeyTimer()281 public void startDoubleTapShiftKeyTimer() { 282 final MainKeyboardView keyboardView = getMainKeyboardView(); 283 if (keyboardView != null) { 284 keyboardView.startDoubleTapShiftKeyTimer(); 285 } 286 } 287 288 // Implements {@link KeyboardState.SwitchActions}. 289 @Override cancelDoubleTapShiftKeyTimer()290 public void cancelDoubleTapShiftKeyTimer() { 291 final MainKeyboardView keyboardView = getMainKeyboardView(); 292 if (keyboardView != null) { 293 keyboardView.cancelDoubleTapShiftKeyTimer(); 294 } 295 } 296 297 // Implements {@link KeyboardState.SwitchActions}. 298 @Override isInDoubleTapShiftKeyTimeout()299 public boolean isInDoubleTapShiftKeyTimeout() { 300 final MainKeyboardView keyboardView = getMainKeyboardView(); 301 return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout(); 302 } 303 304 /** 305 * Updates state machine to figure out when to automatically switch back to the previous mode. 306 */ onCodeInput(final int code, final int currentAutoCapsState, final int currentRecapitalizeState)307 public void onCodeInput(final int code, final int currentAutoCapsState, 308 final int currentRecapitalizeState) { 309 mState.onCodeInput(code, currentAutoCapsState, currentRecapitalizeState); 310 } 311 isShowingEmojiPalettes()312 public boolean isShowingEmojiPalettes() { 313 return mEmojiPalettesView != null && mEmojiPalettesView.isShown(); 314 } 315 isShowingMoreKeysPanel()316 public boolean isShowingMoreKeysPanel() { 317 if (isShowingEmojiPalettes()) { 318 return false; 319 } 320 return mKeyboardView.isShowingMoreKeysPanel(); 321 } 322 getVisibleKeyboardView()323 public View getVisibleKeyboardView() { 324 if (isShowingEmojiPalettes()) { 325 return mEmojiPalettesView; 326 } 327 return mKeyboardView; 328 } 329 getMainKeyboardView()330 public MainKeyboardView getMainKeyboardView() { 331 return mKeyboardView; 332 } 333 deallocateMemory()334 public void deallocateMemory() { 335 if (mKeyboardView != null) { 336 mKeyboardView.cancelAllOngoingEvents(); 337 mKeyboardView.deallocateMemory(); 338 } 339 if (mEmojiPalettesView != null) { 340 mEmojiPalettesView.stopEmojiPalettes(); 341 } 342 } 343 onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled)344 public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) { 345 if (mKeyboardView != null) { 346 mKeyboardView.closing(); 347 } 348 349 updateKeyboardThemeAndContextThemeWrapper( 350 mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs)); 351 mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( 352 R.layout.input_view, null); 353 mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame); 354 mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById( 355 R.id.emoji_palettes_view); 356 357 mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); 358 mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled); 359 mKeyboardView.setKeyboardActionListener(mLatinIME); 360 mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled( 361 isHardwareAcceleratedDrawingEnabled); 362 mEmojiPalettesView.setKeyboardActionListener(mLatinIME); 363 return mCurrentInputView; 364 } 365 onNetworkStateChanged()366 public void onNetworkStateChanged() { 367 if (mKeyboardView != null) { 368 mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); 369 } 370 } 371 getKeyboardShiftMode()372 public int getKeyboardShiftMode() { 373 final Keyboard keyboard = getKeyboard(); 374 if (keyboard == null) { 375 return WordComposer.CAPS_MODE_OFF; 376 } 377 switch (keyboard.mId.mElementId) { 378 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: 379 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: 380 return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED; 381 case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: 382 return WordComposer.CAPS_MODE_MANUAL_SHIFTED; 383 case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: 384 return WordComposer.CAPS_MODE_AUTO_SHIFTED; 385 default: 386 return WordComposer.CAPS_MODE_OFF; 387 } 388 } 389 getCurrentKeyboardScriptId()390 public int getCurrentKeyboardScriptId() { 391 if (null == mKeyboardLayoutSet) { 392 return ScriptUtils.SCRIPT_UNKNOWN; 393 } 394 return mKeyboardLayoutSet.getScriptId(); 395 } 396 } 397