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.accessibility.AccessibleKeyboardViewProxy;
     30 import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
     31 import com.android.inputmethod.keyboard.internal.KeyboardState;
     32 import com.android.inputmethod.latin.InputView;
     33 import com.android.inputmethod.latin.LatinIME;
     34 import com.android.inputmethod.latin.LatinImeLogger;
     35 import com.android.inputmethod.latin.R;
     36 import com.android.inputmethod.latin.RichInputMethodManager;
     37 import com.android.inputmethod.latin.SubtypeSwitcher;
     38 import com.android.inputmethod.latin.WordComposer;
     39 import com.android.inputmethod.latin.settings.Settings;
     40 import com.android.inputmethod.latin.settings.SettingsValues;
     41 import com.android.inputmethod.latin.utils.ResourceUtils;
     42 
     43 public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
     44     private static final String TAG = KeyboardSwitcher.class.getSimpleName();
     45 
     46     public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
     47 
     48     static final class KeyboardTheme {
     49         public final int mThemeId;
     50         public final int mStyleId;
     51 
     52         // Note: The themeId should be aligned with "themeId" attribute of Keyboard style
     53         // in values/style.xml.
     54         public KeyboardTheme(final int themeId, final int styleId) {
     55             mThemeId = themeId;
     56             mStyleId = styleId;
     57         }
     58     }
     59 
     60     private static final KeyboardTheme[] KEYBOARD_THEMES = {
     61         new KeyboardTheme(0, R.style.KeyboardTheme_ICS),
     62         new KeyboardTheme(1, R.style.KeyboardTheme_GB),
     63     };
     64 
     65     private SubtypeSwitcher mSubtypeSwitcher;
     66     private SharedPreferences mPrefs;
     67 
     68     private InputView mCurrentInputView;
     69     private View mMainKeyboardFrame;
     70     private MainKeyboardView mKeyboardView;
     71     private EmojiPalettesView mEmojiPalettesView;
     72     private LatinIME mLatinIME;
     73     private Resources mResources;
     74 
     75     private KeyboardState mState;
     76 
     77     private KeyboardLayoutSet mKeyboardLayoutSet;
     78 
     79     /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
     80      * what user actually typed. */
     81     private boolean mIsAutoCorrectionActive;
     82 
     83     private KeyboardTheme mKeyboardTheme = KEYBOARD_THEMES[0];
     84     private Context mThemeContext;
     85 
     86     private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
     87 
     88     public static KeyboardSwitcher getInstance() {
     89         return sInstance;
     90     }
     91 
     92     private KeyboardSwitcher() {
     93         // Intentional empty constructor for singleton.
     94     }
     95 
     96     public static void init(final LatinIME latinIme) {
     97         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme);
     98         sInstance.initInternal(latinIme, prefs);
     99     }
    100 
    101     private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) {
    102         mLatinIME = latinIme;
    103         mResources = latinIme.getResources();
    104         mPrefs = prefs;
    105         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
    106         mState = new KeyboardState(this);
    107         setContextThemeWrapper(latinIme, getKeyboardTheme(latinIme, prefs));
    108     }
    109 
    110     private static KeyboardTheme getKeyboardTheme(final Context context,
    111             final SharedPreferences prefs) {
    112         final String defaultIndex = context.getString(R.string.config_default_keyboard_theme_index);
    113         final String themeIndex = prefs.getString(PREF_KEYBOARD_LAYOUT, defaultIndex);
    114         try {
    115             final int index = Integer.valueOf(themeIndex);
    116             if (index >= 0 && index < KEYBOARD_THEMES.length) {
    117                 return KEYBOARD_THEMES[index];
    118             }
    119         } catch (NumberFormatException e) {
    120             // Format error, keyboard theme is default to 0.
    121         }
    122         Log.w(TAG, "Illegal keyboard theme in preference: " + themeIndex + ", default to "
    123                 + defaultIndex);
    124         return KEYBOARD_THEMES[Integer.valueOf(defaultIndex)];
    125     }
    126 
    127     private void setContextThemeWrapper(final Context context, final KeyboardTheme keyboardTheme) {
    128         if (mThemeContext == null || mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) {
    129             mKeyboardTheme = keyboardTheme;
    130             mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
    131             KeyboardLayoutSet.clearKeyboardCache();
    132         }
    133     }
    134 
    135     public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues) {
    136         final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
    137                 mThemeContext, editorInfo);
    138         final Resources res = mThemeContext.getResources();
    139         final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
    140         final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
    141         builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
    142         builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
    143         builder.setOptions(
    144                 settingsValues.isVoiceKeyEnabled(editorInfo),
    145                 true /* always show a voice key on the main keyboard */,
    146                 settingsValues.isLanguageSwitchKeyEnabled());
    147         mKeyboardLayoutSet = builder.build();
    148         try {
    149             mState.onLoadKeyboard();
    150         } catch (KeyboardLayoutSetException e) {
    151             Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
    152             LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause());
    153             return;
    154         }
    155     }
    156 
    157     public void saveKeyboardState() {
    158         if (getKeyboard() != null || isShowingEmojiPalettes()) {
    159             mState.onSaveKeyboardState();
    160         }
    161     }
    162 
    163     public void onFinishInputView() {
    164         mIsAutoCorrectionActive = false;
    165     }
    166 
    167     public void onHideWindow() {
    168         mIsAutoCorrectionActive = false;
    169     }
    170 
    171     private void setKeyboard(final Keyboard keyboard) {
    172         // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}.
    173         setMainKeyboardFrame();
    174         final MainKeyboardView keyboardView = mKeyboardView;
    175         final Keyboard oldKeyboard = keyboardView.getKeyboard();
    176         keyboardView.setKeyboard(keyboard);
    177         mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
    178         keyboardView.setKeyPreviewPopupEnabled(
    179                 Settings.readKeyPreviewPopupEnabled(mPrefs, mResources),
    180                 Settings.readKeyPreviewPopupDismissDelay(mPrefs, mResources));
    181         keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive);
    182         keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
    183         final boolean subtypeChanged = (oldKeyboard == null)
    184                 || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
    185         final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage(
    186                 keyboard.mId.mLocale);
    187         keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
    188                 RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true));
    189     }
    190 
    191     public Keyboard getKeyboard() {
    192         if (mKeyboardView != null) {
    193             return mKeyboardView.getKeyboard();
    194         }
    195         return null;
    196     }
    197 
    198     /**
    199      * Update keyboard shift state triggered by connected EditText status change.
    200      */
    201     public void updateShiftState() {
    202         mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(),
    203                 mLatinIME.getCurrentRecapitalizeState());
    204     }
    205 
    206     // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
    207     // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
    208     public void resetKeyboardStateToAlphabet() {
    209         mState.onResetKeyboardStateToAlphabet();
    210     }
    211 
    212     public void onPressKey(final int code, final boolean isSinglePointer) {
    213         mState.onPressKey(code, isSinglePointer, mLatinIME.getCurrentAutoCapsState());
    214     }
    215 
    216     public void onReleaseKey(final int code, final boolean withSliding) {
    217         mState.onReleaseKey(code, withSliding);
    218     }
    219 
    220     public void onFinishSlidingInput() {
    221         mState.onFinishSlidingInput();
    222     }
    223 
    224     // Implements {@link KeyboardState.SwitchActions}.
    225     @Override
    226     public void setAlphabetKeyboard() {
    227         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
    228     }
    229 
    230     // Implements {@link KeyboardState.SwitchActions}.
    231     @Override
    232     public void setAlphabetManualShiftedKeyboard() {
    233         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
    234     }
    235 
    236     // Implements {@link KeyboardState.SwitchActions}.
    237     @Override
    238     public void setAlphabetAutomaticShiftedKeyboard() {
    239         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
    240     }
    241 
    242     // Implements {@link KeyboardState.SwitchActions}.
    243     @Override
    244     public void setAlphabetShiftLockedKeyboard() {
    245         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
    246     }
    247 
    248     // Implements {@link KeyboardState.SwitchActions}.
    249     @Override
    250     public void setAlphabetShiftLockShiftedKeyboard() {
    251         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
    252     }
    253 
    254     // Implements {@link KeyboardState.SwitchActions}.
    255     @Override
    256     public void setSymbolsKeyboard() {
    257         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
    258     }
    259 
    260     private void setMainKeyboardFrame() {
    261         mMainKeyboardFrame.setVisibility(View.VISIBLE);
    262         mEmojiPalettesView.setVisibility(View.GONE);
    263         mEmojiPalettesView.stopEmojiPalettes();
    264     }
    265 
    266     // Implements {@link KeyboardState.SwitchActions}.
    267     @Override
    268     public void setEmojiKeyboard() {
    269         mMainKeyboardFrame.setVisibility(View.GONE);
    270         mEmojiPalettesView.startEmojiPalettes();
    271         mEmojiPalettesView.setVisibility(View.VISIBLE);
    272     }
    273 
    274     // Implements {@link KeyboardState.SwitchActions}.
    275     @Override
    276     public void setSymbolsShiftedKeyboard() {
    277         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED));
    278     }
    279 
    280     // Implements {@link KeyboardState.SwitchActions}.
    281     @Override
    282     public void requestUpdatingShiftState() {
    283         mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(),
    284                 mLatinIME.getCurrentRecapitalizeState());
    285     }
    286 
    287     // Implements {@link KeyboardState.SwitchActions}.
    288     @Override
    289     public void startDoubleTapShiftKeyTimer() {
    290         final MainKeyboardView keyboardView = getMainKeyboardView();
    291         if (keyboardView != null) {
    292             keyboardView.startDoubleTapShiftKeyTimer();
    293         }
    294     }
    295 
    296     // Implements {@link KeyboardState.SwitchActions}.
    297     @Override
    298     public void cancelDoubleTapShiftKeyTimer() {
    299         final MainKeyboardView keyboardView = getMainKeyboardView();
    300         if (keyboardView != null) {
    301             keyboardView.cancelDoubleTapShiftKeyTimer();
    302         }
    303     }
    304 
    305     // Implements {@link KeyboardState.SwitchActions}.
    306     @Override
    307     public boolean isInDoubleTapShiftKeyTimeout() {
    308         final MainKeyboardView keyboardView = getMainKeyboardView();
    309         return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
    310     }
    311 
    312     /**
    313      * Updates state machine to figure out when to automatically switch back to the previous mode.
    314      */
    315     public void onCodeInput(final int code) {
    316         mState.onCodeInput(code, mLatinIME.getCurrentAutoCapsState());
    317     }
    318 
    319     private boolean isShowingMainKeyboard() {
    320         return null != mKeyboardView && mKeyboardView.isShown();
    321     }
    322 
    323     public boolean isShowingEmojiPalettes() {
    324         return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
    325     }
    326 
    327     public boolean isShowingMoreKeysPanel() {
    328         if (isShowingEmojiPalettes()) {
    329             return false;
    330         }
    331         return mKeyboardView.isShowingMoreKeysPanel();
    332     }
    333 
    334     public View getVisibleKeyboardView() {
    335         if (isShowingEmojiPalettes()) {
    336             return mEmojiPalettesView;
    337         }
    338         return mKeyboardView;
    339     }
    340 
    341     public MainKeyboardView getMainKeyboardView() {
    342         return mKeyboardView;
    343     }
    344 
    345     public void deallocateMemory() {
    346         if (mKeyboardView != null) {
    347             mKeyboardView.cancelAllOngoingEvents();
    348             mKeyboardView.deallocateMemory();
    349         }
    350         if (mEmojiPalettesView != null) {
    351             mEmojiPalettesView.stopEmojiPalettes();
    352         }
    353     }
    354 
    355     public boolean isShowingMainKeyboardOrEmojiPalettes() {
    356         return isShowingMainKeyboard() || isShowingEmojiPalettes();
    357     }
    358 
    359     public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
    360         if (mKeyboardView != null) {
    361             mKeyboardView.closing();
    362         }
    363 
    364         setContextThemeWrapper(mLatinIME, mKeyboardTheme);
    365         mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
    366                 R.layout.input_view, null);
    367         mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
    368         mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
    369                 R.id.emoji_keyboard_view);
    370 
    371         mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
    372         mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
    373         mKeyboardView.setKeyboardActionListener(mLatinIME);
    374         mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
    375                 isHardwareAcceleratedDrawingEnabled);
    376         mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
    377 
    378         // This always needs to be set since the accessibility state can
    379         // potentially change without the input view being re-created.
    380         AccessibleKeyboardViewProxy.getInstance().setView(mKeyboardView);
    381 
    382         return mCurrentInputView;
    383     }
    384 
    385     public void onNetworkStateChanged() {
    386         if (mKeyboardView != null) {
    387             mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
    388         }
    389     }
    390 
    391     public void onAutoCorrectionStateChanged(final boolean isAutoCorrection) {
    392         if (mIsAutoCorrectionActive != isAutoCorrection) {
    393             mIsAutoCorrectionActive = isAutoCorrection;
    394             if (mKeyboardView != null) {
    395                 mKeyboardView.updateAutoCorrectionState(isAutoCorrection);
    396             }
    397         }
    398     }
    399 
    400     public int getKeyboardShiftMode() {
    401         final Keyboard keyboard = getKeyboard();
    402         if (keyboard == null) {
    403             return WordComposer.CAPS_MODE_OFF;
    404         }
    405         switch (keyboard.mId.mElementId) {
    406         case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
    407         case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
    408             return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
    409         case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
    410             return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
    411         case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
    412             return WordComposer.CAPS_MODE_AUTO_SHIFTED;
    413         default:
    414             return WordComposer.CAPS_MODE_OFF;
    415         }
    416     }
    417 }
    418