Remove some of the unused classes

This commit is contained in:
Aleksandras Kostarevas 2024-08-31 15:56:01 +03:00
parent c5d3fe9917
commit 340e298fe7
25 changed files with 1147 additions and 3138 deletions

View File

@ -73,27 +73,6 @@ public final class KeyboardId {
private final int mHashCode;
public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) {
mKeyboardLayoutSetName = params.mSubtype.getKeyboardLayoutSetName();
mLocale = params.mSubtype.getLocale();
mWidth = params.mKeyboardWidth;
mHeight = params.mKeyboardHeight;
mMode = params.mMode;
mElementId = elementId;
mEditorInfo = params.mEditorInfo;
mClobberSettingsKey = params.mNoSettingsKey;
mBottomEmojiKeyEnabled = params.mBottomEmojiKeyEnabled;
mBottomActionKeyId = params.mBottomActionKeyId;
mCustomActionLabel = (mEditorInfo.actionLabel != null)
? mEditorInfo.actionLabel.toString() : null;
mHasShortcutKey = params.mVoiceInputKeyEnabled;
mIsSplitLayout = params.mIsSplitLayoutEnabled;
mNumberRow = params.mNumberRow || params.mIsPasswordField;
mLongPressKeySettings = params.mLongPressKeySettings;
mHashCode = computeHashCode(this);
}
public KeyboardId(String mKeyboardLayoutSetName, Locale mLocale, int mWidth, int mHeight, int mMode, int mElementId, EditorInfo mEditorInfo, boolean mClobberSettingsKey, boolean mBottomEmojiKeyEnabled, int mBottomActionKeyId, String mCustomActionLabel, boolean mHasShortcutKey, boolean mIsSplitLayout, boolean mNumberRow, LongPressKeySettings mLongPressKeySettings) {
this.mKeyboardLayoutSetName = mKeyboardLayoutSetName;
this.mLocale = mLocale;

View File

@ -1,521 +0,0 @@
/*
* 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 org.futo.inputmethod.keyboard;
import static org.futo.inputmethod.latin.common.Constants.ImeOption.FORCE_ASCII;
import static org.futo.inputmethod.latin.common.Constants.ImeOption.NO_SETTINGS_KEY;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.text.InputType;
import android.util.Log;
import android.util.SparseArray;
import android.util.Xml;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.compat.EditorInfoCompatUtils;
import org.futo.inputmethod.compat.InputMethodSubtypeCompatUtils;
import org.futo.inputmethod.compat.UserManagerCompatUtils;
import org.futo.inputmethod.keyboard.internal.KeyboardBuilder;
import org.futo.inputmethod.keyboard.internal.KeyboardParams;
import org.futo.inputmethod.keyboard.internal.UniqueKeysCache;
import org.futo.inputmethod.latin.InputAttributes;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.RichInputMethodSubtype;
import org.futo.inputmethod.latin.settings.LongPressKeySettings;
import org.futo.inputmethod.latin.utils.InputTypeUtils;
import org.futo.inputmethod.latin.utils.ScriptUtils;
import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils;
import org.futo.inputmethod.latin.utils.XmlParseUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* This class represents a set of keyboard layouts. Each of them represents a different keyboard
* specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same
* {@link KeyboardLayoutSet} are related to each other.
* A {@link KeyboardLayoutSet} needs to be created for each
* {@link android.view.inputmethod.EditorInfo}.
*/
public final class KeyboardLayoutSet {
private static final String TAG = KeyboardLayoutSet.class.getSimpleName();
private static final boolean DEBUG_CACHE = false;
private static final String TAG_KEYBOARD_SET = "KeyboardLayoutSet";
private static final String TAG_ELEMENT = "Element";
private static final String TAG_FEATURE = "Feature";
private static final String KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX = "keyboard_layout_set_";
private final Context mContext;
@Nonnull
private final Params mParams;
// How many layouts we forcibly keep in cache. This only includes ALPHABET (default) and
// ALPHABET_AUTOMATIC_SHIFTED layouts - other layouts may stay in memory in the map of
// soft-references, but we forcibly cache this many alphabetic/auto-shifted layouts.
private static final int FORCIBLE_CACHE_SIZE = 4;
// By construction of soft references, anything that is also referenced somewhere else
// will stay in the cache. So we forcibly keep some references in an array to prevent
// them from disappearing from sKeyboardCache.
private static final Keyboard[] sForcibleKeyboardCache = new Keyboard[FORCIBLE_CACHE_SIZE];
private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache =
new HashMap<>();
@Nonnull
private static final UniqueKeysCache sUniqueKeysCache = UniqueKeysCache.newInstance();
private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes =
new HashMap<>();
@SuppressWarnings("serial")
public static final class KeyboardLayoutSetException extends RuntimeException {
public final KeyboardId mKeyboardId;
public KeyboardLayoutSetException(final Throwable cause, final KeyboardId keyboardId) {
super(cause);
mKeyboardId = keyboardId;
}
}
private static final class ElementParams {
int mKeyboardXmlId;
boolean mProximityCharsCorrectionEnabled;
boolean mSupportsSplitLayout;
boolean mAllowRedundantMoreKeys;
public ElementParams() {}
}
public static final class Params {
String mKeyboardLayoutSetName;
int mMode;
boolean mDisableTouchPositionCorrectionDataForTest;
// TODO: Use {@link InputAttributes} instead of these variables.
EditorInfo mEditorInfo;
boolean mIsPasswordField;
boolean mVoiceInputKeyEnabled;
boolean mNoSettingsKey;
boolean mBottomEmojiKeyEnabled;
int mBottomActionKeyId;
RichInputMethodSubtype mSubtype;
boolean mIsSpellChecker;
int mKeyboardWidth;
int mKeyboardHeight;
int mScriptId = ScriptUtils.SCRIPT_LATIN;
// Indicates if the user has enabled the split-layout preference
// and the required ProductionFlags are enabled.
boolean mIsSplitLayoutEnabledByUser;
// Indicates if split layout is actually enabled, taking into account
// whether the user has enabled it, and the keyboard layout supports it.
boolean mIsSplitLayoutEnabled;
// Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
new SparseArray<>();
boolean mNumberRow;
LongPressKeySettings mLongPressKeySettings;
}
public static void onSystemLocaleChanged() {
clearKeyboardCache();
}
public static void onKeyboardThemeChanged() {
clearKeyboardCache();
}
private static void clearKeyboardCache() {
sKeyboardCache.clear();
sUniqueKeysCache.clear();
}
public static int getScriptId(final Resources resources,
@Nonnull final InputMethodSubtype subtype) {
final Integer value = sScriptIdsForSubtypes.get(subtype);
if (null == value) {
final int scriptId = Builder.readScriptId(resources, subtype);
sScriptIdsForSubtypes.put(subtype, scriptId);
return scriptId;
}
return value;
}
KeyboardLayoutSet(final Context context, @Nonnull final Params params) {
mContext = context;
mParams = params;
}
@Nonnull
public Keyboard getKeyboard(final int baseKeyboardLayoutSetElementId) {
final int keyboardLayoutSetElementId;
switch (mParams.mMode) {
case KeyboardId.MODE_PHONE:
if (baseKeyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS) {
keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS;
} else {
keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE;
}
break;
case KeyboardId.MODE_NUMBER:
case KeyboardId.MODE_DATE:
case KeyboardId.MODE_TIME:
case KeyboardId.MODE_DATETIME:
keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER;
break;
default:
keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId;
break;
}
ElementParams elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get(
keyboardLayoutSetElementId);
if (elementParams == null) {
elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get(
KeyboardId.ELEMENT_ALPHABET);
}
// 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.
mParams.mIsSplitLayoutEnabled = mParams.mIsSplitLayoutEnabledByUser
&& elementParams.mSupportsSplitLayout;
final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams);
try {
return getKeyboard(elementParams, id);
} catch (final RuntimeException e) {
Log.e(TAG, "Can't create keyboard: " + id, e);
throw new KeyboardLayoutSetException(e, id);
}
}
@Nonnull
private Keyboard getKeyboard(final ElementParams elementParams, final KeyboardId id) {
final SoftReference<Keyboard> ref = sKeyboardCache.get(id);
final Keyboard cachedKeyboard = (ref == null) ? null : ref.get();
if (cachedKeyboard != null) {
if (DEBUG_CACHE) {
Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": HIT id=" + id);
}
return cachedKeyboard;
}
final KeyboardBuilder<KeyboardParams> builder =
new KeyboardBuilder<>(mContext, new KeyboardParams(sUniqueKeysCache));
sUniqueKeysCache.setEnabled(id.isAlphabetKeyboard());
builder.setAllowRedundantMoreKes(elementParams.mAllowRedundantMoreKeys);
final int keyboardXmlId = elementParams.mKeyboardXmlId;
builder.load(keyboardXmlId, id);
if (mParams.mDisableTouchPositionCorrectionDataForTest) {
builder.disableTouchPositionCorrectionDataForTest();
}
builder.setProximityCharsCorrectionEnabled(elementParams.mProximityCharsCorrectionEnabled);
final Keyboard keyboard = builder.build();
sKeyboardCache.put(id, new SoftReference<>(keyboard));
if ((id.mElementId == KeyboardId.ELEMENT_ALPHABET
|| id.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)
&& !mParams.mIsSpellChecker) {
// We only forcibly cache the primary, "ALPHABET", layouts.
for (int i = sForcibleKeyboardCache.length - 1; i >= 1; --i) {
sForcibleKeyboardCache[i] = sForcibleKeyboardCache[i - 1];
}
sForcibleKeyboardCache[0] = keyboard;
if (DEBUG_CACHE) {
Log.d(TAG, "forcing caching of keyboard with id=" + id);
}
}
if (DEBUG_CACHE) {
Log.d(TAG, "keyboard cache size=" + sKeyboardCache.size() + ": "
+ ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
}
return keyboard;
}
public int getScriptId() {
return mParams.mScriptId;
}
public static final class Builder {
private final Context mContext;
private final String mPackageName;
private final Resources mResources;
private final Params mParams = new Params();
private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
public Builder(final Context context, @Nullable final EditorInfo ei) {
mContext = context;
mPackageName = context.getPackageName();
mResources = context.getResources();
final Params params = mParams;
params.mLongPressKeySettings = new LongPressKeySettings(context);
final EditorInfo editorInfo = (ei != null) ? ei : EMPTY_EDITOR_INFO;
params.mMode = getKeyboardMode(editorInfo);
// TODO: Consolidate those with {@link InputAttributes}.
params.mEditorInfo = editorInfo;
params.mIsPasswordField = InputTypeUtils.isPasswordInputType(editorInfo.inputType);
params.mNoSettingsKey = InputAttributes.inPrivateImeOptions(
mPackageName, NO_SETTINGS_KEY, editorInfo);
// When the device is still unlocked, features like showing the IME setting app need to
// be locked down.
// TODO: Switch to {@code UserManagerCompat.isUserUnlocked()} in the support-v4 library
// when it becomes publicly available.
@UserManagerCompatUtils.LockState
final int lockState = UserManagerCompatUtils.getUserLockState(context);
if (lockState == UserManagerCompatUtils.LOCK_STATE_LOCKED) {
params.mNoSettingsKey = true;
}
}
public Builder setKeyboardGeometry(final int keyboardWidth, final int keyboardHeight) {
mParams.mKeyboardWidth = keyboardWidth;
mParams.mKeyboardHeight = keyboardHeight;
return this;
}
public Builder setSubtype(@Nonnull final RichInputMethodSubtype subtype) {
final boolean asciiCapable = InputMethodSubtypeCompatUtils.isAsciiCapable(subtype);
// TODO: Consolidate with {@link InputAttributes}.
@SuppressWarnings("deprecation")
final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
mPackageName, FORCE_ASCII, mParams.mEditorInfo);
final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii(
mParams.mEditorInfo.imeOptions)
|| deprecatedForceAscii;
final RichInputMethodSubtype keyboardSubtype = (forceAscii && !asciiCapable)
? RichInputMethodSubtype.getNoLanguageSubtype()
: subtype;
mParams.mSubtype = keyboardSubtype;
mParams.mKeyboardLayoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
+ keyboardSubtype.getKeyboardLayoutSetName();
return this;
}
public Builder setIsSpellChecker(final boolean isSpellChecker) {
mParams.mIsSpellChecker = isSpellChecker;
return this;
}
public Builder setVoiceInputKeyEnabled(final boolean enabled) {
mParams.mVoiceInputKeyEnabled = enabled;
return this;
}
public Builder setBottomActionKey(final boolean enabled, final int action) {
mParams.mBottomEmojiKeyEnabled = enabled;
mParams.mBottomActionKeyId = action;
return this;
}
public Builder disableTouchPositionCorrectionData() {
mParams.mDisableTouchPositionCorrectionDataForTest = true;
return this;
}
public Builder setSplitLayoutEnabledByUser(final boolean enabled) {
mParams.mIsSplitLayoutEnabledByUser = enabled;
return this;
}
public Builder setNumberRow(final boolean enabled) {
mParams.mNumberRow = enabled;
return this;
}
// Super redux version of reading the script ID for some subtype from Xml.
static int readScriptId(final Resources resources, final InputMethodSubtype subtype) {
final String layoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
+ SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
final int xmlId = getXmlId(resources, layoutSetName);
final XmlResourceParser parser = resources.getXml(xmlId);
try {
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
// Bovinate through the XML stupidly searching for TAG_FEATURE, and read
// the script Id from it.
parser.next();
final String tag = parser.getName();
if (TAG_FEATURE.equals(tag)) {
return readScriptIdFromTagFeature(resources, parser);
}
}
} catch (final IOException | XmlPullParserException e) {
throw new RuntimeException(e.getMessage() + " in " + layoutSetName, e);
} finally {
parser.close();
}
// If the tag is not found, then the default script is Latin.
return ScriptUtils.SCRIPT_LATIN;
}
private static int readScriptIdFromTagFeature(final Resources resources,
final XmlPullParser parser) throws IOException, XmlPullParserException {
final TypedArray featureAttr = resources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.KeyboardLayoutSet_Feature);
try {
final int scriptId =
featureAttr.getInt(R.styleable.KeyboardLayoutSet_Feature_supportedScript,
ScriptUtils.SCRIPT_UNKNOWN);
XmlParseUtils.checkEndTag(TAG_FEATURE, parser);
return scriptId;
} finally {
featureAttr.recycle();
}
}
public KeyboardLayoutSet build() {
if (mParams.mSubtype == null)
throw new RuntimeException("KeyboardLayoutSet subtype is not specified");
final int xmlId = getXmlId(mResources, mParams.mKeyboardLayoutSetName);
try {
parseKeyboardLayoutSet(mResources, xmlId);
} catch (final IOException | XmlPullParserException e) {
throw new RuntimeException(e.getMessage() + " in " + mParams.mKeyboardLayoutSetName,
e);
}
return new KeyboardLayoutSet(mContext, mParams);
}
private static int getXmlId(final Resources resources, final String keyboardLayoutSetName) {
final String packageName = resources.getResourcePackageName(
R.xml.keyboard_layout_set_qwerty);
return resources.getIdentifier(keyboardLayoutSetName, "xml", packageName);
}
private void parseKeyboardLayoutSet(final Resources res, final int resId)
throws XmlPullParserException, IOException {
final XmlResourceParser parser = res.getXml(resId);
try {
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
final int event = parser.next();
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_KEYBOARD_SET.equals(tag)) {
parseKeyboardLayoutSetContent(parser);
} else {
throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
}
}
}
} finally {
parser.close();
}
}
private void parseKeyboardLayoutSetContent(final XmlPullParser parser)
throws XmlPullParserException, IOException {
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
final int event = parser.next();
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_ELEMENT.equals(tag)) {
parseKeyboardLayoutSetElement(parser);
} else if (TAG_FEATURE.equals(tag)) {
mParams.mScriptId = readScriptIdFromTagFeature(mResources, parser);
} else {
throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
}
} else if (event == XmlPullParser.END_TAG) {
final String tag = parser.getName();
if (TAG_KEYBOARD_SET.equals(tag)) {
break;
}
throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_KEYBOARD_SET);
}
}
}
private void parseKeyboardLayoutSetElement(final XmlPullParser parser)
throws XmlPullParserException, IOException {
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.KeyboardLayoutSet_Element);
try {
XmlParseUtils.checkAttributeExists(a,
R.styleable.KeyboardLayoutSet_Element_elementName, "elementName",
TAG_ELEMENT, parser);
XmlParseUtils.checkAttributeExists(a,
R.styleable.KeyboardLayoutSet_Element_elementKeyboard, "elementKeyboard",
TAG_ELEMENT, parser);
XmlParseUtils.checkEndTag(TAG_ELEMENT, parser);
final ElementParams elementParams = new ElementParams();
final int elementName = a.getInt(
R.styleable.KeyboardLayoutSet_Element_elementName, 0);
elementParams.mKeyboardXmlId = a.getResourceId(
R.styleable.KeyboardLayoutSet_Element_elementKeyboard, 0);
elementParams.mProximityCharsCorrectionEnabled = a.getBoolean(
R.styleable.KeyboardLayoutSet_Element_enableProximityCharsCorrection,
false);
elementParams.mSupportsSplitLayout = a.getBoolean(
R.styleable.KeyboardLayoutSet_Element_supportsSplitLayout, false);
elementParams.mAllowRedundantMoreKeys = a.getBoolean(
R.styleable.KeyboardLayoutSet_Element_allowRedundantMoreKeys, false);
mParams.mKeyboardLayoutSetElementIdToParamsMap.put(elementName, elementParams);
} finally {
a.recycle();
}
}
private static int getKeyboardMode(final EditorInfo editorInfo) {
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:
if (InputTypeUtils.isEmailVariation(variation)) {
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;
}
}
}
}

View File

@ -112,7 +112,7 @@ public final class KeyboardSwitcher implements SwitchActions {
|| !mThemeContext.getResources().equals(context.getResources())) {
mKeyboardTheme = keyboardTheme;
mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
KeyboardLayoutSet.onKeyboardThemeChanged();
KeyboardLayoutSetV2.onKeyboardThemeChanged();
themeSwitchPending = false;
return true;
}

View File

@ -30,14 +30,11 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.View;
import org.futo.inputmethod.keyboard.internal.KeyDrawParams;
import org.futo.inputmethod.keyboard.internal.KeyVisualAttributes;
import org.futo.inputmethod.latin.uix.DynamicThemeProvider;
import org.futo.inputmethod.latin.uix.DynamicThemeProviderOwner;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.common.Constants;
import org.futo.inputmethod.latin.utils.TypefaceUtils;
@ -138,16 +135,7 @@ public class KeyboardView extends View {
final TypedArray keyAttr = context.obtainStyledAttributes(attrs,
R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView);
assert(context instanceof ContextThemeWrapper);
if(((ContextThemeWrapper) context).getBaseContext() instanceof DynamicThemeProviderOwner) {
mDrawableProvider = ((DynamicThemeProviderOwner) ((ContextThemeWrapper) context).getBaseContext()).getDrawableProvider();
} else if(context instanceof DynamicThemeProviderOwner) {
mDrawableProvider = ((DynamicThemeProviderOwner) context).getDrawableProvider();
} else {
throw new IllegalStateException("Failed to obtain DynamicThemeProvider");
}
mDrawableProvider = DynamicThemeProvider.obtainFromContext(context);
boolean isMoreKeys = keyAttr.getBoolean(R.styleable.Keyboard_Key_isMoreKey, false);
boolean isMoreKeysAction = keyAttr.getBoolean(R.styleable.Keyboard_Key_isAction, false);

View File

@ -17,14 +17,15 @@
package org.futo.inputmethod.keyboard;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Paint;
import org.futo.inputmethod.annotations.UsedForTesting;
import org.futo.inputmethod.keyboard.internal.KeyboardBuilder;
import org.futo.inputmethod.keyboard.internal.KeyboardParams;
import org.futo.inputmethod.keyboard.internal.MoreKeySpec;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.common.StringUtils;
import org.futo.inputmethod.latin.uix.DynamicThemeProvider;
import org.futo.inputmethod.latin.utils.TypefaceUtils;
import java.util.List;
@ -258,12 +259,14 @@ public final class MoreKeysKeyboard extends Keyboard {
}
}
public static class Builder extends KeyboardBuilder<MoreKeysKeyboardParams> {
public static class Builder {
private final Key mParentKey;
private static final float LABEL_PADDING_RATIO = 0.2f;
private static final float DIVIDER_RATIO = 0.2f;
private MoreKeysKeyboardParams mParams = new MoreKeysKeyboardParams();
/**
* The builder of MoreKeysKeyboard.
* @param context the context of {@link MoreKeysKeyboardView}.
@ -278,8 +281,17 @@ public final class MoreKeysKeyboard extends Keyboard {
public Builder(final Context context, final Key key, final Keyboard keyboard,
final boolean isSingleMoreKeyWithPreview, final int keyPreviewVisibleWidth,
final int keyPreviewVisibleHeight, final Paint paintToMeasure) {
super(context, new MoreKeysKeyboardParams());
load(keyboard.mMoreKeysTemplate, keyboard.mId);
final Resources res = context.getResources();
mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
DynamicThemeProvider provider = DynamicThemeProvider.obtainFromContext(context);
mParams.mIconsSet.loadIcons(null, provider);
mParams.mThemeId = 3;
mParams.mTextsSet.setLocale(keyboard.mId.mLocale, context);
mParams.mProximityCharsCorrectionEnabled = false;
// TODO: More keys keyboard's vertical gap is currently calculated heuristically.
// Should revise the algorithm.
@ -329,7 +341,6 @@ public final class MoreKeysKeyboard extends Keyboard {
return maxWidth;
}
@Override
@Nonnull
public MoreKeysKeyboard build() {
final MoreKeysKeyboardParams params = mParams;

View File

@ -1,239 +0,0 @@
/*
* Copyright (C) 2010 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 org.futo.inputmethod.keyboard.internal;
import android.content.res.TypedArray;
import android.util.Log;
import android.util.SparseArray;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.utils.XmlParseUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class KeyStylesSet {
private static final String TAG = KeyStylesSet.class.getSimpleName();
private static final boolean DEBUG = false;
@Nonnull
private final HashMap<String, KeyStyle> mStyles = new HashMap<>();
@Nonnull
private final KeyboardTextsSet mTextsSet;
@Nonnull
private final KeyStyle mEmptyKeyStyle;
@Nonnull
private static final String EMPTY_STYLE_NAME = "<empty>";
public KeyStylesSet(@Nonnull final KeyboardTextsSet textsSet) {
mTextsSet = textsSet;
mEmptyKeyStyle = new EmptyKeyStyle(textsSet);
mStyles.put(EMPTY_STYLE_NAME, mEmptyKeyStyle);
}
private static final class EmptyKeyStyle extends KeyStyle {
EmptyKeyStyle(@Nonnull final KeyboardTextsSet textsSet) {
super(textsSet);
}
@Override
@Nullable
public String[] getStringArray(final TypedArray a, final int index, Function<String, String> stringMutator) {
return parseStringArray(a, index, stringMutator);
}
@Override
@Nullable
public String getString(final TypedArray a, final int index, Function<String, String> stringMutator) {
return parseString(a, index, stringMutator);
}
@Override
public int getInt(final TypedArray a, final int index, final int defaultValue) {
return a.getInt(index, defaultValue);
}
@Override
public int getFlags(final TypedArray a, final int index) {
return a.getInt(index, 0);
}
}
private static final class DeclaredKeyStyle extends KeyStyle {
private final HashMap<String, KeyStyle> mStyles;
private final String mParentStyleName;
private final SparseArray<Object> mStyleAttributes = new SparseArray<>();
public DeclaredKeyStyle(@Nonnull final String parentStyleName,
@Nonnull final KeyboardTextsSet textsSet,
@Nonnull final HashMap<String, KeyStyle> styles) {
super(textsSet);
mParentStyleName = parentStyleName;
mStyles = styles;
}
@Override
@Nullable
public String[] getStringArray(final TypedArray a, final int index, Function<String, String> stringMutator) {
if (a.hasValue(index)) {
return parseStringArray(a, index, stringMutator);
}
final Object value = mStyleAttributes.get(index);
if (value != null) {
final String[] array = (String[])value;
return Arrays.copyOf(array, array.length);
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getStringArray(a, index, stringMutator);
}
@Override
@Nullable
public String getString(final TypedArray a, final int index, Function<String, String> stringMutator) {
if (a.hasValue(index)) {
return parseString(a, index, stringMutator);
}
final Object value = mStyleAttributes.get(index);
if (value != null) {
return (String)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getString(a, index, stringMutator);
}
@Override
public int getInt(final TypedArray a, final int index, final int defaultValue) {
if (a.hasValue(index)) {
return a.getInt(index, defaultValue);
}
final Object value = mStyleAttributes.get(index);
if (value != null) {
return (Integer)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getInt(a, index, defaultValue);
}
@Override
public int getFlags(final TypedArray a, final int index) {
final int parentFlags = mStyles.get(mParentStyleName).getFlags(a, index);
final Integer value = (Integer)mStyleAttributes.get(index);
final int styleFlags = (value != null) ? value : 0;
final int flags = a.getInt(index, 0);
return flags | styleFlags | parentFlags;
}
public void readKeyAttributes(final TypedArray keyAttr) {
// TODO: Currently not all Key attributes can be declared as style.
readString(keyAttr, R.styleable.Keyboard_Key_altCode);
readString(keyAttr, R.styleable.Keyboard_Key_keySpec);
readString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
readStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
readStringArray(keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys);
readFlags(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags);
readInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn);
readInt(keyAttr, R.styleable.Keyboard_Key_backgroundType);
readFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
}
private void readString(final TypedArray a, final int index) {
if (a.hasValue(index)) {
mStyleAttributes.put(index, parseString(a, index, null));
}
}
private void readInt(final TypedArray a, final int index) {
if (a.hasValue(index)) {
mStyleAttributes.put(index, a.getInt(index, 0));
}
}
private void readFlags(final TypedArray a, final int index) {
if (a.hasValue(index)) {
final Integer value = (Integer)mStyleAttributes.get(index);
final int styleFlags = value != null ? value : 0;
mStyleAttributes.put(index, a.getInt(index, 0) | styleFlags);
}
}
private void readStringArray(final TypedArray a, final int index) {
if (a.hasValue(index)) {
mStyleAttributes.put(index, parseStringArray(a, index, null));
}
}
}
public void parseKeyStyleAttributes(final TypedArray keyStyleAttr, final TypedArray keyAttrs,
final XmlPullParser parser) throws XmlPullParserException {
final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
if (styleName == null) {
throw new XmlParseUtils.ParseException(
KeyboardBuilder.TAG_KEY_STYLE + " has no styleName attribute", parser);
}
if (DEBUG) {
Log.d(TAG, String.format("<%s styleName=%s />",
KeyboardBuilder.TAG_KEY_STYLE, styleName));
if (mStyles.containsKey(styleName)) {
Log.d(TAG, KeyboardBuilder.TAG_KEY_STYLE + " " + styleName + " is overridden at "
+ parser.getPositionDescription());
}
}
final String parentStyleInAttr = keyStyleAttr.getString(
R.styleable.Keyboard_KeyStyle_parentStyle);
if (parentStyleInAttr != null && !mStyles.containsKey(parentStyleInAttr)) {
throw new XmlParseUtils.ParseException(
"Unknown parentStyle " + parentStyleInAttr, parser);
}
final String parentStyleName = (parentStyleInAttr == null) ? EMPTY_STYLE_NAME
: parentStyleInAttr;
final DeclaredKeyStyle style = new DeclaredKeyStyle(parentStyleName, mTextsSet, mStyles);
style.readKeyAttributes(keyAttrs);
mStyles.put(styleName, style);
}
@Nonnull
public KeyStyle getKeyStyle(final TypedArray keyAttr, final XmlPullParser parser)
throws XmlParseUtils.ParseException {
final String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle);
if (styleName == null) {
return mEmptyKeyStyle;
}
final KeyStyle style = mStyles.get(styleName);
if (style == null) {
throw new XmlParseUtils.ParseException("Unknown key style: " + styleName, parser);
}
return style;
}
public void addDynamicKeyStyle(final String styleName, final String keySpec, final int backgroundType, final int keyActionFlags) {
final DeclaredKeyStyle style = new DeclaredKeyStyle(EMPTY_STYLE_NAME, mTextsSet, mStyles);
style.mStyleAttributes.put(R.styleable.Keyboard_Key_keySpec, mTextsSet.resolveTextReference(keySpec));
style.mStyleAttributes.put(R.styleable.Keyboard_Key_backgroundType, (backgroundType));
style.mStyleAttributes.put(R.styleable.Keyboard_Key_keyActionFlags, (keyActionFlags));
mStyles.put(styleName, style);
}
}

View File

@ -1,924 +0,0 @@
/*
* Copyright (C) 2012 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 org.futo.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.util.Xml;
import android.view.ContextThemeWrapper;
import org.futo.inputmethod.annotations.UsedForTesting;
import org.futo.inputmethod.keyboard.Key;
import org.futo.inputmethod.keyboard.Keyboard;
import org.futo.inputmethod.keyboard.KeyboardId;
import org.futo.inputmethod.keyboard.KeyboardTheme;
import org.futo.inputmethod.latin.uix.DynamicThemeProvider;
import org.futo.inputmethod.latin.uix.DynamicThemeProviderOwner;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.common.Constants;
import org.futo.inputmethod.latin.common.StringUtils;
import org.futo.inputmethod.latin.utils.ResourceUtils;
import org.futo.inputmethod.latin.utils.XmlParseUtils;
import org.futo.inputmethod.latin.utils.XmlParseUtils.ParseException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.Locale;
import javax.annotation.Nonnull;
/**
* Keyboard Building helper.
*
* This class parses Keyboard XML file and eventually build a Keyboard.
* The Keyboard XML file looks like:
* <pre>
* &lt;!-- xml/keyboard.xml --&gt;
* &lt;Keyboard keyboard_attributes*&gt;
* &lt;!-- Keyboard Content --&gt;
* &lt;Row row_attributes*&gt;
* &lt;!-- Row Content --&gt;
* &lt;Key key_attributes* /&gt;
* &lt;Spacer horizontalGap="32.0dp" /&gt;
* &lt;include keyboardLayout="@xml/other_keys"&gt;
* ...
* &lt;/Row&gt;
* &lt;include keyboardLayout="@xml/other_rows"&gt;
* ...
* &lt;/Keyboard&gt;
* </pre>
* The XML file which is included in other file must have &lt;merge&gt; as root element,
* such as:
* <pre>
* &lt;!-- xml/other_keys.xml --&gt;
* &lt;merge&gt;
* &lt;Key key_attributes* /&gt;
* ...
* &lt;/merge&gt;
* </pre>
* and
* <pre>
* &lt;!-- xml/other_rows.xml --&gt;
* &lt;merge&gt;
* &lt;Row row_attributes*&gt;
* &lt;Key key_attributes* /&gt;
* &lt;/Row&gt;
* ...
* &lt;/merge&gt;
* </pre>
* You can also use switch-case-default tags to select Rows and Keys.
* <pre>
* &lt;switch&gt;
* &lt;case case_attribute*&gt;
* &lt;!-- Any valid tags at switch position --&gt;
* &lt;/case&gt;
* ...
* &lt;default&gt;
* &lt;!-- Any valid tags at switch position --&gt;
* &lt;/default&gt;
* &lt;/switch&gt;
* </pre>
* You can declare Key style and specify styles within Key tags.
* <pre>
* &lt;switch&gt;
* &lt;case mode="email"&gt;
* &lt;key-style styleName="f1-key" parentStyle="modifier-key"
* keyLabel=".com"
* /&gt;
* &lt;/case&gt;
* &lt;case mode="url"&gt;
* &lt;key-style styleName="f1-key" parentStyle="modifier-key"
* keyLabel="http://"
* /&gt;
* &lt;/case&gt;
* &lt;/switch&gt;
* ...
* &lt;Key keyStyle="shift-key" ... /&gt;
* </pre>
*/
// TODO: Remove this class, XML is no longer used for building keyboards
public class KeyboardBuilder<KP extends KeyboardParams> {
private static final String BUILDER_TAG = "Keyboard.Builder";
private static final boolean DEBUG = false;
// Keyboard XML Tags
private static final String TAG_KEYBOARD = "Keyboard";
private static final String TAG_ROW = "Row";
private static final String TAG_GRID_ROWS = "GridRows";
private static final String TAG_KEY = "Key";
private static final String TAG_SPACER = "Spacer";
private static final String TAG_INCLUDE = "include";
private static final String TAG_MERGE = "merge";
private static final String TAG_SWITCH = "switch";
private static final String TAG_CASE = "case";
private static final String TAG_DEFAULT = "default";
public static final String TAG_KEY_STYLE = "key-style";
private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
private static final int DEFAULT_KEYBOARD_ROWS = 4;
@Nonnull
protected final KP mParams;
protected final Context mContext;
protected final Resources mResources;
private int mCurrentY = 0;
private KeyboardRow mCurrentRow = null;
private boolean mLeftEdge;
private boolean mTopEdge;
private Key mRightEdgeKey = null;
private DynamicThemeProvider mProvider = null;
public KeyboardBuilder(final Context context, @Nonnull final KP params) {
mContext = context;
if(mContext instanceof DynamicThemeProviderOwner) {
mProvider = ((DynamicThemeProviderOwner) mContext).getDrawableProvider();
}else if(mContext instanceof ContextThemeWrapper) {
Context baseContext = ((ContextThemeWrapper) mContext).getBaseContext();
if(baseContext instanceof DynamicThemeProviderOwner) {
mProvider = ((DynamicThemeProviderOwner) baseContext).getDrawableProvider();
}
}
final Resources res = context.getResources();
mResources = res;
mParams = params;
params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
}
public void setAllowRedundantMoreKes(final boolean enabled) {
mParams.mAllowRedundantMoreKeys = enabled;
}
public KeyboardBuilder<KP> load(final int xmlId, final KeyboardId id) {
mParams.mId = id;
final String actionKeySpec = "!icon/action_" + id.mBottomActionKeyId + "|!code/action_" + id.mBottomActionKeyId;
mParams.mKeyStyles.addDynamicKeyStyle("bottomEmojiKeyStyle",
actionKeySpec,
2,
0x02 | 0x08);
final XmlResourceParser parser = mResources.getXml(xmlId);
try {
parseKeyboard(parser);
} catch (XmlPullParserException e) {
Log.w(BUILDER_TAG, "keyboard XML parse error", e);
throw new IllegalArgumentException(e.getMessage(), e);
} catch (IOException e) {
Log.w(BUILDER_TAG, "keyboard XML parse error", e);
throw new RuntimeException(e.getMessage(), e);
} finally {
parser.close();
}
return this;
}
@UsedForTesting
public void disableTouchPositionCorrectionDataForTest() {
mParams.mTouchPositionCorrection.setEnabled(false);
}
public void setProximityCharsCorrectionEnabled(final boolean enabled) {
mParams.mProximityCharsCorrectionEnabled = enabled;
}
@Nonnull
public Keyboard build() {
return new Keyboard(mParams);
}
private int mIndent;
private static final String SPACES = " ";
private static String spaces(final int count) {
return (count < SPACES.length()) ? SPACES.substring(0, count) : SPACES;
}
private void startTag(final String format, final Object ... args) {
Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
}
private void endTag(final String format, final Object ... args) {
Log.d(BUILDER_TAG, String.format(spaces(mIndent-- * 2) + format, args));
}
private void startEndTag(final String format, final Object ... args) {
Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
mIndent--;
}
private void parseKeyboard(final XmlPullParser parser)
throws XmlPullParserException, IOException {
if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
final int event = parser.next();
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_KEYBOARD.equals(tag)) {
parseKeyboardAttributes(parser);
startKeyboard();
parseKeyboardContent(parser, false);
return;
}
throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD);
}
}
}
private void parseKeyboardAttributes(final XmlPullParser parser) {
final AttributeSet attr = Xml.asAttributeSet(parser);
final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
attr, R.styleable.Keyboard, R.attr.keyboardStyle, R.style.Keyboard);
final TypedArray keyAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key);
try {
final KeyboardParams params = mParams;
int extraSidePadding = 0;
int extraBottomPadding = 0;
float heightMultiplier = 1.0f;
float gapMultiplier = 1.0f;
if(params.mIsMainKeyboard) {
extraSidePadding = mResources.getDimensionPixelSize(R.dimen.keyboardSidePaddingAddl);
extraBottomPadding = mResources.getDimensionPixelSize(R.dimen.keyboardBottomPaddingAddl);
heightMultiplier = mProvider.getKeyboardHeightMultiplier() * mResources.getFraction(R.fraction.keyboardHeightMultiplierAddl, 1, 1);
gapMultiplier = mResources.getFraction(R.fraction.keyboardGapMultiplierAddl, 1, 1);
}
final int height = (int) (params.mId.mHeight * heightMultiplier);
final int width = params.mId.mWidth;
params.mOccupiedHeight = height;
params.mOccupiedWidth = width;
params.mTopPadding = (int)keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardTopPadding, height, height, 0);
params.mBottomPadding = (int)(keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardBottomPadding, height, height, 0)
) + extraBottomPadding;
params.mLeftPadding = (int)keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardLeftPadding, width, width, 0) + extraSidePadding;
params.mRightPadding = (int)keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardRightPadding, width, width, 0) + extraSidePadding;
final int baseWidth =
params.mOccupiedWidth - params.mLeftPadding - params.mRightPadding;
params.mBaseWidth = baseWidth;
params.mDefaultKeyWidth = (int)keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
baseWidth, baseWidth, baseWidth / DEFAULT_KEYBOARD_COLUMNS);
params.mHorizontalGap = (int)(keyboardAttr.getFraction(
R.styleable.Keyboard_horizontalGap, baseWidth, baseWidth, 0) * gapMultiplier);
// TODO: Fix keyboard geometry calculation clearer. Historically vertical gap between
// rows are determined based on the entire keyboard height including top and bottom
// paddings.
params.mVerticalGap = (int)(keyboardAttr.getFraction(
R.styleable.Keyboard_verticalGap, height, height, 0) * gapMultiplier);
final int baseHeight = params.mOccupiedHeight - params.mTopPadding
- params.mBottomPadding + params.mVerticalGap;
params.mBaseHeight = baseHeight;
params.mDefaultRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_rowHeight, baseHeight, baseHeight / DEFAULT_KEYBOARD_ROWS);
params.mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr, mProvider);
params.mMoreKeysTemplate = keyboardAttr.getResourceId(
R.styleable.Keyboard_moreKeysTemplate, 0);
params.mMaxMoreKeysKeyboardColumn = keyAttr.getInt(
R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
params.mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0);
params.mIconsSet.loadIcons(keyboardAttr, mProvider);
params.mTextsSet.setLocale(params.mId.getLocale(), mContext);
final int resourceId = keyboardAttr.getResourceId(
R.styleable.Keyboard_touchPositionCorrectionData, 0);
if (resourceId != 0) {
final String[] data = mResources.getStringArray(resourceId);
params.mTouchPositionCorrection.load(data);
}
} finally {
keyAttr.recycle();
keyboardAttr.recycle();
}
}
private void parseKeyboardContent(final XmlPullParser parser, final boolean skip)
throws XmlPullParserException, IOException {
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
final int event = parser.next();
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_ROW.equals(tag)) {
final KeyboardRow row = parseRowAttributes(parser);
if (DEBUG) startTag("<%s>%s", TAG_ROW, skip ? " skipped" : "");
if (!skip) {
startRow(row);
}
parseRowContent(parser, row, skip);
} else if (TAG_GRID_ROWS.equals(tag)) {
if (DEBUG) startTag("<%s>%s", TAG_GRID_ROWS, skip ? " skipped" : "");
parseGridRows(parser, skip);
} else if (TAG_INCLUDE.equals(tag)) {
parseIncludeKeyboardContent(parser, skip);
} else if (TAG_SWITCH.equals(tag)) {
parseSwitchKeyboardContent(parser, skip);
} else if (TAG_KEY_STYLE.equals(tag)) {
parseKeyStyle(parser, skip);
} else {
throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_ROW);
}
} else if (event == XmlPullParser.END_TAG) {
final String tag = parser.getName();
if (DEBUG) endTag("</%s>", tag);
if (TAG_KEYBOARD.equals(tag)) {
endKeyboard();
return;
}
if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) || TAG_MERGE.equals(tag)) {
return;
}
throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
}
}
}
private KeyboardRow parseRowAttributes(final XmlPullParser parser)
throws XmlPullParserException {
final AttributeSet attr = Xml.asAttributeSet(parser);
final TypedArray keyboardAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard);
try {
if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap)) {
throw new XmlParseUtils.IllegalAttribute(parser, TAG_ROW, "horizontalGap");
}
if (keyboardAttr.hasValue(R.styleable.Keyboard_verticalGap)) {
throw new XmlParseUtils.IllegalAttribute(parser, TAG_ROW, "verticalGap");
}
return new KeyboardRow(mResources, mParams, parser, mCurrentY);
} finally {
keyboardAttr.recycle();
}
}
private void parseRowContent(final XmlPullParser parser, final KeyboardRow row,
final boolean skip) throws XmlPullParserException, IOException {
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
final int event = parser.next();
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_KEY.equals(tag)) {
parseKey(parser, row, skip);
} else if (TAG_SPACER.equals(tag)) {
parseSpacer(parser, row, skip);
} else if (TAG_INCLUDE.equals(tag)) {
parseIncludeRowContent(parser, row, skip);
} else if (TAG_SWITCH.equals(tag)) {
parseSwitchRowContent(parser, row, skip);
} else if (TAG_KEY_STYLE.equals(tag)) {
parseKeyStyle(parser, skip);
} else {
throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_ROW);
}
} else if (event == XmlPullParser.END_TAG) {
final String tag = parser.getName();
if (DEBUG) endTag("</%s>", tag);
if (TAG_ROW.equals(tag)) {
if (!skip) {
endRow(row);
}
return;
}
if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) || TAG_MERGE.equals(tag)) {
return;
}
throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
}
}
}
private void parseGridRows(final XmlPullParser parser, final boolean skip)
throws XmlPullParserException, IOException {
if (skip) {
XmlParseUtils.checkEndTag(TAG_GRID_ROWS, parser);
if (DEBUG) {
startEndTag("<%s /> skipped", TAG_GRID_ROWS);
}
return;
}
final KeyboardRow gridRows = new KeyboardRow(mResources, mParams, parser, mCurrentY);
final TypedArray gridRowAttr = mResources.obtainAttributes(
Xml.asAttributeSet(parser), R.styleable.Keyboard_GridRows);
final int codesArrayId = gridRowAttr.getResourceId(
R.styleable.Keyboard_GridRows_codesArray, 0);
final int textsArrayId = gridRowAttr.getResourceId(
R.styleable.Keyboard_GridRows_textsArray, 0);
gridRowAttr.recycle();
if (codesArrayId == 0 && textsArrayId == 0) {
throw new XmlParseUtils.ParseException(
"Missing codesArray or textsArray attributes", parser);
}
if (codesArrayId != 0 && textsArrayId != 0) {
throw new XmlParseUtils.ParseException(
"Both codesArray and textsArray attributes specifed", parser);
}
final String[] array = mResources.getStringArray(
codesArrayId != 0 ? codesArrayId : textsArrayId);
final int counts = array.length;
final float keyWidth = gridRows.getKeyWidth(null, 0.0f);
final int numColumns = (int)(mParams.mOccupiedWidth / keyWidth);
for (int index = 0; index < counts; index += numColumns) {
final KeyboardRow row = new KeyboardRow(mResources, mParams, parser, mCurrentY);
startRow(row);
for (int c = 0; c < numColumns; c++) {
final int i = index + c;
if (i >= counts) {
break;
}
final String label;
final int code;
final String outputText;
final int supportedMinSdkVersion;
if (codesArrayId != 0) {
final String codeArraySpec = array[i];
label = CodesArrayParser.parseLabel(codeArraySpec);
code = CodesArrayParser.parseCode(codeArraySpec);
outputText = CodesArrayParser.parseOutputText(codeArraySpec);
supportedMinSdkVersion =
CodesArrayParser.getMinSupportSdkVersion(codeArraySpec);
} else {
final String textArraySpec = array[i];
// TODO: Utilize KeySpecParser or write more generic TextsArrayParser.
label = textArraySpec;
code = Constants.CODE_OUTPUT_TEXT;
outputText = textArraySpec + (char)Constants.CODE_SPACE;
supportedMinSdkVersion = 0;
}
if (Build.VERSION.SDK_INT < supportedMinSdkVersion) {
continue;
}
final int labelFlags = row.getDefaultKeyLabelFlags();
// TODO: Should be able to assign default keyActionFlags as well.
final int backgroundType = row.getDefaultBackgroundType();
final int x = (int)row.getKeyX(null);
final int y = row.getKeyY();
final int width = (int)keyWidth;
final int height = row.getRowHeight();
//final Key key = new Key(label, KeyboardIconsSet.ICON_UNDEFINED, code, outputText,
// null /* hintLabel */, labelFlags, backgroundType, x, y, width, height,
// mParams.mHorizontalGap, mParams.mVerticalGap, KeyConsts.ACTION_FLAGS_NO_KEY_PREVIEW);
//endKey(key);
row.advanceXPos(keyWidth);
}
endRow(row);
}
XmlParseUtils.checkEndTag(TAG_GRID_ROWS, parser);
}
private void parseKey(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
throws XmlPullParserException, IOException {
if (skip) {
XmlParseUtils.checkEndTag(TAG_KEY, parser);
if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY);
return;
}
final TypedArray keyAttr = mResources.obtainAttributes(
Xml.asAttributeSet(parser), R.styleable.Keyboard_Key);
final KeyStyle keyStyle = mParams.mKeyStyles.getKeyStyle(keyAttr, parser);
final String keySpec = keyStyle.getString(keyAttr, R.styleable.Keyboard_Key_keySpec, null);
if (TextUtils.isEmpty(keySpec)) {
throw new ParseException("Empty keySpec", parser);
}
//final Key key = new Key(keySpec, keyAttr, keyStyle, mParams, row);
keyAttr.recycle();
//if (DEBUG) {
// startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY, (key.isEnabled() ? "" : " disabled"),
// key, Arrays.toString(key.getMoreKeys()));
//}
XmlParseUtils.checkEndTag(TAG_KEY, parser);
//endKey(key);
}
private void parseSpacer(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
throws XmlPullParserException, IOException {
if (skip) {
XmlParseUtils.checkEndTag(TAG_SPACER, parser);
if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER);
return;
}
final TypedArray keyAttr = mResources.obtainAttributes(
Xml.asAttributeSet(parser), R.styleable.Keyboard_Key);
final KeyStyle keyStyle = mParams.mKeyStyles.getKeyStyle(keyAttr, parser);
//final Key spacer = new Key.Spacer(keyAttr, keyStyle, mParams, row);
keyAttr.recycle();
if (DEBUG) startEndTag("<%s />", TAG_SPACER);
XmlParseUtils.checkEndTag(TAG_SPACER, parser);
//endKey(spacer);
}
private void parseIncludeKeyboardContent(final XmlPullParser parser, final boolean skip)
throws XmlPullParserException, IOException {
parseIncludeInternal(parser, null, skip);
}
private void parseIncludeRowContent(final XmlPullParser parser, final KeyboardRow row,
final boolean skip) throws XmlPullParserException, IOException {
parseIncludeInternal(parser, row, skip);
}
private void parseIncludeInternal(final XmlPullParser parser, final KeyboardRow row,
final boolean skip) throws XmlPullParserException, IOException {
if (skip) {
XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE);
return;
}
final AttributeSet attr = Xml.asAttributeSet(parser);
final TypedArray keyboardAttr = mResources.obtainAttributes(
attr, R.styleable.Keyboard_Include);
final TypedArray keyAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key);
int keyboardLayout = 0;
try {
XmlParseUtils.checkAttributeExists(
keyboardAttr, R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
TAG_INCLUDE, parser);
keyboardLayout = keyboardAttr.getResourceId(
R.styleable.Keyboard_Include_keyboardLayout, 0);
if (row != null) {
// Override current x coordinate.
row.setXPos(row.getKeyX(keyAttr));
// Push current Row attributes and update with new attributes.
row.pushRowAttributes(keyAttr);
}
} finally {
keyboardAttr.recycle();
keyAttr.recycle();
}
XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
if (DEBUG) {
startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
mResources.getResourceEntryName(keyboardLayout));
}
final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
try {
parseMerge(parserForInclude, row, skip);
} finally {
if (row != null) {
// Restore Row attributes.
row.popRowAttributes();
}
parserForInclude.close();
}
}
private void parseMerge(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
throws XmlPullParserException, IOException {
if (DEBUG) startTag("<%s>", TAG_MERGE);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
final int event = parser.next();
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_MERGE.equals(tag)) {
if (row == null) {
parseKeyboardContent(parser, skip);
} else {
parseRowContent(parser, row, skip);
}
return;
}
throw new XmlParseUtils.ParseException(
"Included keyboard layout must have <merge> root element", parser);
}
}
}
private void parseSwitchKeyboardContent(final XmlPullParser parser, final boolean skip)
throws XmlPullParserException, IOException {
parseSwitchInternal(parser, null, skip);
}
private void parseSwitchRowContent(final XmlPullParser parser, final KeyboardRow row,
final boolean skip) throws XmlPullParserException, IOException {
parseSwitchInternal(parser, row, skip);
}
private void parseSwitchInternal(final XmlPullParser parser, final KeyboardRow row,
final boolean skip) throws XmlPullParserException, IOException {
if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
boolean selected = false;
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
final int event = parser.next();
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_CASE.equals(tag)) {
selected |= parseCase(parser, row, selected ? true : skip);
} else if (TAG_DEFAULT.equals(tag)) {
selected |= parseDefault(parser, row, selected ? true : skip);
} else {
throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_SWITCH);
}
} else if (event == XmlPullParser.END_TAG) {
final String tag = parser.getName();
if (TAG_SWITCH.equals(tag)) {
if (DEBUG) endTag("</%s>", TAG_SWITCH);
return;
}
throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_SWITCH);
}
}
}
private boolean parseCase(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
throws XmlPullParserException, IOException {
final boolean selected = parseCaseCondition(parser);
if (row == null) {
// Processing Rows.
parseKeyboardContent(parser, selected ? skip : true);
} else {
// Processing Keys.
parseRowContent(parser, row, selected ? skip : true);
}
return selected;
}
private boolean parseCaseCondition(final XmlPullParser parser) {
final KeyboardId id = mParams.mId;
if (id == null) {
return true;
}
final AttributeSet attr = Xml.asAttributeSet(parser);
final TypedArray caseAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Case);
try {
final boolean keyboardLayoutSetMatched = matchString(caseAttr,
R.styleable.Keyboard_Case_keyboardLayoutSet,
id.mKeyboardLayoutSetName);
final boolean keyboardLayoutSetElementMatched = matchTypedValue(caseAttr,
R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId,
KeyboardId.elementIdToName(id.mElementId));
final boolean keyboardThemeMacthed = matchTypedValue(caseAttr,
R.styleable.Keyboard_Case_keyboardTheme, mParams.mThemeId,
KeyboardTheme.getKeyboardThemeName(mParams.mThemeId));
final boolean modeMatched = matchTypedValue(caseAttr,
R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
final boolean navigateNextMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_navigateNext, id.navigateNext());
final boolean navigatePreviousMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_navigatePrevious, id.navigatePrevious());
final boolean passwordInputMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
final boolean clobberSettingsKeyMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
final boolean hasShortcutKeyMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
final boolean languageSwitchKeyEnabledMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_bottomEmojiKeyEnabled,
id.mBottomEmojiKeyEnabled);
final boolean isMultiLineMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine());
final boolean imeActionMatched = matchInteger(caseAttr,
R.styleable.Keyboard_Case_imeAction, id.imeAction());
final boolean isIconDefinedMatched = isIconDefined(caseAttr,
R.styleable.Keyboard_Case_isIconDefined, mParams.mIconsSet);
final Locale locale = id.getLocale();
final boolean localeCodeMatched = matchLocaleCodes(caseAttr, locale);
final boolean languageCodeMatched = matchLanguageCodes(caseAttr, locale);
final boolean countryCodeMatched = matchCountryCodes(caseAttr, locale);
final boolean splitLayoutMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_isSplitLayout, id.mIsSplitLayout);
final boolean numberRowMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_numberRow, id.mNumberRow);
final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched
&& keyboardThemeMacthed && modeMatched && navigateNextMatched
&& navigatePreviousMatched && passwordInputMatched && clobberSettingsKeyMatched
&& hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
&& isMultiLineMatched && imeActionMatched && isIconDefinedMatched
&& localeCodeMatched && languageCodeMatched && countryCodeMatched
&& splitLayoutMatched && numberRowMatched;
if (DEBUG) {
startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
textAttr(caseAttr.getString(
R.styleable.Keyboard_Case_keyboardLayoutSet), "keyboardLayoutSet"),
textAttr(caseAttr.getString(
R.styleable.Keyboard_Case_keyboardLayoutSetElement),
"keyboardLayoutSetElement"),
textAttr(caseAttr.getString(
R.styleable.Keyboard_Case_keyboardTheme), "keyboardTheme"),
textAttr(caseAttr.getString(R.styleable.Keyboard_Case_mode), "mode"),
textAttr(caseAttr.getString(R.styleable.Keyboard_Case_imeAction),
"imeAction"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_navigateNext,
"navigateNext"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_navigatePrevious,
"navigatePrevious"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_clobberSettingsKey,
"clobberSettingsKey"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_passwordInput,
"passwordInput"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey,
"hasShortcutKey"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_bottomEmojiKeyEnabled,
"bottomEmojiKeyEnabled"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_isMultiLine,
"isMultiLine"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_isSplitLayout,
"splitLayout"),
textAttr(caseAttr.getString(R.styleable.Keyboard_Case_isIconDefined),
"isIconDefined"),
textAttr(caseAttr.getString(R.styleable.Keyboard_Case_localeCode),
"localeCode"),
textAttr(caseAttr.getString(R.styleable.Keyboard_Case_languageCode),
"languageCode"),
textAttr(caseAttr.getString(R.styleable.Keyboard_Case_countryCode),
"countryCode"),
selected ? "" : " skipped");
}
return selected;
} finally {
caseAttr.recycle();
}
}
private static boolean matchLocaleCodes(TypedArray caseAttr, final Locale locale) {
return matchString(caseAttr, R.styleable.Keyboard_Case_localeCode, locale.toString());
}
private static boolean matchLanguageCodes(TypedArray caseAttr, Locale locale) {
return matchString(caseAttr, R.styleable.Keyboard_Case_languageCode, locale.getLanguage());
}
private static boolean matchCountryCodes(TypedArray caseAttr, Locale locale) {
return matchString(caseAttr, R.styleable.Keyboard_Case_countryCode, locale.getCountry());
}
private static boolean matchInteger(final TypedArray a, final int index, final int value) {
// If <case> does not have "index" attribute, that means this <case> is wild-card for
// the attribute.
return !a.hasValue(index) || a.getInt(index, 0) == value;
}
private static boolean matchBoolean(final TypedArray a, final int index, final boolean value) {
// If <case> does not have "index" attribute, that means this <case> is wild-card for
// the attribute.
return !a.hasValue(index) || a.getBoolean(index, false) == value;
}
private static boolean matchString(final TypedArray a, final int index, final String value) {
// If <case> does not have "index" attribute, that means this <case> is wild-card for
// the attribute.
return !a.hasValue(index)
|| StringUtils.containsInArray(value, a.getString(index).split("\\|"));
}
private static boolean matchTypedValue(final TypedArray a, final int index, final int intValue,
final String strValue) {
// If <case> does not have "index" attribute, that means this <case> is wild-card for
// the attribute.
final TypedValue v = a.peekValue(index);
if (v == null) {
return true;
}
if (ResourceUtils.isIntegerValue(v)) {
return intValue == a.getInt(index, 0);
}
if (ResourceUtils.isStringValue(v)) {
return StringUtils.containsInArray(strValue, a.getString(index).split("\\|"));
}
return false;
}
private static boolean isIconDefined(final TypedArray a, final int index,
final KeyboardIconsSet iconsSet) {
if (!a.hasValue(index)) {
return true;
}
final String iconName = a.getString(index);
return iconsSet.getIconDrawable(iconName) != null;
}
private boolean parseDefault(final XmlPullParser parser, final KeyboardRow row,
final boolean skip) throws XmlPullParserException, IOException {
if (DEBUG) startTag("<%s>", TAG_DEFAULT);
if (row == null) {
parseKeyboardContent(parser, skip);
} else {
parseRowContent(parser, row, skip);
}
return true;
}
private void parseKeyStyle(final XmlPullParser parser, final boolean skip)
throws XmlPullParserException, IOException {
final AttributeSet attr = Xml.asAttributeSet(parser);
final TypedArray keyStyleAttr = mResources.obtainAttributes(
attr, R.styleable.Keyboard_KeyStyle);
final TypedArray keyAttrs = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key);
try {
if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) {
throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
+ "/> needs styleName attribute", parser);
}
if (DEBUG) {
startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
skip ? " skipped" : "");
}
if (!skip) {
mParams.mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
}
} finally {
keyStyleAttr.recycle();
keyAttrs.recycle();
}
XmlParseUtils.checkEndTag(TAG_KEY_STYLE, parser);
}
private void startKeyboard() {
mCurrentY += mParams.mTopPadding;
mTopEdge = true;
}
private void startRow(final KeyboardRow row) {
addEdgeSpace(mParams.mLeftPadding, row);
mCurrentRow = row;
mLeftEdge = true;
mRightEdgeKey = null;
}
private void endRow(final KeyboardRow row) {
if (mCurrentRow == null) {
throw new RuntimeException("orphan end row tag");
}
if (mRightEdgeKey != null) {
mRightEdgeKey.markAsRightEdge(mParams);
mRightEdgeKey = null;
}
addEdgeSpace(mParams.mRightPadding, row);
mCurrentY += row.getRowHeight();
mCurrentRow = null;
mTopEdge = false;
}
private void endKey(@Nonnull final Key key) {
mParams.onAddKey(key);
if (mLeftEdge) {
key.markAsLeftEdge(mParams);
mLeftEdge = false;
}
if (mTopEdge) {
key.markAsTopEdge(mParams);
}
mRightEdgeKey = key;
}
private void endKeyboard() {
mParams.removeRedundantMoreKeys();
// {@link #parseGridRows(XmlPullParser,boolean)} may populate keyboard rows higher than
// previously expected.
final int actualHeight = mCurrentY - mParams.mVerticalGap + mParams.mBottomPadding;
mParams.mOccupiedHeight = Math.max(mParams.mOccupiedHeight, actualHeight);
}
private void addEdgeSpace(final float width, final KeyboardRow row) {
row.advanceXPos(width);
mLeftEdge = false;
mRightEdgeKey = null;
}
private static String textAttr(final String value, final String name) {
return value != null ? String.format(" %s=%s", name, value) : "";
}
private static String booleanAttr(final TypedArray a, final int index, final String name) {
return a.hasValue(index)
? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
}
}

View File

@ -76,8 +76,6 @@ public class KeyboardParams {
public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
@Nonnull
public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
@Nonnull
public final KeyStylesSet mKeyStyles = new KeyStylesSet(mTextsSet);
@Nonnull
private final UniqueKeysCache mUniqueKeysCache;

View File

@ -1,188 +0,0 @@
/*
* Copyright (C) 2012 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 org.futo.inputmethod.keyboard.internal;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.Xml;
import org.futo.inputmethod.keyboard.KeyConsts;
import org.futo.inputmethod.keyboard.Keyboard;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.utils.ResourceUtils;
import org.xmlpull.v1.XmlPullParser;
import java.util.ArrayDeque;
/**
* Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
* Some of the key size defaults can be overridden per row from what the {@link Keyboard}
* defines.
*/
// TODO: Remove this class, it is no longer used
public final class KeyboardRow {
// keyWidth enum constants
private static final int KEYWIDTH_NOT_ENUM = 0;
private static final int KEYWIDTH_FILL_RIGHT = -1;
private final KeyboardParams mParams;
/** The height of this row. */
private final int mRowHeight;
private final ArrayDeque<RowAttributes> mRowAttributesStack = new ArrayDeque<>();
// TODO: Add keyActionFlags.
private static class RowAttributes {
/** Default width of a key in this row. */
public final float mDefaultKeyWidth;
/** Default keyLabelFlags in this row. */
public final int mDefaultKeyLabelFlags;
/** Default backgroundType for this row */
public final int mDefaultBackgroundType;
/**
* Parse and create key attributes. This constructor is used to parse Row tag.
*
* @param keyAttr an attributes array of Row tag.
* @param defaultKeyWidth a default key width.
* @param keyboardWidth the keyboard width that is required to calculate keyWidth attribute.
*/
public RowAttributes(final TypedArray keyAttr, final float defaultKeyWidth,
final int keyboardWidth) {
mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
keyboardWidth, keyboardWidth, defaultKeyWidth);
mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0);
mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
KeyConsts.BACKGROUND_TYPE_NORMAL);
}
/**
* Parse and update key attributes using default attributes. This constructor is used
* to parse include tag.
*
* @param keyAttr an attributes array of include tag.
* @param defaultRowAttr default Row attributes.
* @param keyboardWidth the keyboard width that is required to calculate keyWidth attribute.
*/
public RowAttributes(final TypedArray keyAttr, final RowAttributes defaultRowAttr,
final int keyboardWidth) {
mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
keyboardWidth, keyboardWidth, defaultRowAttr.mDefaultKeyWidth);
mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0)
| defaultRowAttr.mDefaultKeyLabelFlags;
mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
defaultRowAttr.mDefaultBackgroundType);
}
}
private final int mCurrentY;
// Will be updated by {@link Key}'s constructor.
private float mCurrentX;
public KeyboardRow(final Resources res, final KeyboardParams params,
final XmlPullParser parser, final int y) {
mParams = params;
final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard);
mRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_rowHeight, params.mBaseHeight, params.mDefaultRowHeight);
keyboardAttr.recycle();
final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_Key);
mRowAttributesStack.push(new RowAttributes(
keyAttr, params.mDefaultKeyWidth, params.mBaseWidth));
keyAttr.recycle();
mCurrentY = y;
mCurrentX = 0.0f;
}
public int getRowHeight() {
return mRowHeight;
}
public void pushRowAttributes(final TypedArray keyAttr) {
final RowAttributes newAttributes = new RowAttributes(
keyAttr, mRowAttributesStack.peek(), mParams.mBaseWidth);
mRowAttributesStack.push(newAttributes);
}
public void popRowAttributes() {
mRowAttributesStack.pop();
}
public float getDefaultKeyWidth() {
return mRowAttributesStack.peek().mDefaultKeyWidth;
}
public int getDefaultKeyLabelFlags() {
return mRowAttributesStack.peek().mDefaultKeyLabelFlags;
}
public int getDefaultBackgroundType() {
return mRowAttributesStack.peek().mDefaultBackgroundType;
}
public void setXPos(final float keyXPos) {
mCurrentX = keyXPos;
}
public void advanceXPos(final float width) {
mCurrentX += width;
}
public int getKeyY() {
return mCurrentY;
}
public float getKeyX(final TypedArray keyAttr) {
if (keyAttr == null || !keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
return mCurrentX;
}
final float keyXPos = keyAttr.getFraction(R.styleable.Keyboard_Key_keyXPos,
mParams.mBaseWidth, mParams.mBaseWidth, 0);
if (keyXPos >= 0) {
return keyXPos + mParams.mLeftPadding;
}
// If keyXPos is negative, the actual x-coordinate will be
// keyboardWidth + keyXPos.
// keyXPos shouldn't be less than mCurrentX because drawable area for this
// key starts at mCurrentX. Or, this key will overlaps the adjacent key on
// its left hand side.
final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mRightPadding;
return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
}
public float getKeyWidth(final TypedArray keyAttr, final float keyXPos) {
if (keyAttr == null) {
return getDefaultKeyWidth();
}
final int widthType = ResourceUtils.getEnumValue(keyAttr,
R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
switch (widthType) {
case KEYWIDTH_FILL_RIGHT:
// If keyWidth is fillRight, the actual key width will be determined to fill
// out the area up to the right edge of the keyboard.
final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mRightPadding;
return keyboardRightEdge - keyXPos;
default: // KEYWIDTH_NOT_ENUM
return keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
mParams.mBaseWidth, mParams.mBaseWidth, getDefaultKeyWidth());
}
}
}

View File

@ -22,10 +22,9 @@ import android.content.Intent;
import android.os.Process;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.latin.utils.UncachedInputMethodManagerUtils;
import org.futo.inputmethod.v2keyboard.KeyboardLayoutSetV2;
/**
* This class detects the {@link Intent#ACTION_MY_PACKAGE_REPLACED} broadcast intent when this IME
@ -63,7 +62,7 @@ public final class SystemBroadcastReceiver extends BroadcastReceiver {
Log.i(TAG, "Boot has been completed");
} else if (Intent.ACTION_LOCALE_CHANGED.equals(intentAction)) {
Log.i(TAG, "System locale changed");
KeyboardLayoutSet.onSystemLocaleChanged();
KeyboardLayoutSetV2.onSystemLocaleChanged();
}
// The process that hosts this broadcast receiver is invoked and remains alive even after

View File

@ -18,21 +18,15 @@ package org.futo.inputmethod.latin.spellcheck;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.service.textservice.SpellCheckerService;
import android.text.InputType;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.textservice.SuggestionsInfo;
import org.futo.inputmethod.keyboard.Keyboard;
import org.futo.inputmethod.keyboard.KeyboardId;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.latin.DictionaryFacilitator;
import org.futo.inputmethod.latin.DictionaryFacilitatorLruCache;
import org.futo.inputmethod.latin.NgramContext;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.RichInputMethodSubtype;
import org.futo.inputmethod.latin.SuggestedWords;
import org.futo.inputmethod.latin.common.ComposedData;
import org.futo.inputmethod.latin.settings.SettingsValuesForSuggestion;
@ -227,19 +221,20 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
final String keyboardLayoutName = getKeyboardLayoutNameForLocale(locale);
final InputMethodSubtype subtype = AdditionalSubtypeUtils.createDummyAdditionalSubtype(
locale.toString(), keyboardLayoutName);
final KeyboardLayoutSet keyboardLayoutSet = createKeyboardSetForSpellChecker(subtype);
return keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
throw new UnsupportedOperationException("TODO: Implement");
//final KeyboardLayoutSet keyboardLayoutSet = createKeyboardSetForSpellChecker(subtype);
//return keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
}
private KeyboardLayoutSet createKeyboardSetForSpellChecker(final InputMethodSubtype subtype) {
/*private KeyboardLayoutSet createKeyboardSetForSpellChecker(final InputMethodSubtype subtype) {
final EditorInfo editorInfo = new EditorInfo();
editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(this, editorInfo);
builder.setKeyboardGeometry(
SPELLCHECKER_DUMMY_KEYBOARD_WIDTH, SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT);
builder.setSubtype(RichInputMethodSubtype.getRichInputMethodSubtype(subtype));
builder.setIsSpellChecker(true /* isSpellChecker */);
builder.setIsSpellChecker(true);
builder.disableTouchPositionCorrectionData();
return builder.build();
}
}*/
}

View File

@ -1,7 +1,9 @@
package org.futo.inputmethod.latin.uix
import android.content.Context
import android.content.res.TypedArray
import android.graphics.drawable.Drawable
import android.view.ContextThemeWrapper
import androidx.annotation.ColorInt
import org.futo.inputmethod.v2keyboard.KeyVisualStyle
@ -55,5 +57,19 @@ interface DynamicThemeProvider {
fun getDrawableOrDefault(i: Int, keyAttr: TypedArray, provider: DynamicThemeProvider?): Drawable? {
return (provider?.getDrawable(i)) ?: keyAttr.getDrawable(i)
}
@JvmStatic
fun obtainFromContext(context: Context): DynamicThemeProvider {
if (context is DynamicThemeProviderOwner) {
return context.getDrawableProvider()
} else if (context is ContextThemeWrapper) {
val baseContext = context.baseContext
if (baseContext is DynamicThemeProviderOwner) {
return baseContext.getDrawableProvider()
}
}
throw IllegalArgumentException("Could not find DynamicThemeProviderOwner")
}
}
}

View File

@ -372,8 +372,6 @@ data class BaseKey(
/**
* If set, overrides a default hint from the value of moreKeys.
*
* TODO: Currently does not override
*/
val hint: String? = null,
) : AbstractKey {

View File

@ -268,6 +268,18 @@ Stack trace: ${e.stackTrace.map { it.toString() }}
return errorLayout.build(context, keyboardParams, layoutParams)
}
}
companion object {
@JvmStatic
fun onSystemLocaleChanged() {
}
@JvmStatic
fun onKeyboardThemeChanged() {
}
}
}
public fun getKeyboardMode(editorInfo: EditorInfo): Int {

View File

@ -1,7 +1,6 @@
package org.futo.inputmethod.v2keyboard
import android.content.Context
import android.view.ContextThemeWrapper
import androidx.compose.ui.unit.Dp
import org.futo.inputmethod.keyboard.KeyConsts
import org.futo.inputmethod.keyboard.internal.KeyboardLayoutElement
@ -9,7 +8,6 @@ import org.futo.inputmethod.keyboard.internal.KeyboardParams
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.common.Constants
import org.futo.inputmethod.latin.uix.DynamicThemeProvider
import org.futo.inputmethod.latin.uix.DynamicThemeProviderOwner
import kotlin.math.floor
import kotlin.math.roundToInt
@ -577,17 +575,9 @@ data class LayoutEngine(
params.mBaseHeight = totalKeyboardHeight
params.mDefaultRowHeight = rowHeightPx.roundToInt()
var provider: DynamicThemeProvider? = null
if (context is DynamicThemeProviderOwner) {
provider = (context as DynamicThemeProviderOwner).getDrawableProvider()
} else if (context is ContextThemeWrapper) {
val baseContext = (context as ContextThemeWrapper).baseContext
if (baseContext is DynamicThemeProviderOwner) {
provider = (baseContext as DynamicThemeProviderOwner).getDrawableProvider()
}
}
val provider = DynamicThemeProvider.obtainFromContext(context)
params.mIconsSet.loadIcons(null, provider!!)
params.mIconsSet.loadIcons(null, provider)
params.mThemeId = 3
params.mTextsSet.setLocale(params.mId.locale, context)

View File

@ -25,7 +25,6 @@ import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.compat.InputMethodSubtypeCompatUtils;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet.Builder;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.RichInputMethodManager;
import org.futo.inputmethod.latin.RichInputMethodSubtype;

View File

@ -25,9 +25,7 @@ import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.keyboard.Key;
import org.futo.inputmethod.keyboard.Keyboard;
import org.futo.inputmethod.keyboard.KeyboardId;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.keyboard.KeyboardLayoutSetTestsBase;
import org.futo.inputmethod.keyboard.internal.KeyboardIconsSet;
import org.futo.inputmethod.keyboard.layout.expected.ExpectedKeyVisual;
import org.futo.inputmethod.latin.common.Constants;
import org.futo.inputmethod.latin.common.LocaleUtils;

View File

@ -21,7 +21,6 @@ import android.test.suitebuilder.annotation.MediumTest;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.keyboard.internal.KeyboardIconsSet;
import org.futo.inputmethod.keyboard.internal.KeyboardTextsSet;
import org.futo.inputmethod.latin.R;

View File

@ -22,7 +22,6 @@ import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.keyboard.Key;
import org.futo.inputmethod.keyboard.Keyboard;
import org.futo.inputmethod.keyboard.KeyboardId;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.keyboard.KeyboardLayoutSetTestsBase;
import org.futo.inputmethod.keyboard.KeyboardTheme;
import org.futo.inputmethod.keyboard.layout.LayoutBase;

View File

@ -22,7 +22,6 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.keyboard.KeyboardId;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.keyboard.layout.Dvorak;
import org.futo.inputmethod.keyboard.layout.LayoutBase;
import org.futo.inputmethod.keyboard.layout.customizer.DvorakCustomizer.EnglishDvorakCustomizer;

View File

@ -22,7 +22,6 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.keyboard.KeyboardId;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.keyboard.layout.Dvorak;
import org.futo.inputmethod.keyboard.layout.LayoutBase;
import org.futo.inputmethod.keyboard.layout.customizer.DvorakCustomizer.EnglishDvorakCustomizer;

View File

@ -21,7 +21,6 @@ import android.text.InputType;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.keyboard.layout.LayoutBase;
import org.futo.inputmethod.keyboard.layout.Qwerty;
import org.futo.inputmethod.keyboard.layout.customizer.EnglishCustomizer;

View File

@ -21,7 +21,6 @@ import android.text.InputType;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.keyboard.layout.LayoutBase;
import org.futo.inputmethod.keyboard.layout.Qwerty;
import org.futo.inputmethod.keyboard.layout.customizer.EnglishCustomizer;

View File

@ -20,7 +20,6 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import org.futo.inputmethod.keyboard.KeyboardLayoutSet;
import org.futo.inputmethod.keyboard.layout.LayoutBase;
import org.futo.inputmethod.keyboard.layout.Qwerty;
import org.futo.inputmethod.keyboard.layout.customizer.EnglishCustomizer;