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