2011-12-12 07:11:37 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011 The Android Open Source Project
|
|
|
|
*
|
2013-01-21 12:52:57 +00:00
|
|
|
* 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
|
2011-12-12 07:11:37 +00:00
|
|
|
*
|
2013-01-21 12:52:57 +00:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2011-12-12 07:11:37 +00:00
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
2013-01-21 12:52:57 +00:00
|
|
|
* 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.
|
2011-12-12 07:11:37 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.inputmethod.keyboard;
|
|
|
|
|
2012-04-19 03:42:13 +00:00
|
|
|
import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII;
|
|
|
|
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE;
|
|
|
|
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT;
|
|
|
|
import static com.android.inputmethod.latin.Constants.ImeOption.NO_SETTINGS_KEY;
|
|
|
|
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE;
|
|
|
|
|
2011-12-12 07:11:37 +00:00
|
|
|
import android.content.Context;
|
2012-01-13 07:14:02 +00:00
|
|
|
import android.content.res.Configuration;
|
2011-12-12 07:11:37 +00:00
|
|
|
import android.content.res.Resources;
|
2011-12-13 08:30:51 +00:00
|
|
|
import android.content.res.TypedArray;
|
|
|
|
import android.content.res.XmlResourceParser;
|
2012-03-08 08:07:02 +00:00
|
|
|
import android.text.InputType;
|
2013-01-29 07:11:52 +00:00
|
|
|
import android.text.TextUtils;
|
2013-05-07 10:09:30 +00:00
|
|
|
import android.util.DisplayMetrics;
|
2011-12-15 05:45:14 +00:00
|
|
|
import android.util.Log;
|
2012-06-29 07:32:45 +00:00
|
|
|
import android.util.SparseArray;
|
2011-12-13 08:30:51 +00:00
|
|
|
import android.util.Xml;
|
2011-12-12 07:11:37 +00:00
|
|
|
import android.view.inputmethod.EditorInfo;
|
2012-04-04 09:18:56 +00:00
|
|
|
import android.view.inputmethod.InputMethodSubtype;
|
2011-12-12 07:11:37 +00:00
|
|
|
|
2013-05-21 03:14:13 +00:00
|
|
|
import com.android.inputmethod.annotations.UsedForTesting;
|
2012-01-17 05:38:00 +00:00
|
|
|
import com.android.inputmethod.compat.EditorInfoCompatUtils;
|
2012-08-30 05:22:40 +00:00
|
|
|
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
|
|
|
|
import com.android.inputmethod.keyboard.internal.KeyboardParams;
|
|
|
|
import com.android.inputmethod.keyboard.internal.KeysCache;
|
2013-05-07 10:09:30 +00:00
|
|
|
import com.android.inputmethod.latin.AdditionalSubtype;
|
2012-08-21 07:34:55 +00:00
|
|
|
import com.android.inputmethod.latin.CollectionUtils;
|
2012-04-19 07:39:25 +00:00
|
|
|
import com.android.inputmethod.latin.InputAttributes;
|
2012-04-02 13:43:38 +00:00
|
|
|
import com.android.inputmethod.latin.InputTypeUtils;
|
2011-12-15 05:45:14 +00:00
|
|
|
import com.android.inputmethod.latin.LatinImeLogger;
|
2011-12-12 07:11:37 +00:00
|
|
|
import com.android.inputmethod.latin.R;
|
2013-01-29 07:11:52 +00:00
|
|
|
import com.android.inputmethod.latin.ResourceUtils;
|
2012-04-04 03:52:11 +00:00
|
|
|
import com.android.inputmethod.latin.SubtypeLocale;
|
2012-04-04 05:30:42 +00:00
|
|
|
import com.android.inputmethod.latin.SubtypeSwitcher;
|
2011-12-18 10:54:08 +00:00
|
|
|
import com.android.inputmethod.latin.XmlParseUtils;
|
2011-12-12 07:11:37 +00:00
|
|
|
|
2011-12-13 08:30:51 +00:00
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
2011-12-15 05:45:14 +00:00
|
|
|
import java.lang.ref.SoftReference;
|
2011-12-13 08:30:51 +00:00
|
|
|
import java.util.HashMap;
|
2011-12-12 07:11:37 +00:00
|
|
|
|
|
|
|
/**
|
2012-04-04 05:30:42 +00:00
|
|
|
* This class represents a set of keyboard layouts. Each of them represents a different keyboard
|
2011-12-15 05:45:14 +00:00
|
|
|
* specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same
|
2012-04-04 05:30:42 +00:00
|
|
|
* {@link KeyboardLayoutSet} are related to each other.
|
|
|
|
* A {@link KeyboardLayoutSet} needs to be created for each
|
|
|
|
* {@link android.view.inputmethod.EditorInfo}.
|
2011-12-12 07:11:37 +00:00
|
|
|
*/
|
2012-09-27 09:16:16 +00:00
|
|
|
public final class KeyboardLayoutSet {
|
2012-04-04 05:30:42 +00:00
|
|
|
private static final String TAG = KeyboardLayoutSet.class.getSimpleName();
|
2011-12-15 05:45:14 +00:00
|
|
|
private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
|
2011-12-13 08:30:51 +00:00
|
|
|
|
2012-04-04 05:30:42 +00:00
|
|
|
private static final String TAG_KEYBOARD_SET = "KeyboardLayoutSet";
|
2011-12-15 05:45:14 +00:00
|
|
|
private static final String TAG_ELEMENT = "Element";
|
2011-12-12 07:11:37 +00:00
|
|
|
|
2012-04-05 05:59:55 +00:00
|
|
|
private static final String KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX = "keyboard_layout_set_";
|
2013-05-07 10:09:30 +00:00
|
|
|
private static final int SPELLCHECKER_DUMMY_KEYBOARD_WIDTH = 480;
|
|
|
|
private static final int SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT = 800;
|
2012-04-04 05:30:42 +00:00
|
|
|
|
2011-12-15 05:45:14 +00:00
|
|
|
private final Context mContext;
|
|
|
|
private final Params mParams;
|
2012-02-02 12:24:09 +00:00
|
|
|
|
|
|
|
private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache =
|
2012-08-21 07:34:55 +00:00
|
|
|
CollectionUtils.newHashMap();
|
2012-02-02 12:24:09 +00:00
|
|
|
private static final KeysCache sKeysCache = new KeysCache();
|
2012-01-25 10:43:13 +00:00
|
|
|
|
2012-12-11 03:46:23 +00:00
|
|
|
@SuppressWarnings("serial")
|
2012-09-27 09:16:16 +00:00
|
|
|
public static final class KeyboardLayoutSetException extends RuntimeException {
|
2012-01-31 08:15:24 +00:00
|
|
|
public final KeyboardId mKeyboardId;
|
2012-04-03 05:28:56 +00:00
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
public KeyboardLayoutSetException(final Throwable cause, final KeyboardId keyboardId) {
|
2012-01-31 08:15:24 +00:00
|
|
|
super(cause);
|
|
|
|
mKeyboardId = keyboardId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-27 09:16:16 +00:00
|
|
|
private static final class ElementParams {
|
2012-08-30 05:22:40 +00:00
|
|
|
int mKeyboardXmlId;
|
|
|
|
boolean mProximityCharsCorrectionEnabled;
|
|
|
|
public ElementParams() {}
|
2012-01-25 10:43:13 +00:00
|
|
|
}
|
2011-12-16 07:22:20 +00:00
|
|
|
|
2012-12-11 03:46:23 +00:00
|
|
|
public static final class Params {
|
2012-04-04 05:30:42 +00:00
|
|
|
String mKeyboardLayoutSetName;
|
2011-12-16 07:22:20 +00:00
|
|
|
int mMode;
|
2012-02-07 08:07:23 +00:00
|
|
|
EditorInfo mEditorInfo;
|
2012-09-25 02:58:36 +00:00
|
|
|
boolean mDisableTouchPositionCorrectionDataForTest;
|
2011-12-16 07:22:20 +00:00
|
|
|
boolean mVoiceKeyEnabled;
|
|
|
|
boolean mVoiceKeyOnMain;
|
|
|
|
boolean mNoSettingsKey;
|
2012-02-18 01:46:01 +00:00
|
|
|
boolean mLanguageSwitchKeyEnabled;
|
2012-04-11 09:21:10 +00:00
|
|
|
InputMethodSubtype mSubtype;
|
2011-12-16 07:22:20 +00:00
|
|
|
int mOrientation;
|
2013-01-29 07:11:52 +00:00
|
|
|
int mKeyboardWidth;
|
|
|
|
int mKeyboardHeight;
|
2012-06-29 07:32:45 +00:00
|
|
|
// Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
|
|
|
|
final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
|
2012-08-22 05:22:20 +00:00
|
|
|
CollectionUtils.newSparseArray();
|
2011-12-12 07:11:37 +00:00
|
|
|
}
|
|
|
|
|
2011-12-15 05:45:14 +00:00
|
|
|
public static void clearKeyboardCache() {
|
|
|
|
sKeyboardCache.clear();
|
2012-02-02 12:24:09 +00:00
|
|
|
sKeysCache.clear();
|
2011-12-15 05:45:14 +00:00
|
|
|
}
|
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
KeyboardLayoutSet(final Context context, final Params params) {
|
2011-12-15 05:45:14 +00:00
|
|
|
mContext = context;
|
|
|
|
mParams = params;
|
|
|
|
}
|
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
public Keyboard getKeyboard(final int baseKeyboardLayoutSetElementId) {
|
2012-04-04 05:30:42 +00:00
|
|
|
final int keyboardLayoutSetElementId;
|
2012-01-26 09:03:30 +00:00
|
|
|
switch (mParams.mMode) {
|
|
|
|
case KeyboardId.MODE_PHONE:
|
2012-04-04 05:30:42 +00:00
|
|
|
if (baseKeyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS) {
|
|
|
|
keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS;
|
2012-02-01 06:07:25 +00:00
|
|
|
} else {
|
2012-04-04 05:30:42 +00:00
|
|
|
keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE;
|
2012-02-01 06:07:25 +00:00
|
|
|
}
|
2012-01-26 09:03:30 +00:00
|
|
|
break;
|
|
|
|
case KeyboardId.MODE_NUMBER:
|
2012-02-27 07:16:24 +00:00
|
|
|
case KeyboardId.MODE_DATE:
|
|
|
|
case KeyboardId.MODE_TIME:
|
|
|
|
case KeyboardId.MODE_DATETIME:
|
2012-04-04 05:30:42 +00:00
|
|
|
keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER;
|
2012-01-26 09:03:30 +00:00
|
|
|
break;
|
|
|
|
default:
|
2012-04-04 05:30:42 +00:00
|
|
|
keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId;
|
2012-01-26 09:03:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-12-15 05:45:14 +00:00
|
|
|
|
2012-04-04 05:30:42 +00:00
|
|
|
ElementParams elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get(
|
|
|
|
keyboardLayoutSetElementId);
|
2012-03-28 08:33:25 +00:00
|
|
|
if (elementParams == null) {
|
2012-04-04 05:30:42 +00:00
|
|
|
elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get(
|
2012-01-26 09:03:30 +00:00
|
|
|
KeyboardId.ELEMENT_ALPHABET);
|
|
|
|
}
|
2012-12-11 03:46:23 +00:00
|
|
|
// Note: The keyboard for each shift state, and mode are represented as an elementName
|
|
|
|
// attribute in a keyboard_layout_set XML file. Also each keyboard layout XML resource is
|
|
|
|
// specified as an elementKeyboard attribute in the file.
|
|
|
|
// The KeyboardId is an internal key for a Keyboard object.
|
|
|
|
final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams);
|
2012-01-31 08:15:24 +00:00
|
|
|
try {
|
2012-04-13 04:07:28 +00:00
|
|
|
return getKeyboard(elementParams, id);
|
2012-01-31 08:15:24 +00:00
|
|
|
} catch (RuntimeException e) {
|
2012-04-04 05:30:42 +00:00
|
|
|
throw new KeyboardLayoutSetException(e, id);
|
2012-01-31 08:15:24 +00:00
|
|
|
}
|
2011-12-15 05:45:14 +00:00
|
|
|
}
|
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
private Keyboard getKeyboard(final ElementParams elementParams, final KeyboardId id) {
|
2011-12-17 23:36:16 +00:00
|
|
|
final SoftReference<Keyboard> ref = sKeyboardCache.get(id);
|
|
|
|
Keyboard keyboard = (ref == null) ? null : ref.get();
|
2011-12-15 05:45:14 +00:00
|
|
|
if (keyboard == null) {
|
2012-08-30 05:22:40 +00:00
|
|
|
final KeyboardBuilder<KeyboardParams> builder =
|
|
|
|
new KeyboardBuilder<KeyboardParams>(mContext, new KeyboardParams());
|
2012-04-03 05:28:56 +00:00
|
|
|
if (id.isAlphabetKeyboard()) {
|
|
|
|
builder.setAutoGenerate(sKeysCache);
|
2011-12-15 05:45:14 +00:00
|
|
|
}
|
2012-04-03 05:28:56 +00:00
|
|
|
final int keyboardXmlId = elementParams.mKeyboardXmlId;
|
2012-04-10 10:06:03 +00:00
|
|
|
builder.load(keyboardXmlId, id);
|
2012-09-25 02:58:36 +00:00
|
|
|
if (mParams.mDisableTouchPositionCorrectionDataForTest) {
|
|
|
|
builder.disableTouchPositionCorrectionDataForTest();
|
|
|
|
}
|
2012-04-03 05:28:56 +00:00
|
|
|
builder.setProximityCharsCorrectionEnabled(
|
|
|
|
elementParams.mProximityCharsCorrectionEnabled);
|
|
|
|
keyboard = builder.build();
|
2011-12-17 23:36:16 +00:00
|
|
|
sKeyboardCache.put(id, new SoftReference<Keyboard>(keyboard));
|
2011-12-15 05:45:14 +00:00
|
|
|
|
|
|
|
if (DEBUG_CACHE) {
|
|
|
|
Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": "
|
|
|
|
+ ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
|
|
|
|
}
|
|
|
|
} else if (DEBUG_CACHE) {
|
|
|
|
Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": HIT id=" + id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return keyboard;
|
|
|
|
}
|
|
|
|
|
2012-09-27 09:16:16 +00:00
|
|
|
public static final class Builder {
|
2011-12-15 05:45:14 +00:00
|
|
|
private final Context mContext;
|
2012-01-13 07:14:02 +00:00
|
|
|
private final String mPackageName;
|
2011-12-12 07:11:37 +00:00
|
|
|
private final Resources mResources;
|
2012-01-13 07:14:02 +00:00
|
|
|
private final EditorInfo mEditorInfo;
|
2011-12-12 07:11:37 +00:00
|
|
|
|
2011-12-16 07:22:20 +00:00
|
|
|
private final Params mParams = new Params();
|
2011-12-12 07:11:37 +00:00
|
|
|
|
2012-03-28 09:35:01 +00:00
|
|
|
private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
|
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
public Builder(final Context context, final EditorInfo editorInfo) {
|
2011-12-15 05:45:14 +00:00
|
|
|
mContext = context;
|
2012-01-13 07:14:02 +00:00
|
|
|
mPackageName = context.getPackageName();
|
2011-12-12 07:11:37 +00:00
|
|
|
mResources = context.getResources();
|
2012-01-13 07:14:02 +00:00
|
|
|
mEditorInfo = editorInfo;
|
2011-12-16 07:22:20 +00:00
|
|
|
final Params params = mParams;
|
2011-12-12 07:11:37 +00:00
|
|
|
|
2012-03-08 08:07:02 +00:00
|
|
|
params.mMode = getKeyboardMode(editorInfo);
|
2012-02-07 08:07:23 +00:00
|
|
|
params.mEditorInfo = (editorInfo != null) ? editorInfo : EMPTY_EDITOR_INFO;
|
2012-04-19 07:39:25 +00:00
|
|
|
params.mNoSettingsKey = InputAttributes.inPrivateImeOptions(
|
2012-04-19 03:42:13 +00:00
|
|
|
mPackageName, NO_SETTINGS_KEY, mEditorInfo);
|
2012-01-13 07:14:02 +00:00
|
|
|
}
|
|
|
|
|
2013-05-07 09:52:22 +00:00
|
|
|
public Builder setScreenGeometry(final int widthPixels, final int heightPixels) {
|
2012-07-30 04:35:06 +00:00
|
|
|
final Params params = mParams;
|
2013-01-29 07:11:52 +00:00
|
|
|
params.mOrientation = (heightPixels > widthPixels)
|
|
|
|
? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
|
|
|
|
setDefaultKeyboardSize(widthPixels, heightPixels);
|
2012-01-13 07:14:02 +00:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2013-01-29 07:11:52 +00:00
|
|
|
private void setDefaultKeyboardSize(final int widthPixels, final int heightPixels) {
|
|
|
|
final String keyboardHeightString = ResourceUtils.getDeviceOverrideValue(
|
|
|
|
mResources, R.array.keyboard_heights);
|
|
|
|
final float keyboardHeight;
|
|
|
|
if (TextUtils.isEmpty(keyboardHeightString)) {
|
|
|
|
keyboardHeight = mResources.getDimension(R.dimen.keyboardHeight);
|
|
|
|
} else {
|
|
|
|
keyboardHeight = Float.parseFloat(keyboardHeightString)
|
|
|
|
* mResources.getDisplayMetrics().density;
|
|
|
|
}
|
|
|
|
final float maxKeyboardHeight = mResources.getFraction(
|
|
|
|
R.fraction.maxKeyboardHeight, heightPixels, heightPixels);
|
|
|
|
float minKeyboardHeight = mResources.getFraction(
|
|
|
|
R.fraction.minKeyboardHeight, heightPixels, heightPixels);
|
|
|
|
if (minKeyboardHeight < 0.0f) {
|
|
|
|
// Specified fraction was negative, so it should be calculated against display
|
|
|
|
// width.
|
|
|
|
minKeyboardHeight = -mResources.getFraction(
|
|
|
|
R.fraction.minKeyboardHeight, widthPixels, widthPixels);
|
|
|
|
}
|
|
|
|
// Keyboard height will not exceed maxKeyboardHeight and will not be less than
|
|
|
|
// minKeyboardHeight.
|
|
|
|
mParams.mKeyboardHeight = (int)Math.max(
|
|
|
|
Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
|
|
|
|
mParams.mKeyboardWidth = widthPixels;
|
|
|
|
}
|
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
public Builder setSubtype(final InputMethodSubtype subtype) {
|
2012-04-19 03:42:13 +00:00
|
|
|
final boolean asciiCapable = subtype.containsExtraValueKey(ASCII_CAPABLE);
|
|
|
|
@SuppressWarnings("deprecation")
|
2012-04-19 07:39:25 +00:00
|
|
|
final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
|
2012-04-19 03:42:13 +00:00
|
|
|
mPackageName, FORCE_ASCII, mEditorInfo);
|
2012-02-07 08:07:23 +00:00
|
|
|
final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii(
|
|
|
|
mParams.mEditorInfo.imeOptions)
|
2012-01-25 11:54:00 +00:00
|
|
|
|| deprecatedForceAscii;
|
2012-04-04 05:30:42 +00:00
|
|
|
final InputMethodSubtype keyboardSubtype = (forceAscii && !asciiCapable)
|
|
|
|
? SubtypeSwitcher.getInstance().getNoLanguageSubtype()
|
|
|
|
: subtype;
|
2012-04-11 09:21:10 +00:00
|
|
|
mParams.mSubtype = keyboardSubtype;
|
2012-04-04 05:30:42 +00:00
|
|
|
mParams.mKeyboardLayoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
|
2012-04-13 04:07:28 +00:00
|
|
|
+ SubtypeLocale.getKeyboardLayoutSetName(keyboardSubtype);
|
2012-01-13 07:14:02 +00:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
public Builder setOptions(final boolean voiceKeyEnabled, final boolean voiceKeyOnMain,
|
|
|
|
final boolean languageSwitchKeyEnabled) {
|
2011-12-12 07:11:37 +00:00
|
|
|
@SuppressWarnings("deprecation")
|
2012-04-19 07:39:25 +00:00
|
|
|
final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions(
|
2012-04-19 03:42:13 +00:00
|
|
|
null, NO_MICROPHONE_COMPAT, mEditorInfo);
|
2012-04-19 07:39:25 +00:00
|
|
|
final boolean noMicrophone = InputAttributes.inPrivateImeOptions(
|
2012-04-19 03:42:13 +00:00
|
|
|
mPackageName, NO_MICROPHONE, mEditorInfo)
|
2012-01-25 11:54:00 +00:00
|
|
|
|| deprecatedNoMicrophone;
|
2012-01-13 07:14:02 +00:00
|
|
|
mParams.mVoiceKeyEnabled = voiceKeyEnabled && !noMicrophone;
|
|
|
|
mParams.mVoiceKeyOnMain = voiceKeyOnMain;
|
2012-02-18 01:46:01 +00:00
|
|
|
mParams.mLanguageSwitchKeyEnabled = languageSwitchKeyEnabled;
|
2012-01-13 07:14:02 +00:00
|
|
|
return this;
|
2011-12-12 07:11:37 +00:00
|
|
|
}
|
|
|
|
|
2013-05-07 10:09:30 +00:00
|
|
|
public void disableTouchPositionCorrectionData() {
|
2012-09-25 02:58:36 +00:00
|
|
|
mParams.mDisableTouchPositionCorrectionDataForTest = true;
|
2012-03-28 09:35:01 +00:00
|
|
|
}
|
|
|
|
|
2012-04-04 05:30:42 +00:00
|
|
|
public KeyboardLayoutSet build() {
|
2012-01-13 07:14:02 +00:00
|
|
|
if (mParams.mOrientation == Configuration.ORIENTATION_UNDEFINED)
|
|
|
|
throw new RuntimeException("Screen geometry is not specified");
|
2012-04-11 09:21:10 +00:00
|
|
|
if (mParams.mSubtype == null)
|
2012-04-04 05:30:42 +00:00
|
|
|
throw new RuntimeException("KeyboardLayoutSet subtype is not specified");
|
2012-04-05 05:59:55 +00:00
|
|
|
final String packageName = mResources.getResourcePackageName(
|
|
|
|
R.xml.keyboard_layout_set_qwerty);
|
2012-04-04 05:30:42 +00:00
|
|
|
final String keyboardLayoutSetName = mParams.mKeyboardLayoutSetName;
|
2012-04-05 05:59:55 +00:00
|
|
|
final int xmlId = mResources.getIdentifier(keyboardLayoutSetName, "xml", packageName);
|
2012-04-10 10:06:03 +00:00
|
|
|
try {
|
|
|
|
parseKeyboardLayoutSet(mResources, xmlId);
|
2013-03-26 03:13:56 +00:00
|
|
|
} catch (final IOException e) {
|
|
|
|
throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e);
|
|
|
|
} catch (final XmlPullParserException e) {
|
2013-03-26 03:03:07 +00:00
|
|
|
throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e);
|
2012-04-10 10:06:03 +00:00
|
|
|
}
|
2012-04-04 05:30:42 +00:00
|
|
|
return new KeyboardLayoutSet(mContext, mParams);
|
2011-12-12 07:11:37 +00:00
|
|
|
}
|
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
private void parseKeyboardLayoutSet(final Resources res, final int resId)
|
2012-04-04 05:30:42 +00:00
|
|
|
throws XmlPullParserException, IOException {
|
2011-12-13 08:30:51 +00:00
|
|
|
final XmlResourceParser parser = res.getXml(resId);
|
|
|
|
try {
|
2013-03-26 03:13:56 +00:00
|
|
|
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
|
|
|
|
final int event = parser.next();
|
2011-12-13 08:30:51 +00:00
|
|
|
if (event == XmlPullParser.START_TAG) {
|
|
|
|
final String tag = parser.getName();
|
|
|
|
if (TAG_KEYBOARD_SET.equals(tag)) {
|
2012-04-04 05:30:42 +00:00
|
|
|
parseKeyboardLayoutSetContent(parser);
|
2011-12-13 08:30:51 +00:00
|
|
|
} else {
|
2013-03-26 03:03:07 +00:00
|
|
|
throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
|
2011-12-13 08:30:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
parser.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
private void parseKeyboardLayoutSetContent(final XmlPullParser parser)
|
2012-04-04 05:30:42 +00:00
|
|
|
throws XmlPullParserException, IOException {
|
2013-03-26 03:13:56 +00:00
|
|
|
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
|
|
|
|
final int event = parser.next();
|
2011-12-13 08:30:51 +00:00
|
|
|
if (event == XmlPullParser.START_TAG) {
|
|
|
|
final String tag = parser.getName();
|
|
|
|
if (TAG_ELEMENT.equals(tag)) {
|
2012-04-04 05:30:42 +00:00
|
|
|
parseKeyboardLayoutSetElement(parser);
|
2011-12-13 08:30:51 +00:00
|
|
|
} else {
|
2013-03-26 03:03:07 +00:00
|
|
|
throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
|
2011-12-13 08:30:51 +00:00
|
|
|
}
|
|
|
|
} else if (event == XmlPullParser.END_TAG) {
|
|
|
|
final String tag = parser.getName();
|
|
|
|
if (TAG_KEYBOARD_SET.equals(tag)) {
|
|
|
|
break;
|
|
|
|
} else {
|
2013-03-26 03:03:07 +00:00
|
|
|
throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_KEYBOARD_SET);
|
2011-12-13 08:30:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
private void parseKeyboardLayoutSetElement(final XmlPullParser parser)
|
2012-04-04 05:30:42 +00:00
|
|
|
throws XmlPullParserException, IOException {
|
2011-12-13 08:30:51 +00:00
|
|
|
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
|
2012-04-04 05:30:42 +00:00
|
|
|
R.styleable.KeyboardLayoutSet_Element);
|
2011-12-13 08:30:51 +00:00
|
|
|
try {
|
2011-12-14 09:11:27 +00:00
|
|
|
XmlParseUtils.checkAttributeExists(a,
|
2012-04-04 05:30:42 +00:00
|
|
|
R.styleable.KeyboardLayoutSet_Element_elementName, "elementName",
|
2011-12-14 09:11:27 +00:00
|
|
|
TAG_ELEMENT, parser);
|
|
|
|
XmlParseUtils.checkAttributeExists(a,
|
2012-04-04 05:30:42 +00:00
|
|
|
R.styleable.KeyboardLayoutSet_Element_elementKeyboard, "elementKeyboard",
|
2011-12-14 09:11:27 +00:00
|
|
|
TAG_ELEMENT, parser);
|
|
|
|
XmlParseUtils.checkEndTag(TAG_ELEMENT, parser);
|
2011-12-13 08:30:51 +00:00
|
|
|
|
2012-03-28 08:33:25 +00:00
|
|
|
final ElementParams elementParams = new ElementParams();
|
2011-12-13 08:30:51 +00:00
|
|
|
final int elementName = a.getInt(
|
2012-04-04 05:30:42 +00:00
|
|
|
R.styleable.KeyboardLayoutSet_Element_elementName, 0);
|
2012-03-28 08:33:25 +00:00
|
|
|
elementParams.mKeyboardXmlId = a.getResourceId(
|
2012-04-04 05:30:42 +00:00
|
|
|
R.styleable.KeyboardLayoutSet_Element_elementKeyboard, 0);
|
2012-03-28 08:33:25 +00:00
|
|
|
elementParams.mProximityCharsCorrectionEnabled = a.getBoolean(
|
2012-04-04 05:30:42 +00:00
|
|
|
R.styleable.KeyboardLayoutSet_Element_enableProximityCharsCorrection,
|
|
|
|
false);
|
|
|
|
mParams.mKeyboardLayoutSetElementIdToParamsMap.put(elementName, elementParams);
|
2011-12-13 08:30:51 +00:00
|
|
|
} finally {
|
|
|
|
a.recycle();
|
|
|
|
}
|
|
|
|
}
|
2012-03-08 08:07:02 +00:00
|
|
|
|
2012-08-30 05:22:40 +00:00
|
|
|
private static int getKeyboardMode(final EditorInfo editorInfo) {
|
2012-03-08 08:07:02 +00:00
|
|
|
if (editorInfo == null)
|
|
|
|
return KeyboardId.MODE_TEXT;
|
|
|
|
|
|
|
|
final int inputType = editorInfo.inputType;
|
|
|
|
final int variation = inputType & InputType.TYPE_MASK_VARIATION;
|
|
|
|
|
|
|
|
switch (inputType & InputType.TYPE_MASK_CLASS) {
|
|
|
|
case InputType.TYPE_CLASS_NUMBER:
|
|
|
|
return KeyboardId.MODE_NUMBER;
|
|
|
|
case InputType.TYPE_CLASS_DATETIME:
|
|
|
|
switch (variation) {
|
|
|
|
case InputType.TYPE_DATETIME_VARIATION_DATE:
|
|
|
|
return KeyboardId.MODE_DATE;
|
|
|
|
case InputType.TYPE_DATETIME_VARIATION_TIME:
|
|
|
|
return KeyboardId.MODE_TIME;
|
|
|
|
default: // InputType.TYPE_DATETIME_VARIATION_NORMAL
|
|
|
|
return KeyboardId.MODE_DATETIME;
|
|
|
|
}
|
|
|
|
case InputType.TYPE_CLASS_PHONE:
|
|
|
|
return KeyboardId.MODE_PHONE;
|
|
|
|
case InputType.TYPE_CLASS_TEXT:
|
2012-04-02 13:43:38 +00:00
|
|
|
if (InputTypeUtils.isEmailVariation(variation)) {
|
2012-03-08 08:07:02 +00:00
|
|
|
return KeyboardId.MODE_EMAIL;
|
|
|
|
} else if (variation == InputType.TYPE_TEXT_VARIATION_URI) {
|
|
|
|
return KeyboardId.MODE_URL;
|
|
|
|
} else if (variation == InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
|
|
|
|
return KeyboardId.MODE_IM;
|
|
|
|
} else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
|
|
|
|
return KeyboardId.MODE_TEXT;
|
|
|
|
} else {
|
|
|
|
return KeyboardId.MODE_TEXT;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return KeyboardId.MODE_TEXT;
|
|
|
|
}
|
|
|
|
}
|
2011-12-13 08:30:51 +00:00
|
|
|
}
|
2013-05-07 10:09:30 +00:00
|
|
|
|
|
|
|
public static KeyboardLayoutSet createKeyboardSetForSpellChecker(final Context context,
|
|
|
|
final String locale, final String layout) {
|
|
|
|
final InputMethodSubtype subtype =
|
|
|
|
AdditionalSubtype.createAdditionalSubtype(locale, layout, null);
|
|
|
|
return createKeyboardSet(context, subtype, SPELLCHECKER_DUMMY_KEYBOARD_WIDTH,
|
|
|
|
SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT, false);
|
|
|
|
}
|
|
|
|
|
2013-05-21 03:14:13 +00:00
|
|
|
@UsedForTesting
|
2013-05-07 10:09:30 +00:00
|
|
|
public static KeyboardLayoutSet createKeyboardSetForTest(final Context context,
|
|
|
|
final InputMethodSubtype subtype, final int orientation,
|
|
|
|
final boolean testCasesHaveTouchCoordinates) {
|
|
|
|
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
|
|
|
final int width;
|
|
|
|
final int height;
|
|
|
|
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
|
|
width = Math.max(dm.widthPixels, dm.heightPixels);
|
|
|
|
height = Math.min(dm.widthPixels, dm.heightPixels);
|
|
|
|
} else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
|
|
|
width = Math.min(dm.widthPixels, dm.heightPixels);
|
|
|
|
height = Math.max(dm.widthPixels, dm.heightPixels);
|
|
|
|
} else {
|
|
|
|
throw new RuntimeException("Orientation should be ORIENTATION_LANDSCAPE or "
|
|
|
|
+ "ORIENTATION_PORTRAIT: orientation=" + orientation);
|
|
|
|
}
|
|
|
|
return createKeyboardSet(context, subtype, width, height, testCasesHaveTouchCoordinates);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static KeyboardLayoutSet createKeyboardSet(final Context context,
|
|
|
|
final InputMethodSubtype subtype, final int width, final int height,
|
|
|
|
final boolean testCasesHaveTouchCoordinates) {
|
|
|
|
final EditorInfo editorInfo = new EditorInfo();
|
|
|
|
editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
|
|
|
|
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
|
|
|
|
context, editorInfo);
|
|
|
|
builder.setScreenGeometry(width, height);
|
|
|
|
builder.setSubtype(subtype);
|
|
|
|
if (!testCasesHaveTouchCoordinates) {
|
|
|
|
// For spell checker and tests
|
|
|
|
builder.disableTouchPositionCorrectionData();
|
|
|
|
}
|
|
|
|
return builder.build();
|
|
|
|
}
|
2011-12-12 07:11:37 +00:00
|
|
|
}
|