Home | History | Annotate | Download | only in keyboard
      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 
     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         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme);
     79         sInstance.initInternal(latinIme, prefs);
     80     }
     81 
     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 
     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 
     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.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 
    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(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 
    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         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
    207     }
    208 
    209     // Implements {@link KeyboardState.SwitchActions}.
    210     @Override
    211     public void setAlphabetManualShiftedKeyboard() {
    212         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
    213     }
    214 
    215     // Implements {@link KeyboardState.SwitchActions}.
    216     @Override
    217     public void setAlphabetAutomaticShiftedKeyboard() {
    218         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
    219     }
    220 
    221     // Implements {@link KeyboardState.SwitchActions}.
    222     @Override
    223     public void setAlphabetShiftLockedKeyboard() {
    224         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
    225     }
    226 
    227     // Implements {@link KeyboardState.SwitchActions}.
    228     @Override
    229     public void setAlphabetShiftLockShiftedKeyboard() {
    230         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
    231     }
    232 
    233     // Implements {@link KeyboardState.SwitchActions}.
    234     @Override
    235     public void setSymbolsKeyboard() {
    236         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
    237     }
    238 
    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
    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 
    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
    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.
    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
    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
    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
    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      */
    307     public void onCodeInput(final int code, final int currentAutoCapsState,
    308             final int currentRecapitalizeState) {
    309         mState.onCodeInput(code, currentAutoCapsState, currentRecapitalizeState);
    310     }
    311 
    312     public boolean isShowingEmojiPalettes() {
    313         return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
    314     }
    315 
    316     public boolean isShowingMoreKeysPanel() {
    317         if (isShowingEmojiPalettes()) {
    318             return false;
    319         }
    320         return mKeyboardView.isShowingMoreKeysPanel();
    321     }
    322 
    323     public View getVisibleKeyboardView() {
    324         if (isShowingEmojiPalettes()) {
    325             return mEmojiPalettesView;
    326         }
    327         return mKeyboardView;
    328     }
    329 
    330     public MainKeyboardView getMainKeyboardView() {
    331         return mKeyboardView;
    332     }
    333 
    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 
    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 
    366     public void onNetworkStateChanged() {
    367         if (mKeyboardView != null) {
    368             mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
    369         }
    370     }
    371 
    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 
    390     public int getCurrentKeyboardScriptId() {
    391         if (null == mKeyboardLayoutSet) {
    392             return ScriptUtils.SCRIPT_UNKNOWN;
    393         }
    394         return mKeyboardLayoutSet.getScriptId();
    395     }
    396 }
    397