/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.android.inputmethod.keyboard; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.util.DisplayMetrics; import android.util.Xml; import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.internal.XmlParseUtils; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SettingsValues; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.HashMap; import java.util.Locale; /** * This class has a set of {@link KeyboardId}s. Each of them represents a different keyboard * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same * {@link KeyboardSet} are related to each other. * A {@link KeyboardSet} needs to be created for each {@link android.view.inputmethod.EditorInfo}. */ public class KeyboardSet { private static final String TAG_KEYBOARD_SET = "KeyboardSet"; private static final String TAG_ELEMENT = "Element"; // TODO: Make these KeyboardId private. public final KeyboardId mAlphabetId; public final KeyboardId mSymbolsId; public final KeyboardId mSymbolsShiftedId; KeyboardSet(Builder builder) { mAlphabetId = builder.getKeyboardId(false, false); mSymbolsId = builder.getKeyboardId(true, false); mSymbolsShiftedId = builder.getKeyboardId(true, true); } public static class Builder { private final Resources mResources; private final EditorInfo mEditorInfo; private final HashMap mElementKeyboards = new HashMap(); private final int mMode; private final boolean mSettingsKeyEnabled; private final boolean mVoiceKeyEnabled; private final boolean mVoiceKeyOnMain; private final boolean mNoSettingsKey; private final Locale mLocale; private final Configuration mConf; private final DisplayMetrics mMetrics; public Builder(Context context, EditorInfo editorInfo, SettingsValues settingsValues) { mResources = context.getResources(); mEditorInfo = editorInfo; final SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance(); final String packageName = context.getPackageName(); mMode = Utils.getKeyboardMode(mEditorInfo); mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled(); @SuppressWarnings("deprecation") final boolean noMicrophone = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo) || Utils.inPrivateImeOptions( null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo); mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone; mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain(); mNoSettingsKey = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo); final boolean forceAscii = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo); final boolean asciiCapable = subtypeSwitcher.currentSubtypeContainsExtraValueKey( LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE); mLocale = (forceAscii && !asciiCapable) ? Locale.US : subtypeSwitcher.getInputLocale(); mConf = mResources.getConfiguration(); mMetrics = mResources.getDisplayMetrics(); } public KeyboardSet build() { final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mLocale); try { parseKeyboardSet(mResources, R.xml.keyboard_set); } catch (Exception e) { // } finally { LocaleUtils.setSystemLocale(mResources, savedLocale); } return new KeyboardSet(this); } KeyboardId getKeyboardId(boolean isSymbols, boolean isShift) { final int elementState = getElementState(mMode, isSymbols, isShift); final int xmlId = mElementKeyboards.get(elementState); final boolean hasShortcutKey = mVoiceKeyEnabled && (isSymbols != mVoiceKeyOnMain); return new KeyboardId(xmlId, elementState, mLocale, mConf.orientation, mMetrics.widthPixels, mMode, mEditorInfo, mSettingsKeyEnabled, mNoSettingsKey, mVoiceKeyEnabled, hasShortcutKey); } private static int getElementState(int mode, boolean isSymbols, boolean isShift) { switch (mode) { case KeyboardId.MODE_PHONE: return (isSymbols && isShift) ? KeyboardId.ELEMENT_PHONE_SHIFT : KeyboardId.ELEMENT_PHONE; case KeyboardId.MODE_NUMBER: return KeyboardId.ELEMENT_NUMBER; default: if (isSymbols) { return isShift ? KeyboardId.ELEMENT_SYMBOLS_SHIFT : KeyboardId.ELEMENT_SYMBOLS; } return KeyboardId.ELEMENT_ALPHABET; } } private void parseKeyboardSet(Resources res, int resId) throws XmlPullParserException, IOException { final XmlResourceParser parser = res.getXml(resId); try { int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { final String tag = parser.getName(); if (TAG_KEYBOARD_SET.equals(tag)) { parseKeyboardSetContent(parser); } else { throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET); } } } } finally { parser.close(); } } private void parseKeyboardSetContent(XmlPullParser parser) throws XmlPullParserException, IOException { int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { final String tag = parser.getName(); if (TAG_ELEMENT.equals(tag)) { parseKeyboardSetElement(parser); } else { throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET); } } else if (event == XmlPullParser.END_TAG) { final String tag = parser.getName(); if (TAG_KEYBOARD_SET.equals(tag)) { break; } else { throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEYBOARD_SET); } } } } private void parseKeyboardSetElement(XmlPullParser parser) throws XmlPullParserException, IOException { final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.KeyboardSet_Element); try { XmlParseUtils.checkAttributeExists(a, R.styleable.KeyboardSet_Element_elementName, "elementName", TAG_ELEMENT, parser); XmlParseUtils.checkAttributeExists(a, R.styleable.KeyboardSet_Element_elementKeyboard, "elementKeyboard", TAG_ELEMENT, parser); XmlParseUtils.checkEndTag(TAG_ELEMENT, parser); final int elementName = a.getInt( R.styleable.KeyboardSet_Element_elementName, 0); final int elementKeyboard = a.getResourceId( R.styleable.KeyboardSet_Element_elementKeyboard, 0); mElementKeyboards.put(elementName, elementKeyboard); } finally { a.recycle(); } } } public static String parseKeyboardLocale(Resources res, int resId) throws XmlPullParserException, IOException { final XmlPullParser parser = res.getXml(resId); if (parser == null) return ""; int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { final String tag = parser.getName(); if (TAG_KEYBOARD_SET.equals(tag)) { final TypedArray keyboardSetAttr = res.obtainAttributes( Xml.asAttributeSet(parser), R.styleable.KeyboardSet); final String locale = keyboardSetAttr.getString( R.styleable.KeyboardSet_keyboardLocale); keyboardSetAttr.recycle(); return locale; } else { throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET); } } } return ""; } }