mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-05-14 05:52:47 +00:00
prepare for splitting parsing and building of keyboards
one step towards #216
This commit is contained in:
parent
f3c52e9f6c
commit
b326011e0c
3 changed files with 403 additions and 259 deletions
|
@ -230,247 +230,6 @@ public class Key implements Comparable<Key> {
|
|||
mHashCode = computeHashCode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a key in a <GridRows/>.
|
||||
*/
|
||||
public Key(@Nullable final String label, final int code, @Nullable final String outputText,
|
||||
@Nullable final String hintLabel, @Nullable final String moreKeySpecs,
|
||||
final int labelFlags, final int backgroundType, final int x, final int y,
|
||||
final int width, final int height, final KeyboardParams params) {
|
||||
mWidth = width - params.mHorizontalGap;
|
||||
mHeight = height - params.mVerticalGap;
|
||||
mHorizontalGap = params.mHorizontalGap;
|
||||
mVerticalGap = params.mVerticalGap;
|
||||
mHintLabel = hintLabel;
|
||||
mLabelFlags = labelFlags;
|
||||
mBackgroundType = backgroundType;
|
||||
|
||||
if (moreKeySpecs != null) {
|
||||
String[] moreKeys = MoreKeySpec.splitKeySpecs(moreKeySpecs);
|
||||
// Get maximum column order number and set a relevant mode value.
|
||||
int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER
|
||||
| params.mMaxMoreKeysKeyboardColumn;
|
||||
int value;
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY;
|
||||
}
|
||||
mMoreKeysColumnAndFlags = moreKeysColumnAndFlags;
|
||||
|
||||
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, null);
|
||||
int actionFlags = 0;
|
||||
if (moreKeys != null) {
|
||||
actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
|
||||
mMoreKeys = new MoreKeySpec[moreKeys.length];
|
||||
for (int i = 0; i < moreKeys.length; i++) {
|
||||
mMoreKeys[i] = new MoreKeySpec(moreKeys[i], false, Locale.getDefault());
|
||||
}
|
||||
} else {
|
||||
mMoreKeys = null;
|
||||
}
|
||||
mActionFlags = actionFlags;
|
||||
} else {
|
||||
// TODO: Pass keyActionFlags as an argument.
|
||||
mActionFlags = ACTION_FLAGS_NO_KEY_PREVIEW;
|
||||
mMoreKeys = null;
|
||||
mMoreKeysColumnAndFlags = 0;
|
||||
}
|
||||
|
||||
mLabel = label;
|
||||
mOptionalAttributes = OptionalAttributes.newInstance(outputText, CODE_UNSPECIFIED,
|
||||
ICON_UNDEFINED, 0 /* visualInsetsLeft */, 0 /* visualInsetsRight */);
|
||||
mCode = code;
|
||||
mEnabled = (code != CODE_UNSPECIFIED);
|
||||
mIconId = KeyboardIconsSet.ICON_UNDEFINED;
|
||||
// Horizontal gap is divided equally to both sides of the key.
|
||||
mX = x + mHorizontalGap / 2;
|
||||
mY = y;
|
||||
mHitBox.set(x, y, x + width + 1, y + height);
|
||||
mKeyVisualAttributes = null;
|
||||
|
||||
mHashCode = computeHashCode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a key with the given top-left coordinate and extract its attributes from a key
|
||||
* specification string, Key attribute array, key style, and etc.
|
||||
*
|
||||
* @param keySpec the key specification.
|
||||
* @param keyAttr the Key XML attributes array.
|
||||
* @param style the {@link KeyStyle} of this key.
|
||||
* @param params the keyboard building parameters.
|
||||
* @param row the row that this key belongs to. row's x-coordinate will be the right edge of
|
||||
* this key.
|
||||
*/
|
||||
public Key(@Nullable final String keySpec, @NonNull final TypedArray keyAttr,
|
||||
@NonNull final KeyStyle style, @NonNull final KeyboardParams params,
|
||||
@NonNull final KeyboardRow row) {
|
||||
mHorizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
|
||||
mVerticalGap = params.mVerticalGap;
|
||||
|
||||
final float horizontalGapFloat = mHorizontalGap;
|
||||
final int rowHeight = row.getRowHeight();
|
||||
mHeight = rowHeight - mVerticalGap;
|
||||
|
||||
final float keyXPos = row.getKeyX(keyAttr);
|
||||
final float keyWidth = row.getKeyWidth(keyAttr, keyXPos);
|
||||
final int keyYPos = row.getKeyY();
|
||||
|
||||
// Horizontal gap is divided equally to both sides of the key.
|
||||
mX = Math.round(keyXPos + horizontalGapFloat / 2);
|
||||
mY = keyYPos;
|
||||
mWidth = Math.round(keyWidth - horizontalGapFloat);
|
||||
mHitBox.set(Math.round(keyXPos), keyYPos, Math.round(keyXPos + keyWidth) + 1,
|
||||
keyYPos + rowHeight);
|
||||
// Update row to have current x coordinate.
|
||||
row.setXPos(keyXPos + keyWidth);
|
||||
|
||||
mBackgroundType = style.getInt(keyAttr,
|
||||
R.styleable.Keyboard_Key_backgroundType, row.getDefaultBackgroundType());
|
||||
|
||||
final int baseWidth = params.mBaseWidth;
|
||||
final int visualInsetsLeft = Math.round(keyAttr.getFraction(
|
||||
R.styleable.Keyboard_Key_visualInsetsLeft, baseWidth, baseWidth, 0));
|
||||
final int visualInsetsRight = Math.round(keyAttr.getFraction(
|
||||
R.styleable.Keyboard_Key_visualInsetsRight, baseWidth, baseWidth, 0));
|
||||
|
||||
mLabelFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags)
|
||||
| row.getDefaultKeyLabelFlags();
|
||||
final boolean needsToUpcase = needsToUpcase(mLabelFlags, params.mId.mElementId);
|
||||
final Locale localeForUpcasing = params.mId.getLocale();
|
||||
int actionFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
|
||||
String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
|
||||
|
||||
// Get maximum column order number and set a relevant mode value.
|
||||
int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER
|
||||
| style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn,
|
||||
params.mMaxMoreKeysKeyboardColumn);
|
||||
int value;
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY;
|
||||
}
|
||||
mMoreKeysColumnAndFlags = moreKeysColumnAndFlags;
|
||||
|
||||
final String[] additionalMoreKeys;
|
||||
if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) {
|
||||
additionalMoreKeys = null;
|
||||
} else {
|
||||
additionalMoreKeys = style.getStringArray(keyAttr,
|
||||
R.styleable.Keyboard_Key_additionalMoreKeys);
|
||||
}
|
||||
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
|
||||
if (moreKeys != null) {
|
||||
actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
|
||||
mMoreKeys = new MoreKeySpec[moreKeys.length];
|
||||
for (int i = 0; i < moreKeys.length; i++) {
|
||||
mMoreKeys[i] = new MoreKeySpec(moreKeys[i], needsToUpcase, localeForUpcasing);
|
||||
}
|
||||
} else {
|
||||
mMoreKeys = null;
|
||||
}
|
||||
mActionFlags = actionFlags;
|
||||
|
||||
mIconId = KeySpecParser.getIconId(keySpec);
|
||||
final int disabledIconId = KeySpecParser.getIconId(style.getString(keyAttr,
|
||||
R.styleable.Keyboard_Key_keyIconDisabled));
|
||||
|
||||
final int code = KeySpecParser.getCode(keySpec);
|
||||
if ((mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0) {
|
||||
mLabel = params.mId.mCustomActionLabel;
|
||||
} else if (code >= Character.MIN_SUPPLEMENTARY_CODE_POINT) {
|
||||
// This is a workaround to have a key that has a supplementary code point in its label.
|
||||
// Because we can put a string in resource neither as a XML entity of a supplementary
|
||||
// code point nor as a surrogate pair.
|
||||
mLabel = new StringBuilder().appendCodePoint(code).toString();
|
||||
} else {
|
||||
final String label = KeySpecParser.getLabel(keySpec);
|
||||
mLabel = needsToUpcase
|
||||
? StringUtils.toTitleCaseOfKeyLabel(label, localeForUpcasing)
|
||||
: label;
|
||||
}
|
||||
if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) {
|
||||
mHintLabel = null;
|
||||
} else {
|
||||
final String hintLabel = style.getString(
|
||||
keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
|
||||
mHintLabel = needsToUpcase
|
||||
? StringUtils.toTitleCaseOfKeyLabel(hintLabel, localeForUpcasing)
|
||||
: hintLabel;
|
||||
}
|
||||
String outputText = KeySpecParser.getOutputText(keySpec);
|
||||
if (needsToUpcase) {
|
||||
outputText = StringUtils.toTitleCaseOfKeyLabel(outputText, localeForUpcasing);
|
||||
}
|
||||
// Choose the first letter of the label as primary code if not specified.
|
||||
if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText)
|
||||
&& !TextUtils.isEmpty(mLabel)) {
|
||||
if (StringUtils.codePointCount(mLabel) == 1) {
|
||||
// Use the first letter of the hint label if shiftedLetterActivated flag is
|
||||
// specified.
|
||||
if (hasShiftedLetterHint() && isShiftedLetterActivated()) {
|
||||
mCode = mHintLabel.codePointAt(0);
|
||||
} else {
|
||||
mCode = mLabel.codePointAt(0);
|
||||
}
|
||||
} else {
|
||||
// In some locale and case, the character might be represented by multiple code
|
||||
// points, such as upper case Eszett of German alphabet.
|
||||
outputText = mLabel;
|
||||
mCode = CODE_OUTPUT_TEXT;
|
||||
}
|
||||
} else if (code == CODE_UNSPECIFIED && outputText != null) {
|
||||
if (StringUtils.codePointCount(outputText) == 1) {
|
||||
mCode = outputText.codePointAt(0);
|
||||
outputText = null;
|
||||
} else {
|
||||
mCode = CODE_OUTPUT_TEXT;
|
||||
}
|
||||
} else {
|
||||
mCode = needsToUpcase ? StringUtils.toTitleCaseOfKeyCode(code, localeForUpcasing)
|
||||
: code;
|
||||
}
|
||||
final int altCodeInAttr = KeySpecParser.parseCode(
|
||||
style.getString(keyAttr, R.styleable.Keyboard_Key_altCode), CODE_UNSPECIFIED);
|
||||
final int altCode = needsToUpcase
|
||||
? StringUtils.toTitleCaseOfKeyCode(altCodeInAttr, localeForUpcasing)
|
||||
: altCodeInAttr;
|
||||
mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode,
|
||||
disabledIconId, visualInsetsLeft, visualInsetsRight);
|
||||
mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
|
||||
mHashCode = computeHashCode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor for DynamicGridKeyboard.GridKey.
|
||||
*
|
||||
|
@ -506,6 +265,38 @@ public class Key implements Comparable<Key> {
|
|||
mEnabled = key.mEnabled;
|
||||
}
|
||||
|
||||
/** constructor from KeyParams */
|
||||
private Key(KeyParams keyParams) {
|
||||
// stuff to copy
|
||||
mCode = keyParams.mCode;
|
||||
mLabel = keyParams.mLabel;
|
||||
mHintLabel = keyParams.mHintLabel;
|
||||
mLabelFlags = keyParams.mLabelFlags;
|
||||
mIconId = keyParams.mIconId;
|
||||
mMoreKeys = keyParams.mMoreKeys;
|
||||
mMoreKeysColumnAndFlags = keyParams.mMoreKeysColumnAndFlags;
|
||||
mBackgroundType = keyParams.mBackgroundType;
|
||||
mActionFlags = keyParams.mActionFlags;
|
||||
mKeyVisualAttributes = keyParams.mKeyVisualAttributes;
|
||||
mOptionalAttributes = keyParams.mOptionalAttributes;
|
||||
mEnabled = keyParams.mEnabled;
|
||||
|
||||
// stuff to create
|
||||
mWidth = keyParams.mWidth;
|
||||
mHeight = keyParams.mHeight;
|
||||
if (!isSpacer() && (mWidth == 0 || mHeight == 0)) {
|
||||
throw new IllegalStateException("key needs positive width and height");
|
||||
}
|
||||
mHorizontalGap = isSpacer() ? 0 : keyParams.mHorizontalGap;
|
||||
mVerticalGap = keyParams.mVerticalGap;
|
||||
// Horizontal gap is divided equally to both sides of the key.
|
||||
mX = Math.round(keyParams.xPos + ((float) keyParams.mHorizontalGap) / 2);
|
||||
mY = keyParams.yPos;
|
||||
mHitBox.set(Math.round(keyParams.xPos), keyParams.yPos, Math.round(keyParams.xPos + mWidth + mHorizontalGap) + 1,
|
||||
keyParams.yPos + mHeight + mHorizontalGap);
|
||||
mHashCode = computeHashCode(this);
|
||||
}
|
||||
|
||||
private Key(@NonNull final Key key, @Nullable final MoreKeySpec[] moreKeys) {
|
||||
// Final attributes.
|
||||
mCode = key.mCode;
|
||||
|
@ -1121,10 +912,15 @@ public class Key implements Comparable<Key> {
|
|||
|| iconName.equals(KeyboardIconsSet.NAME_EMOJI_ACTION_KEY);
|
||||
}
|
||||
|
||||
public boolean isFunctional() {
|
||||
return mBackgroundType == BACKGROUND_TYPE_FUNCTIONAL
|
||||
|| mBackgroundType == BACKGROUND_TYPE_STICKY_OFF
|
||||
|| mBackgroundType == BACKGROUND_TYPE_STICKY_ON;
|
||||
}
|
||||
|
||||
public static class Spacer extends Key {
|
||||
public Spacer(final TypedArray keyAttr, final KeyStyle keyStyle,
|
||||
final KeyboardParams params, final KeyboardRow row) {
|
||||
super(null /* keySpec */, keyAttr, keyStyle, params, row);
|
||||
private Spacer(KeyParams keyParams) {
|
||||
super(keyParams);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1138,9 +934,303 @@ public class Key implements Comparable<Key> {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isFunctional() {
|
||||
return mBackgroundType == BACKGROUND_TYPE_FUNCTIONAL
|
||||
|| mBackgroundType == BACKGROUND_TYPE_STICKY_OFF
|
||||
|| mBackgroundType == BACKGROUND_TYPE_STICKY_ON;
|
||||
// for creating keys that might get modified later
|
||||
public static class KeyParams {
|
||||
// params for building
|
||||
boolean isSpacer;
|
||||
// relative values and absolute keyboard dimensions for re-determining key dimensions (if necessary)
|
||||
/*
|
||||
// todo: currently commented because not used, but planned (see todo below)
|
||||
public int keyboardWidth;
|
||||
public int keyboardHeight;
|
||||
// relative widths and heights may not add up to 100% when including gaps
|
||||
// this is ok with fill right, but otherwise?
|
||||
public float mRelativeWidth; // also allow -1f as value, this means "fill right"
|
||||
public float mRelativeHeight;
|
||||
public float mRelativeHorizontalGap;
|
||||
public float mRelativeVerticalGap;
|
||||
*/
|
||||
// stuff that likely remains after constructor, maybe make final
|
||||
final int mCode;
|
||||
@Nullable final String mLabel;
|
||||
@Nullable final String mHintLabel;
|
||||
final int mLabelFlags;
|
||||
final int mIconId;
|
||||
public final MoreKeySpec[] mMoreKeys;
|
||||
final int mMoreKeysColumnAndFlags;
|
||||
final int mBackgroundType;
|
||||
final int mActionFlags;
|
||||
@Nullable final KeyVisualAttributes mKeyVisualAttributes;
|
||||
@Nullable final OptionalAttributes mOptionalAttributes;
|
||||
public boolean mEnabled = true;
|
||||
|
||||
// stuff that may very well change, or only be set just before it's needed
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
int mHorizontalGap;
|
||||
int mVerticalGap;
|
||||
float xPos;
|
||||
int yPos;
|
||||
|
||||
public static KeyParams newSpacer(final TypedArray keyAttr, final KeyStyle keyStyle,
|
||||
final KeyboardParams params, final KeyboardRow row) {
|
||||
final KeyParams keyParams = new KeyParams(null, keyAttr, keyStyle, params, row);
|
||||
keyParams.isSpacer = true;
|
||||
return keyParams;
|
||||
}
|
||||
|
||||
public Key createKey() {
|
||||
if (isSpacer) return new Spacer(this);
|
||||
return new Key(this);
|
||||
}
|
||||
|
||||
// todo:
|
||||
// get relativeWidth and others when creating the params
|
||||
// check how relative width could be adjusted
|
||||
// there is the fillRight thing
|
||||
// and not sure if there is a spacer on the left side if the key starts not directly at the edge
|
||||
// then it should be possible to re-create the entire keyboard using the new dimensions
|
||||
// can add keys (spacer) in a row, for split keyboard
|
||||
/*
|
||||
public void setDimensionsFromRelativeSize() {
|
||||
if (keyboardHeight == 0 || keyboardWidth == 0 || mRelativeHeight == 0 || mRelativeWidth == 0)
|
||||
throw new IllegalStateException("can't use setUsingRelativeHeight, not all fields are set");
|
||||
float horizontalGap = isSpacer ? 0f : mRelativeHorizontalGap * keyboardWidth;
|
||||
mHorizontalGap = (int) horizontalGap;
|
||||
float verticalGap = mRelativeVerticalGap * mRelativeHeight;
|
||||
mVerticalGap = (int) verticalGap;
|
||||
float keyWidth = mRelativeWidth * keyboardWidth;
|
||||
mWidth = Math.round(keyWidth - horizontalGap);
|
||||
mHeight = (int) (mRelativeHeight * keyboardHeight - verticalGap);
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Create keyParams with the given top-left coordinate and extract its attributes from a key
|
||||
* specification string, Key attribute array, key style, and etc.
|
||||
*
|
||||
* @param keySpec the key specification.
|
||||
* @param keyAttr the Key XML attributes array.
|
||||
* @param style the {@link KeyStyle} of this key.
|
||||
* @param params the keyboard building parameters.
|
||||
* @param row the row that this key belongs to. row's x-coordinate will be the right edge of
|
||||
* this key.
|
||||
*/
|
||||
public KeyParams(@Nullable final String keySpec, @NonNull final TypedArray keyAttr,
|
||||
@NonNull final KeyStyle style, @NonNull final KeyboardParams params,
|
||||
@NonNull final KeyboardRow row) {
|
||||
mHorizontalGap = params.mHorizontalGap;
|
||||
mVerticalGap = params.mVerticalGap;
|
||||
|
||||
final float horizontalGapFloat = mHorizontalGap;
|
||||
final int rowHeight = row.getRowHeight();
|
||||
mHeight = rowHeight - mVerticalGap;
|
||||
|
||||
xPos = row.getKeyX(keyAttr);
|
||||
final float keyWidth = row.getKeyWidth(keyAttr, xPos);
|
||||
yPos = row.getKeyY();
|
||||
|
||||
mWidth = Math.round(keyWidth - horizontalGapFloat);
|
||||
// Update row to have current x coordinate.
|
||||
row.setXPos(xPos + keyWidth);
|
||||
|
||||
mBackgroundType = style.getInt(keyAttr,
|
||||
R.styleable.Keyboard_Key_backgroundType, row.getDefaultBackgroundType());
|
||||
|
||||
final int baseWidth = params.mBaseWidth;
|
||||
final int visualInsetsLeft = Math.round(keyAttr.getFraction(
|
||||
R.styleable.Keyboard_Key_visualInsetsLeft, baseWidth, baseWidth, 0));
|
||||
final int visualInsetsRight = Math.round(keyAttr.getFraction(
|
||||
R.styleable.Keyboard_Key_visualInsetsRight, baseWidth, baseWidth, 0));
|
||||
|
||||
mLabelFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags)
|
||||
| row.getDefaultKeyLabelFlags();
|
||||
final boolean needsToUpcase = needsToUpcase(mLabelFlags, params.mId.mElementId);
|
||||
final Locale localeForUpcasing = params.mId.getLocale();
|
||||
int actionFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
|
||||
String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
|
||||
|
||||
// Get maximum column order number and set a relevant mode value.
|
||||
int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER
|
||||
| style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn,
|
||||
params.mMaxMoreKeysKeyboardColumn);
|
||||
int value;
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY;
|
||||
}
|
||||
mMoreKeysColumnAndFlags = moreKeysColumnAndFlags;
|
||||
|
||||
final String[] additionalMoreKeys;
|
||||
if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) {
|
||||
additionalMoreKeys = null;
|
||||
} else {
|
||||
additionalMoreKeys = style.getStringArray(keyAttr,
|
||||
R.styleable.Keyboard_Key_additionalMoreKeys);
|
||||
}
|
||||
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
|
||||
if (moreKeys != null) {
|
||||
actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
|
||||
mMoreKeys = new MoreKeySpec[moreKeys.length];
|
||||
for (int i = 0; i < moreKeys.length; i++) {
|
||||
mMoreKeys[i] = new MoreKeySpec(moreKeys[i], needsToUpcase, localeForUpcasing);
|
||||
}
|
||||
} else {
|
||||
mMoreKeys = null;
|
||||
}
|
||||
mActionFlags = actionFlags;
|
||||
|
||||
mIconId = KeySpecParser.getIconId(keySpec);
|
||||
final int disabledIconId = KeySpecParser.getIconId(style.getString(keyAttr,
|
||||
R.styleable.Keyboard_Key_keyIconDisabled));
|
||||
|
||||
final int code = KeySpecParser.getCode(keySpec);
|
||||
if ((mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0) {
|
||||
mLabel = params.mId.mCustomActionLabel;
|
||||
} else if (code >= Character.MIN_SUPPLEMENTARY_CODE_POINT) {
|
||||
// This is a workaround to have a key that has a supplementary code point in its label.
|
||||
// Because we can put a string in resource neither as a XML entity of a supplementary
|
||||
// code point nor as a surrogate pair.
|
||||
mLabel = new StringBuilder().appendCodePoint(code).toString();
|
||||
} else {
|
||||
final String label = KeySpecParser.getLabel(keySpec);
|
||||
mLabel = needsToUpcase
|
||||
? StringUtils.toTitleCaseOfKeyLabel(label, localeForUpcasing)
|
||||
: label;
|
||||
}
|
||||
if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) {
|
||||
mHintLabel = null;
|
||||
} else {
|
||||
final String hintLabel = style.getString(
|
||||
keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
|
||||
mHintLabel = needsToUpcase
|
||||
? StringUtils.toTitleCaseOfKeyLabel(hintLabel, localeForUpcasing)
|
||||
: hintLabel;
|
||||
}
|
||||
String outputText = KeySpecParser.getOutputText(keySpec);
|
||||
if (needsToUpcase) {
|
||||
outputText = StringUtils.toTitleCaseOfKeyLabel(outputText, localeForUpcasing);
|
||||
}
|
||||
// Choose the first letter of the label as primary code if not specified.
|
||||
if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText)
|
||||
&& !TextUtils.isEmpty(mLabel)) {
|
||||
if (StringUtils.codePointCount(mLabel) == 1) {
|
||||
// Use the first letter of the hint label if shiftedLetterActivated flag is
|
||||
// specified.
|
||||
if ((mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0 && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0 && !TextUtils.isEmpty(mHintLabel)) {
|
||||
mCode = mHintLabel.codePointAt(0);
|
||||
} else {
|
||||
mCode = mLabel.codePointAt(0);
|
||||
}
|
||||
} else {
|
||||
// In some locale and case, the character might be represented by multiple code
|
||||
// points, such as upper case Eszett of German alphabet.
|
||||
outputText = mLabel;
|
||||
mCode = CODE_OUTPUT_TEXT;
|
||||
}
|
||||
} else if (code == CODE_UNSPECIFIED && outputText != null) {
|
||||
if (StringUtils.codePointCount(outputText) == 1) {
|
||||
mCode = outputText.codePointAt(0);
|
||||
outputText = null;
|
||||
} else {
|
||||
mCode = CODE_OUTPUT_TEXT;
|
||||
}
|
||||
} else {
|
||||
mCode = needsToUpcase ? StringUtils.toTitleCaseOfKeyCode(code, localeForUpcasing)
|
||||
: code;
|
||||
}
|
||||
final int altCodeInAttr = KeySpecParser.parseCode(
|
||||
style.getString(keyAttr, R.styleable.Keyboard_Key_altCode), CODE_UNSPECIFIED);
|
||||
final int altCode = needsToUpcase
|
||||
? StringUtils.toTitleCaseOfKeyCode(altCodeInAttr, localeForUpcasing)
|
||||
: altCodeInAttr;
|
||||
mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode,
|
||||
disabledIconId, visualInsetsLeft, visualInsetsRight);
|
||||
mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
|
||||
}
|
||||
|
||||
/** for <GridRows/> */
|
||||
public KeyParams(@Nullable final String label, final int code, @Nullable final String outputText,
|
||||
@Nullable final String hintLabel, @Nullable final String moreKeySpecs,
|
||||
final int labelFlags, final int backgroundType, final int x, final int y,
|
||||
final int width, final int height, final KeyboardParams params) {
|
||||
mWidth = width - params.mHorizontalGap;
|
||||
mHeight = height - params.mVerticalGap;
|
||||
mHorizontalGap = params.mHorizontalGap;
|
||||
mVerticalGap = params.mVerticalGap;
|
||||
mHintLabel = hintLabel;
|
||||
mLabelFlags = labelFlags;
|
||||
mBackgroundType = backgroundType;
|
||||
xPos = x;
|
||||
yPos = y;
|
||||
|
||||
if (moreKeySpecs != null) {
|
||||
String[] moreKeys = MoreKeySpec.splitKeySpecs(moreKeySpecs);
|
||||
// Get maximum column order number and set a relevant mode value.
|
||||
int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER
|
||||
| params.mMaxMoreKeysKeyboardColumn;
|
||||
int value;
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY;
|
||||
}
|
||||
mMoreKeysColumnAndFlags = moreKeysColumnAndFlags;
|
||||
|
||||
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, null);
|
||||
int actionFlags = 0;
|
||||
if (moreKeys != null) {
|
||||
actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
|
||||
mMoreKeys = new MoreKeySpec[moreKeys.length];
|
||||
for (int i = 0; i < moreKeys.length; i++) {
|
||||
mMoreKeys[i] = new MoreKeySpec(moreKeys[i], false, Locale.getDefault());
|
||||
}
|
||||
} else {
|
||||
mMoreKeys = null;
|
||||
}
|
||||
mActionFlags = actionFlags;
|
||||
} else {
|
||||
// TODO: Pass keyActionFlags as an argument.
|
||||
mActionFlags = ACTION_FLAGS_NO_KEY_PREVIEW;
|
||||
mMoreKeys = null;
|
||||
mMoreKeysColumnAndFlags = 0;
|
||||
}
|
||||
|
||||
mLabel = label;
|
||||
mOptionalAttributes = OptionalAttributes.newInstance(outputText, CODE_UNSPECIFIED,
|
||||
ICON_UNDEFINED, 0 /* visualInsetsLeft */, 0 /* visualInsetsRight */);
|
||||
mCode = code;
|
||||
mEnabled = (code != CODE_UNSPECIFIED);
|
||||
mIconId = KeyboardIconsSet.ICON_UNDEFINED;
|
||||
mKeyVisualAttributes = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import android.util.Xml;
|
|||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
|
||||
import org.dslul.openboard.inputmethod.compat.EditorInfoCompatUtils;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardBuilder;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardParams;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.UniqueKeysCache;
|
||||
|
@ -217,7 +216,7 @@ public final class KeyboardLayoutSet {
|
|||
final KeyboardBuilder<KeyboardParams> builder =
|
||||
new KeyboardBuilder<>(mContext, new KeyboardParams(sUniqueKeysCache));
|
||||
sUniqueKeysCache.setEnabled(id.isAlphabetKeyboard());
|
||||
builder.setAllowRedundantMoreKes(elementParams.mAllowRedundantMoreKeys);
|
||||
builder.setAllowRedundantMoreKeys(elementParams.mAllowRedundantMoreKeys);
|
||||
final int keyboardXmlId = elementParams.mKeyboardXmlId;
|
||||
builder.load(keyboardXmlId, id);
|
||||
if (mParams.mDisableTouchPositionCorrectionDataForTest) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.xmlpull.v1.XmlPullParser;
|
|||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
|
@ -135,10 +136,13 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
protected final Resources mResources;
|
||||
|
||||
private int mCurrentY = 0;
|
||||
// currently not used, but will be relevant when resizing a row or inserting a new key
|
||||
private float mCurrentX = 0f;
|
||||
private KeyboardRow mCurrentRow = null;
|
||||
private boolean mLeftEdge;
|
||||
private boolean mTopEdge;
|
||||
private Key mRightEdgeKey = null;
|
||||
private final ArrayList<ArrayList<Key.KeyParams>> keysInRows = new ArrayList<>();
|
||||
|
||||
public KeyboardBuilder(final Context context, @NonNull final KP params) {
|
||||
mContext = context;
|
||||
|
@ -151,10 +155,15 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
|
||||
}
|
||||
|
||||
public void setAllowRedundantMoreKes(final boolean enabled) {
|
||||
public void setAllowRedundantMoreKeys(final boolean enabled) {
|
||||
mParams.mAllowRedundantMoreKeys = enabled;
|
||||
}
|
||||
|
||||
// todo:
|
||||
// split the parser from the builder
|
||||
// parser should just setup the params (parseKeyboardAttributes)
|
||||
// and return something like keysInRows
|
||||
// then builder can nicely adjust the keyboard, maybe dimensions, maybe insert keys/spacers
|
||||
public KeyboardBuilder<KP> load(final int xmlId, final KeyboardId id) {
|
||||
mParams.mId = id;
|
||||
try (XmlResourceParser parser = mResources.getXml(xmlId)) {
|
||||
|
@ -180,6 +189,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
|
||||
@NonNull
|
||||
public Keyboard build() {
|
||||
addKeysToParams();
|
||||
return new Keyboard(mParams);
|
||||
}
|
||||
|
||||
|
@ -402,6 +412,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
R.styleable.Keyboard_GridRows_textsArray, 0);
|
||||
final int moreCodesArrayId = gridRowAttr.getResourceId(
|
||||
R.styleable.Keyboard_GridRows_moreCodesArray, 0);
|
||||
// todo: read relative key width, key / row height and gaps (but they might also be absolute, see getDimensionOrFraction)
|
||||
gridRowAttr.recycle();
|
||||
if (codesArrayId == 0 && textsArrayId == 0) {
|
||||
throw new XmlParseUtils.ParseException(
|
||||
|
@ -429,6 +440,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
for (int index = 0; index < counts; index += numColumns) {
|
||||
final KeyboardRow row = new KeyboardRow(mResources, mParams, parser, mCurrentY);
|
||||
startRow(row);
|
||||
final ArrayList<Key.KeyParams> keyParamsRow = keysInRows.get(keysInRows.size() - 1);
|
||||
for (int c = 0; c < numColumns; c++) {
|
||||
final int i = index + c;
|
||||
if (i >= counts) {
|
||||
|
@ -468,9 +480,10 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
final int width = (int)keyWidth;
|
||||
final int height = row.getRowHeight();
|
||||
final String hintLabel = moreKeySpecs != null ? "\u25E5" : null;
|
||||
final Key key = new Key(label, code, outputText, hintLabel, moreKeySpecs,
|
||||
final Key.KeyParams key = new Key.KeyParams(label, code, outputText, hintLabel, moreKeySpecs,
|
||||
labelFlags, backgroundType, x, y, width, height, mParams);
|
||||
endKey(key);
|
||||
// todo: add relative width and others
|
||||
keyParamsRow.add(key);
|
||||
row.advanceXPos(keyWidth);
|
||||
}
|
||||
endRow(row);
|
||||
|
@ -493,14 +506,15 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
if (TextUtils.isEmpty(keySpec)) {
|
||||
throw new ParseException("Empty keySpec", parser);
|
||||
}
|
||||
final Key key = new Key(keySpec, keyAttr, keyStyle, mParams, row);
|
||||
final Key.KeyParams key = new Key.KeyParams(keySpec, keyAttr, keyStyle, mParams, row);
|
||||
keyAttr.recycle();
|
||||
// todo: add relative width and others
|
||||
if (DEBUG) {
|
||||
startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY, (key.isEnabled() ? "" : " disabled"),
|
||||
key, Arrays.toString(key.getMoreKeys()));
|
||||
startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY, (key.mEnabled ? "" : " disabled"),
|
||||
key, Arrays.toString(key.mMoreKeys));
|
||||
}
|
||||
XmlParseUtils.checkEndTag(TAG_KEY, parser);
|
||||
endKey(key);
|
||||
keysInRows.get(keysInRows.size() - 1).add(key);
|
||||
}
|
||||
|
||||
private void parseSpacer(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
|
||||
|
@ -513,11 +527,12 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
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);
|
||||
final Key.KeyParams spacer = Key.KeyParams.newSpacer(keyAttr, keyStyle, mParams, row);
|
||||
keyAttr.recycle();
|
||||
// todo: add relative width and others
|
||||
keysInRows.get(keysInRows.size() - 1).add(spacer);
|
||||
if (DEBUG) startEndTag("<%s />", TAG_SPACER);
|
||||
XmlParseUtils.checkEndTag(TAG_SPACER, parser);
|
||||
endKey(spacer);
|
||||
}
|
||||
|
||||
private void parseIncludeKeyboardContent(final XmlPullParser parser, final boolean skip)
|
||||
|
@ -861,6 +876,13 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
mCurrentRow = row;
|
||||
mLeftEdge = true;
|
||||
mRightEdgeKey = null;
|
||||
keysInRows.add(new ArrayList<>());
|
||||
}
|
||||
|
||||
private void startRowNew() {
|
||||
addEdgeSpaceNew(mParams.mLeftPadding);
|
||||
mLeftEdge = true;
|
||||
mRightEdgeKey = null;
|
||||
}
|
||||
|
||||
private void endRow(final KeyboardRow row) {
|
||||
|
@ -877,6 +899,18 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
mTopEdge = false;
|
||||
}
|
||||
|
||||
private void endRowNew() {
|
||||
int lastKeyHeight = 0;
|
||||
if (mRightEdgeKey != null) {
|
||||
mRightEdgeKey.markAsRightEdge(mParams);
|
||||
lastKeyHeight = mRightEdgeKey.getHeight() + mRightEdgeKey.getVerticalGap();
|
||||
mRightEdgeKey = null;
|
||||
}
|
||||
addEdgeSpaceNew(mParams.mRightPadding);
|
||||
mCurrentY += lastKeyHeight;
|
||||
mTopEdge = false;
|
||||
}
|
||||
|
||||
private void endKey(@NonNull final Key key) {
|
||||
mParams.onAddKey(key);
|
||||
if (mLeftEdge) {
|
||||
|
@ -897,12 +931,33 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
mParams.mOccupiedHeight = Math.max(mParams.mOccupiedHeight, actualHeight);
|
||||
}
|
||||
|
||||
private void addKeysToParams() {
|
||||
// need to reset it, we need to sum it up to get the height nicely
|
||||
// (though in the end we could just not touch it at all, final used value is the same as the one before resetting)
|
||||
mCurrentY = 0;
|
||||
startKeyboard();
|
||||
for (ArrayList<Key.KeyParams> row : keysInRows) {
|
||||
startRowNew();
|
||||
for (Key.KeyParams keyParams : row) {
|
||||
endKey(keyParams.createKey());
|
||||
}
|
||||
endRowNew();
|
||||
}
|
||||
endKeyboard();
|
||||
}
|
||||
|
||||
private void addEdgeSpace(final float width, final KeyboardRow row) {
|
||||
row.advanceXPos(width);
|
||||
mLeftEdge = false;
|
||||
mRightEdgeKey = null;
|
||||
}
|
||||
|
||||
private void addEdgeSpaceNew(final float width) {
|
||||
mCurrentX += width;
|
||||
mLeftEdge = false;
|
||||
mRightEdgeKey = null;
|
||||
}
|
||||
|
||||
private static String textAttr(final String value, final String name) {
|
||||
return value != null ? String.format(" %s=%s", name, value) : "";
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue