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