KeyboardSwitcher.java revision ad1f88081190ad3c5569b2774c03e644786770d2
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
17package com.android.inputmethod.keyboard;
18
19import android.content.Context;
20import android.content.SharedPreferences;
21import android.content.res.Configuration;
22import android.content.res.Resources;
23import android.preference.PreferenceManager;
24import android.util.Log;
25import android.view.ContextThemeWrapper;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.inputmethod.EditorInfo;
29
30import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
31import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
32import com.android.inputmethod.keyboard.emoji.EmojiPalettesView;
33import com.android.inputmethod.keyboard.internal.KeyboardState;
34import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
35import com.android.inputmethod.latin.InputView;
36import com.android.inputmethod.latin.LatinIME;
37import com.android.inputmethod.latin.R;
38import com.android.inputmethod.latin.RichInputMethodManager;
39import com.android.inputmethod.latin.SubtypeSwitcher;
40import com.android.inputmethod.latin.WordComposer;
41import com.android.inputmethod.latin.settings.SettingsValues;
42import com.android.inputmethod.latin.utils.ResourceUtils;
43import com.android.inputmethod.latin.utils.ScriptUtils;
44
45public 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    private SettingsValues mCurrentSettingsValues;
64
65    private KeyboardTheme mKeyboardTheme;
66    private Context mThemeContext;
67
68    private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
69
70    public static KeyboardSwitcher getInstance() {
71        return sInstance;
72    }
73
74    private KeyboardSwitcher() {
75        // Intentional empty constructor for singleton.
76    }
77
78    public static void init(final LatinIME latinIme) {
79        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme);
80        sInstance.initInternal(latinIme, prefs);
81    }
82
83    private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) {
84        mLatinIME = latinIme;
85        mPrefs = prefs;
86        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
87        mState = new KeyboardState(this);
88        mIsHardwareAcceleratedDrawingEnabled =
89                InputMethodServiceCompatUtils.enableHardwareAcceleration(mLatinIME);
90    }
91
92    public void updateKeyboardTheme() {
93        final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper(
94                mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs));
95        if (themeUpdated && mKeyboardView != null) {
96            mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled));
97        }
98    }
99
100    private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context,
101            final KeyboardTheme keyboardTheme) {
102        if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) {
103            mKeyboardTheme = keyboardTheme;
104            mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
105            KeyboardLayoutSet.clearKeyboardCache();
106            return true;
107        }
108        return false;
109    }
110
111    public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues,
112            final int currentAutoCapsState, final int currentRecapitalizeState) {
113        final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
114                mThemeContext, editorInfo);
115        final Resources res = mThemeContext.getResources();
116        final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
117        final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
118        builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
119        builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
120        builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey);
121        builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey());
122        mKeyboardLayoutSet = builder.build();
123        mCurrentSettingsValues = settingsValues;
124        try {
125            mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
126            mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale(), mThemeContext);
127        } catch (KeyboardLayoutSetException e) {
128            Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
129            return;
130        }
131    }
132
133    public void saveKeyboardState() {
134        if (getKeyboard() != null || isShowingEmojiPalettes()) {
135            mState.onSaveKeyboardState();
136        }
137    }
138
139    public void onHideWindow() {
140        if (mKeyboardView != null) {
141            mKeyboardView.onHideWindow();
142        }
143    }
144
145    private void setKeyboard(final Keyboard keyboard) {
146        // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}.
147        setMainKeyboardFrame();
148        final MainKeyboardView keyboardView = mKeyboardView;
149        final Keyboard oldKeyboard = keyboardView.getKeyboard();
150        keyboardView.setKeyboard(keyboard);
151        mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding);
152        keyboardView.setKeyPreviewPopupEnabled(
153                mCurrentSettingsValues.mKeyPreviewPopupOn,
154                mCurrentSettingsValues.mKeyPreviewPopupDismissDelay);
155        keyboardView.setKeyPreviewAnimationParams(
156                mCurrentSettingsValues.mKeyPreviewShowUpStartScale,
157                mCurrentSettingsValues.mKeyPreviewShowUpDuration,
158                mCurrentSettingsValues.mKeyPreviewDismissEndScale,
159                mCurrentSettingsValues.mKeyPreviewDismissDuration);
160        keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
161        final boolean subtypeChanged = (oldKeyboard == null)
162                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
163        final int languageOnSpacebarFormatType = mSubtypeSwitcher.getLanguageOnSpacebarFormatType(
164                keyboard.mId.mSubtype);
165        final boolean hasMultipleEnabledIMEsOrSubtypes = RichInputMethodManager.getInstance()
166                .hasMultipleEnabledIMEsOrSubtypes(true /* shouldIncludeAuxiliarySubtypes */);
167        keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, languageOnSpacebarFormatType,
168                hasMultipleEnabledIMEsOrSubtypes);
169    }
170
171    public Keyboard getKeyboard() {
172        if (mKeyboardView != null) {
173            return mKeyboardView.getKeyboard();
174        }
175        return null;
176    }
177
178    // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
179    // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
180    public void resetKeyboardStateToAlphabet(final int currentAutoCapsState,
181            final int currentRecapitalizeState) {
182        mState.onResetKeyboardStateToAlphabet(currentAutoCapsState, currentRecapitalizeState);
183    }
184
185    public void onPressKey(final int code, final boolean isSinglePointer,
186            final int currentAutoCapsState, final int currentRecapitalizeState) {
187        mState.onPressKey(code, isSinglePointer, currentAutoCapsState, currentRecapitalizeState);
188    }
189
190    public void onReleaseKey(final int code, final boolean withSliding,
191            final int currentAutoCapsState, final int currentRecapitalizeState) {
192        mState.onReleaseKey(code, withSliding, currentAutoCapsState, currentRecapitalizeState);
193    }
194
195    public void onFinishSlidingInput(final int currentAutoCapsState,
196            final int currentRecapitalizeState) {
197        mState.onFinishSlidingInput(currentAutoCapsState, currentRecapitalizeState);
198    }
199
200    // Implements {@link KeyboardState.SwitchActions}.
201    @Override
202    public void setAlphabetKeyboard() {
203        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
204    }
205
206    // Implements {@link KeyboardState.SwitchActions}.
207    @Override
208    public void setAlphabetManualShiftedKeyboard() {
209        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
210    }
211
212    // Implements {@link KeyboardState.SwitchActions}.
213    @Override
214    public void setAlphabetAutomaticShiftedKeyboard() {
215        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
216    }
217
218    // Implements {@link KeyboardState.SwitchActions}.
219    @Override
220    public void setAlphabetShiftLockedKeyboard() {
221        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
222    }
223
224    // Implements {@link KeyboardState.SwitchActions}.
225    @Override
226    public void setAlphabetShiftLockShiftedKeyboard() {
227        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
228    }
229
230    // Implements {@link KeyboardState.SwitchActions}.
231    @Override
232    public void setSymbolsKeyboard() {
233        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
234    }
235
236    private void setMainKeyboardFrame() {
237        mMainKeyboardFrame.setVisibility(hasHardwareKeyboard() ? View.GONE : View.VISIBLE);
238        mEmojiPalettesView.setVisibility(View.GONE);
239        mEmojiPalettesView.stopEmojiPalettes();
240    }
241
242    // TODO: Move this boolean to a member of {@link SettingsValues} and reset it
243    // at {@link LatinIME#onConfigurationChanged(Configuration)}.
244    public boolean hasHardwareKeyboard() {
245        // Copied from {@link InputMethodServce#onEvaluateInputViewShown()}.
246        final Configuration config = mLatinIME.getResources().getConfiguration();
247        final boolean noHardwareKeyboard = config.keyboard == Configuration.KEYBOARD_NOKEYS
248                || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
249        return !noHardwareKeyboard;
250    }
251
252    // Implements {@link KeyboardState.SwitchActions}.
253    @Override
254    public void setEmojiKeyboard() {
255        final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
256        mMainKeyboardFrame.setVisibility(View.GONE);
257        mEmojiPalettesView.startEmojiPalettes(
258                mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
259                mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet);
260        mEmojiPalettesView.setVisibility(View.VISIBLE);
261    }
262
263    public void onToggleEmojiKeyboard() {
264        if (mKeyboardLayoutSet == null) {
265            return;
266        }
267        if (isShowingEmojiPalettes()) {
268            setAlphabetKeyboard();
269        } else {
270            setEmojiKeyboard();
271        }
272    }
273
274    // Implements {@link KeyboardState.SwitchActions}.
275    @Override
276    public void setSymbolsShiftedKeyboard() {
277        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED));
278    }
279
280    // Future method for requesting an updating to the shift state.
281    public void requestUpdatingShiftState(final int currentAutoCapsState,
282            final int currentRecapitalizeState) {
283        mState.onUpdateShiftState(currentAutoCapsState, currentRecapitalizeState);
284    }
285
286    // Implements {@link KeyboardState.SwitchActions}.
287    @Override
288    public void startDoubleTapShiftKeyTimer() {
289        final MainKeyboardView keyboardView = getMainKeyboardView();
290        if (keyboardView != null) {
291            keyboardView.startDoubleTapShiftKeyTimer();
292        }
293    }
294
295    // Implements {@link KeyboardState.SwitchActions}.
296    @Override
297    public void cancelDoubleTapShiftKeyTimer() {
298        final MainKeyboardView keyboardView = getMainKeyboardView();
299        if (keyboardView != null) {
300            keyboardView.cancelDoubleTapShiftKeyTimer();
301        }
302    }
303
304    // Implements {@link KeyboardState.SwitchActions}.
305    @Override
306    public boolean isInDoubleTapShiftKeyTimeout() {
307        final MainKeyboardView keyboardView = getMainKeyboardView();
308        return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
309    }
310
311    /**
312     * Updates state machine to figure out when to automatically switch back to the previous mode.
313     */
314    public void onCodeInput(final int code, final int currentAutoCapsState,
315            final int currentRecapitalizeState) {
316        mState.onCodeInput(code, currentAutoCapsState, currentRecapitalizeState);
317    }
318
319    public boolean isShowingEmojiPalettes() {
320        return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
321    }
322
323    public boolean isShowingMoreKeysPanel() {
324        if (isShowingEmojiPalettes()) {
325            return false;
326        }
327        return mKeyboardView.isShowingMoreKeysPanel();
328    }
329
330    public View getVisibleKeyboardView() {
331        if (isShowingEmojiPalettes()) {
332            return mEmojiPalettesView;
333        }
334        return mKeyboardView;
335    }
336
337    public MainKeyboardView getMainKeyboardView() {
338        return mKeyboardView;
339    }
340
341    public void deallocateMemory() {
342        if (mKeyboardView != null) {
343            mKeyboardView.cancelAllOngoingEvents();
344            mKeyboardView.deallocateMemory();
345        }
346        if (mEmojiPalettesView != null) {
347            mEmojiPalettesView.stopEmojiPalettes();
348        }
349    }
350
351    public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
352        if (mKeyboardView != null) {
353            mKeyboardView.closing();
354        }
355
356        updateKeyboardThemeAndContextThemeWrapper(
357                mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs));
358        mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
359                R.layout.input_view, null);
360        mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
361        mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
362                R.id.emoji_keyboard_view);
363
364        mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
365        mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
366        mKeyboardView.setKeyboardActionListener(mLatinIME);
367        mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
368                isHardwareAcceleratedDrawingEnabled);
369        mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
370        return mCurrentInputView;
371    }
372
373    public void onNetworkStateChanged() {
374        if (mKeyboardView != null) {
375            mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
376        }
377    }
378
379    public int getKeyboardShiftMode() {
380        final Keyboard keyboard = getKeyboard();
381        if (keyboard == null) {
382            return WordComposer.CAPS_MODE_OFF;
383        }
384        switch (keyboard.mId.mElementId) {
385        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
386        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
387            return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
388        case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
389            return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
390        case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
391            return WordComposer.CAPS_MODE_AUTO_SHIFTED;
392        default:
393            return WordComposer.CAPS_MODE_OFF;
394        }
395    }
396
397    public int getCurrentKeyboardScriptId() {
398        if (null == mKeyboardLayoutSet) {
399            return ScriptUtils.SCRIPT_UNKNOWN;
400        }
401        return mKeyboardLayoutSet.getScriptId();
402    }
403}
404