Allow splitting all layouts instead of having to define split layouts in xml (#258)

This commit is contained in:
Helium314 2023-11-03 12:28:00 +01:00 committed by GitHub
parent 778f160c07
commit 1c0fb0c672
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 442 additions and 290 deletions

View file

@ -282,18 +282,24 @@ public class Key implements Comparable<Key> {
mEnabled = keyParams.mEnabled;
// stuff to create
mWidth = keyParams.mWidth;
mHeight = keyParams.mHeight;
// interestingly it looks a little better when rounding horizontalGap to int immediately instead of using horizontalGapFloat
// but using float for determining mX and mWidth is more correct and keyboard ends up looking exactly like before introduction of KeyParams
final float horizontalGapFloat = isSpacer() ? 0 : keyParams.mKeyboardParams.mHorizontalGap;
mHorizontalGap = Math.round(horizontalGapFloat);
mVerticalGap = Math.round(keyParams.mKeyboardParams.mVerticalGap);
mWidth = Math.round(keyParams.mFullWidth - horizontalGapFloat);
// todo (later): height better should be rounded, but this may end up shifting all keys up by one pixel,
// increasing the keyboard height by one pixel, but not for emoji keyboard -> the 1 pixel shift feels very wrong
// how to do it properly? check again when keyboard height is same for all views!
mHeight = (int) (keyParams.mFullHeight - keyParams.mKeyboardParams.mVerticalGap);
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);
mX = Math.round(keyParams.xPos + horizontalGapFloat / 2);
mY = Math.round(keyParams.yPos);
mHitBox.set(Math.round(keyParams.xPos), (int) keyParams.yPos, Math.round(keyParams.xPos + keyParams.mFullWidth) + 1,
Math.round(keyParams.yPos + keyParams.mFullHeight));
mHashCode = computeHashCode(this);
}
@ -937,32 +943,30 @@ public class Key implements Comparable<Key> {
// for creating keys that might get modified later
public static class KeyParams {
// params for building
boolean isSpacer;
private final KeyboardParams mParams; // for reading gaps and keyboard width / height
public float mRelativeWidth; // also allows -1f as value, this means "fill right"
public float mRelativeHeight; // also allows negative values, indicating absolute height is defined
private boolean isSpacer;
private final KeyboardParams mKeyboardParams; // for reading gaps and keyboard width / height
public float mRelativeWidth;
public float mRelativeHeight; // also should allow negative values, indicating absolute height is defined
// stuff that likely remains after constructor, maybe make final
final int mCode;
// params that may change
public float mFullWidth;
public float mFullHeight;
public float xPos;
public float yPos;
// params that remains constant
public 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;
public final int mBackgroundType;
final int mActionFlags;
@Nullable final KeyVisualAttributes mKeyVisualAttributes;
@Nullable final OptionalAttributes mOptionalAttributes;
public boolean mEnabled = true;
// stuff that may very well change
private int mWidth;
private int mHeight;
private int mHorizontalGap;
private int mVerticalGap;
private float xPos;
private int yPos;
public final boolean mEnabled;
public static KeyParams newSpacer(final TypedArray keyAttr, final KeyStyle keyStyle,
final KeyboardParams params, final XmlKeyboardRow row) {
@ -971,36 +975,25 @@ public class Key implements Comparable<Key> {
return keyParams;
}
public static KeyParams newSpacer(final KeyboardParams params) {
return new KeyParams(params);
}
public Key createKey() {
if (isSpacer) return new Spacer(this);
return new Key(this);
}
// todo: use it
// first for inserting spacers to get a split keyboard
// any use for adjusting width or height?
// width is already more or less done with one-handed mode, but this could be more flexible
// height is already implemented via the setting
// any use in combination with number row?
// when completely replacing number row stuff, also moreKeys stuff would need to be adjusted
public void setDimensionsFromRelativeSize(final int newX, final int newY) {
public void setDimensionsFromRelativeSize(final float newX, final float newY) {
if (mRelativeHeight == 0 || mRelativeWidth == 0)
throw new IllegalStateException("can't use setUsingRelativeHeight, not all fields are set");
if (mRelativeHeight < 0)
throw new IllegalStateException("can't (yet) deal with absolute height"); // todo: decide... maybe just use it and deal with it properly when it needs to be adjusted?
// todo (later): deal with it properly when it needs to be adjusted, i.e. when changing moreKeys or moreSuggestions
throw new IllegalStateException("can't (yet) deal with absolute height");
xPos = newX;
yPos = newY;
float horizontalGap = isSpacer ? 0f : mParams.mRelativeHorizontalGap * mParams.mId.mWidth; // gap width / height is based on params.mId.height / width
float verticalGap = mParams.mRelativeVerticalGap * mParams.mId.mHeight;
mHorizontalGap = (int) horizontalGap;
mVerticalGap = (int) verticalGap;
float keyWidth;
if (mRelativeWidth > 0)
keyWidth = mRelativeWidth * mParams.mBaseWidth; // key width / height is based on params.mBaseHeight / width
else // fillRight
keyWidth = (mParams.mOccupiedWidth - mParams.mRightPadding) - xPos; // right keyboard edge - x
mWidth = Math.round(keyWidth - horizontalGap);
mHeight = (int) (mRelativeHeight * mParams.mBaseHeight - verticalGap);
mFullWidth = mRelativeWidth * mKeyboardParams.mBaseWidth;
mFullHeight = mRelativeHeight * mKeyboardParams.mBaseHeight;
}
/**
@ -1017,26 +1010,23 @@ public class Key implements Comparable<Key> {
public KeyParams(@Nullable final String keySpec, @NonNull final TypedArray keyAttr,
@NonNull final KeyStyle style, @NonNull final KeyboardParams params,
@NonNull final XmlKeyboardRow row) {
mParams = params;
mKeyboardParams = params;
mRelativeHeight = row.mRelativeRowHeight;
mRelativeWidth = row.getRelativeKeyWidth(keyAttr);
mHorizontalGap = params.mHorizontalGap;
mVerticalGap = params.mVerticalGap;
final float horizontalGapFloat = mHorizontalGap;
final int rowHeight = row.getRowHeight();
mHeight = rowHeight - mVerticalGap;
mFullHeight = row.getRowHeight();
xPos = row.getKeyX(keyAttr);
final float keyWidth = row.getKeyWidth(keyAttr, xPos);
mFullWidth = row.getKeyWidth(keyAttr, xPos);
if (mRelativeWidth == -1f) {
// determine from actual width if using fillRight
mRelativeWidth = mFullWidth / mKeyboardParams.mBaseWidth;
}
yPos = row.getKeyY();
mWidth = Math.round(keyWidth - horizontalGapFloat);
// Update row to have current x coordinate.
row.setXPos(xPos + keyWidth);
row.setXPos(xPos + mFullWidth);
mBackgroundType = style.getInt(keyAttr,
R.styleable.Keyboard_Key_backgroundType, row.getDefaultBackgroundType());
mBackgroundType = style.getInt(keyAttr, R.styleable.Keyboard_Key_backgroundType, row.getDefaultBackgroundType());
final int baseWidth = params.mBaseWidth;
final int visualInsetsLeft = Math.round(keyAttr.getFraction(
@ -1058,13 +1048,11 @@ public class Key implements Comparable<Key> {
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);
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);
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;
@ -1081,8 +1069,7 @@ public class Key implements Comparable<Key> {
if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) {
additionalMoreKeys = null;
} else {
additionalMoreKeys = style.getStringArray(keyAttr,
R.styleable.Keyboard_Key_additionalMoreKeys);
additionalMoreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys);
}
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
if (moreKeys != null) {
@ -1117,8 +1104,7 @@ public class Key implements Comparable<Key> {
if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) {
mHintLabel = null;
} else {
final String hintLabel = style.getString(
keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
final String hintLabel = style.getString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
mHintLabel = needsToUpcase
? StringUtils.toTitleCaseOfKeyLabel(hintLabel, localeForUpcasing)
: hintLabel;
@ -1128,12 +1114,12 @@ public class Key implements Comparable<Key> {
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 (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)) {
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);
@ -1152,8 +1138,7 @@ public class Key implements Comparable<Key> {
mCode = CODE_OUTPUT_TEXT;
}
} else {
mCode = needsToUpcase ? StringUtils.toTitleCaseOfKeyCode(code, localeForUpcasing)
: code;
mCode = needsToUpcase ? StringUtils.toTitleCaseOfKeyCode(code, localeForUpcasing) : code;
}
final int altCodeInAttr = KeySpecParser.parseCode(
style.getString(keyAttr, R.styleable.Keyboard_Key_altCode), CODE_UNSPECIFIED);
@ -1163,6 +1148,7 @@ public class Key implements Comparable<Key> {
mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode,
disabledIconId, visualInsetsLeft, visualInsetsRight);
mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
mEnabled = true;
}
/** for <GridRows/> */
@ -1170,11 +1156,9 @@ public class Key implements Comparable<Key> {
@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) {
mParams = params;
mWidth = width - params.mHorizontalGap;
mHeight = height - params.mVerticalGap;
mHorizontalGap = params.mHorizontalGap;
mVerticalGap = params.mVerticalGap;
mKeyboardParams = params;
mFullWidth = width;
mFullHeight = height;
mHintLabel = hintLabel;
mLabelFlags = labelFlags;
mBackgroundType = backgroundType;
@ -1184,8 +1168,7 @@ public class Key implements Comparable<Key> {
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 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.
@ -1194,8 +1177,7 @@ public class Key implements Comparable<Key> {
}
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);
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;
@ -1235,5 +1217,48 @@ public class Key implements Comparable<Key> {
mIconId = KeyboardIconsSet.ICON_UNDEFINED;
mKeyVisualAttributes = null;
}
/** constructor for a spacer whose size MUST be determined using setDimensionsFromRelativeSize */
private KeyParams(final KeyboardParams params) {
isSpacer = true; // this is only for spacer!
mKeyboardParams = params;
mCode = CODE_UNSPECIFIED;
mLabel = null;
mHintLabel = null;
mKeyVisualAttributes = null;
mOptionalAttributes = null;
mIconId = KeyboardIconsSet.ICON_UNDEFINED;
mBackgroundType = BACKGROUND_TYPE_NORMAL;
mActionFlags = ACTION_FLAGS_NO_KEY_PREVIEW;
mMoreKeys = null;
mMoreKeysColumnAndFlags = 0;
mLabelFlags = LABEL_FLAGS_FONT_NORMAL;
mEnabled = true;
}
public KeyParams(final KeyParams keyParams) {
xPos = keyParams.xPos;
yPos = keyParams.yPos;
mRelativeWidth = keyParams.mRelativeWidth;
mRelativeHeight = keyParams.mRelativeHeight;
isSpacer = keyParams.isSpacer;
mKeyboardParams = keyParams.mKeyboardParams;
mEnabled = keyParams.mEnabled;
mCode = keyParams.mCode;
mLabel = keyParams.mLabel;
mHintLabel = keyParams.mHintLabel;
mLabelFlags = keyParams.mLabelFlags;
mIconId = keyParams.mIconId;
mFullWidth = keyParams.mFullWidth;
mFullHeight = keyParams.mFullHeight;
mMoreKeys = keyParams.mMoreKeys;
mMoreKeysColumnAndFlags = keyParams.mMoreKeysColumnAndFlags;
mBackgroundType = keyParams.mBackgroundType;
mActionFlags = keyParams.mActionFlags;
mKeyVisualAttributes = keyParams.mKeyVisualAttributes;
mOptionalAttributes = keyParams.mOptionalAttributes;
}
}
}

View file

@ -457,8 +457,7 @@ public final class KeyboardLayoutSet {
elementParams.mProximityCharsCorrectionEnabled = a.getBoolean(
R.styleable.KeyboardLayoutSet_Element_enableProximityCharsCorrection,
false);
elementParams.mSupportsSplitLayout = a.getBoolean(
R.styleable.KeyboardLayoutSet_Element_supportsSplitLayout, false);
elementParams.mSupportsSplitLayout = false; // this is to avoid xml parser reading split layouts, todo (later): remove mSupportsSplitLayout
elementParams.mAllowRedundantMoreKeys = a.getBoolean(
R.styleable.KeyboardLayoutSet_Element_allowRedundantMoreKeys, true);
mParams.mKeyboardLayoutSetElementIdToParamsMap.put(elementName, elementParams);

View file

@ -1,152 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
* modified
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
*/
package org.dslul.openboard.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import androidx.annotation.NonNull;
import org.dslul.openboard.inputmethod.annotations.UsedForTesting;
import org.dslul.openboard.inputmethod.keyboard.Key;
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
import org.dslul.openboard.inputmethod.keyboard.KeyboardId;
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.XmlKeyboardParser;
import org.dslul.openboard.inputmethod.latin.R;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
// TODO: Write unit tests for this class.
public class KeyboardBuilder<KP extends KeyboardParams> {
private static final String BUILDER_TAG = "Keyboard.Builder";
@NonNull
protected final KP mParams;
protected final Context mContext;
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 boolean mLeftEdge;
private boolean mTopEdge;
private Key mRightEdgeKey = null;
private ArrayList<ArrayList<Key.KeyParams>> keysInRows;
public KeyboardBuilder(final Context context, @NonNull final KP params) {
mContext = context;
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 setAllowRedundantMoreKeys(final boolean enabled) {
mParams.mAllowRedundantMoreKeys = enabled;
}
public KeyboardBuilder<KP> loadFromXml(final int xmlId, final KeyboardId id) {
mParams.mId = id;
// loading a keyboard should set default params like mParams.readAttributes(mContext, attrs);
// attrs may be null, then default values are used (looks good for "normal" keyboards)
try (XmlKeyboardParser keyboardParser = new XmlKeyboardParser(xmlId, mParams, mContext)) {
keysInRows = keyboardParser.parseKeyboard();
} 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);
}
return this;
}
@UsedForTesting
public void disableTouchPositionCorrectionDataForTest() {
mParams.mTouchPositionCorrection.setEnabled(false);
}
public void setProximityCharsCorrectionEnabled(final boolean enabled) {
mParams.mProximityCharsCorrectionEnabled = enabled;
}
@NonNull
public Keyboard build() {
addKeysToParams();
return new Keyboard(mParams);
}
private void startKeyboard() {
mCurrentY += mParams.mTopPadding;
mTopEdge = true;
}
private void startRow() {
addEdgeSpace(mParams.mLeftPadding);
mLeftEdge = true;
mRightEdgeKey = null;
}
private void endRow() {
int lastKeyHeight = 0;
if (mRightEdgeKey != null) {
mRightEdgeKey.markAsRightEdge(mParams);
lastKeyHeight = mRightEdgeKey.getHeight() + mRightEdgeKey.getVerticalGap();
mRightEdgeKey = null;
}
addEdgeSpace(mParams.mRightPadding);
mCurrentY += lastKeyHeight;
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 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) {
startRow();
for (Key.KeyParams keyParams : row) {
endKey(keyParams.createKey());
}
endRow();
}
endKeyboard();
}
private void addEdgeSpace(final float width) {
mCurrentX += width;
mLeftEdge = false;
mRightEdgeKey = null;
}
}

View file

@ -0,0 +1,279 @@
/*
* Copyright (C) 2012 The Android Open Source Project
* modified
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
*/
package org.dslul.openboard.inputmethod.keyboard.internal
import android.content.Context
import android.content.res.Resources
import android.util.Log
import org.dslul.openboard.inputmethod.annotations.UsedForTesting
import org.dslul.openboard.inputmethod.keyboard.Key
import org.dslul.openboard.inputmethod.keyboard.Key.KeyParams
import org.dslul.openboard.inputmethod.keyboard.Keyboard
import org.dslul.openboard.inputmethod.keyboard.KeyboardId
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.XmlKeyboardParser
import org.dslul.openboard.inputmethod.latin.R
import org.dslul.openboard.inputmethod.latin.common.Constants
import org.dslul.openboard.inputmethod.latin.settings.Settings
import org.xmlpull.v1.XmlPullParserException
import java.io.IOException
// TODO: Write unit tests for this class.
open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context, @JvmField val mParams: KP) {
@JvmField
protected val mResources: Resources
private var mCurrentY = 0
private var mLeftEdge = false
private var mTopEdge = false
private var mRightEdgeKey: Key? = null
private lateinit var keysInRows: ArrayList<ArrayList<KeyParams>>
init {
val res = mContext.resources
mResources = res
mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width)
mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height)
}
fun setAllowRedundantMoreKeys(enabled: Boolean) {
mParams.mAllowRedundantMoreKeys = enabled
}
fun loadFromXml(xmlId: Int, id: KeyboardId): KeyboardBuilder<KP> {
mParams.mId = id
// loading a keyboard should set default params like mParams.readAttributes(mContext, attrs);
// attrs may be null, then default values are used (looks good for "normal" keyboards)
try {
XmlKeyboardParser(xmlId, mParams, mContext).use { keyboardParser ->
keysInRows = keyboardParser.parseKeyboard()
}
} catch (e: XmlPullParserException) {
Log.w(BUILDER_TAG, "keyboard XML parse error", e)
throw IllegalArgumentException(e.message, e)
} catch (e: IOException) {
Log.w(BUILDER_TAG, "keyboard XML parse error", e)
throw RuntimeException(e.message, e)
}
return this
}
@UsedForTesting
fun disableTouchPositionCorrectionDataForTest() {
mParams.mTouchPositionCorrection.setEnabled(false)
}
fun setProximityCharsCorrectionEnabled(enabled: Boolean) {
mParams.mProximityCharsCorrectionEnabled = enabled
}
open fun build(): Keyboard {
if (Settings.getInstance().current.mIsSplitKeyboardEnabled
&& mParams.mId.mElementId in KeyboardId.ELEMENT_ALPHABET..KeyboardId.ELEMENT_SYMBOLS_SHIFTED) {
addSplit()
}
addKeysToParams()
return Keyboard(mParams)
}
// resize keyboard using relative params
// ideally this should not change anything
// but it does a little, depending on how float -> int is done (cast or round, and when to sum up gaps and width)
// still should not be more than a pixel difference
// keep it around for a while, for testing
private fun useRelative() {
for (row in keysInRows) {
if (row.isEmpty()) continue
fillGapsWithSpacers(row)
val y = row.first().yPos
assert(row.all { it.yPos == y })
var currentX = 0f
row.forEach {
it.setDimensionsFromRelativeSize(currentX, y)
currentX += it.mFullWidth
}
}
}
// necessary for adjusting widths and positions properly
// without adding spacers whose width can then be adjusted, we would have to deal with keyXPos,
// which is more complicated than expected
private fun fillGapsWithSpacers(row: MutableList<KeyParams>) {
if (mParams.mId.mElementId !in KeyboardId.ELEMENT_ALPHABET..KeyboardId.ELEMENT_SYMBOLS_SHIFTED) return
if (row.isEmpty()) return
var currentX = 0f + mParams.mLeftPadding
var i = 0
while (i < row.size) {
val currentKeyXPos = row[i].xPos
if (currentKeyXPos > currentX) {
// insert spacer
val spacer = KeyParams.newSpacer(mParams)
spacer.mRelativeWidth = (currentKeyXPos - currentX) / mParams.mBaseWidth
spacer.yPos = row[i].yPos
spacer.mRelativeHeight = row[i].mRelativeHeight
row.add(i, spacer)
i++
currentX += currentKeyXPos - currentX
}
currentX += row[i].mFullWidth
i++
}
if (currentX < mParams.mOccupiedWidth) {
// insert spacer
val spacer = KeyParams.newSpacer(mParams)
spacer.mRelativeWidth = (mParams.mOccupiedWidth - currentX) / mParams.mBaseWidth
spacer.mRelativeHeight = row.last().mRelativeHeight
spacer.yPos = row.last().yPos
row.add(spacer)
}
}
private fun addSplit() {
val spacerRelativeWidth = Settings.getInstance().current.mSpacerRelativeWidth
// adjust gaps for the whole keyboard, so it's the same for all rows
// todo: maybe remove? not sure if narrower gaps are desirable
mParams.mRelativeHorizontalGap *= 1f / (1f + spacerRelativeWidth)
mParams.mHorizontalGap = (mParams.mRelativeHorizontalGap * mParams.mId.mWidth).toInt()
var maxWidthBeforeSpacer = 0f
var maxWidthAfterSpacer = 0f
for (row in keysInRows) {
fillGapsWithSpacers(row)
val y = row.first().yPos // all have the same y, so this is fine
val relativeWidthSum = row.sumOf { it.mRelativeWidth } // sum up relative widths
val spacer = KeyParams.newSpacer(mParams)
spacer.mRelativeWidth = spacerRelativeWidth
spacer.mRelativeHeight = row.first().mRelativeHeight
// insert spacer before first key that starts right of the center (also consider gap)
var insertIndex = row.indexOfFirst { it.xPos > mParams.mOccupiedWidth / 2 }
.takeIf { it > -1 } ?: (row.size / 2) // fallback should never be needed, but better than having an error
if (row.any { it.mCode == Constants.CODE_SPACE }) {
val spaceLeft = row.single { it.mCode == Constants.CODE_SPACE }
reduceSymbolAndActionKeyWidth(row)
insertIndex = row.indexOf(spaceLeft) + 1
val widthBeforeSpace = row.subList(0, insertIndex - 1).sumOf { it.mRelativeWidth }
val widthAfterSpace = row.subList(insertIndex, row.size).sumOf { it.mRelativeWidth }
val spaceLeftWidth = (maxWidthBeforeSpacer - widthBeforeSpace).coerceAtLeast(mParams.mDefaultRelativeKeyWidth)
val spaceRightWidth = (maxWidthAfterSpacer - widthAfterSpace).coerceAtLeast(mParams.mDefaultRelativeKeyWidth)
val spacerWidth = spaceLeft.mRelativeWidth + spacerRelativeWidth - spaceLeftWidth - spaceRightWidth
if (spacerWidth > 0.05f) {
// only insert if the spacer has a reasonable width
val spaceRight = KeyParams(spaceLeft)
spaceLeft.mRelativeWidth = spaceLeftWidth
spaceRight.mRelativeWidth = spaceRightWidth
spacer.mRelativeWidth = spacerWidth
row.add(insertIndex, spaceRight)
row.add(insertIndex, spacer)
} else {
// otherwise increase space width, so other keys are resized properly
spaceLeft.mRelativeWidth += spacerWidth
}
} else {
val widthBeforeSpacer = row.subList(0, insertIndex).sumOf { it.mRelativeWidth }
val widthAfterSpacer = row.subList(insertIndex, row.size).sumOf { it.mRelativeWidth }
maxWidthBeforeSpacer = maxWidthBeforeSpacer.coerceAtLeast(widthBeforeSpacer)
maxWidthAfterSpacer = maxWidthAfterSpacer.coerceAtLeast(widthAfterSpacer)
row.add(insertIndex, spacer)
}
// re-calculate relative widths
val relativeWidthSumNew = row.sumOf { it.mRelativeWidth }
val widthFactor = relativeWidthSum / relativeWidthSumNew
// re-calculate absolute sizes and positions
var currentX = 0f
row.forEach {
it.mRelativeWidth *= widthFactor
it.setDimensionsFromRelativeSize(currentX, y)
currentX += it.mFullWidth
}
}
}
// reduce width of symbol and action key if in the row, and add this width to space to keep other key size constant
private fun reduceSymbolAndActionKeyWidth(row: ArrayList<KeyParams>) {
val spaceKey = row.first { it.mCode == Constants.CODE_SPACE }
val symbolKey = row.firstOrNull { it.mCode == Constants.CODE_SWITCH_ALPHA_SYMBOL }
val symbolKeyWidth = symbolKey?.mRelativeWidth ?: 0f
if (symbolKeyWidth > mParams.mDefaultRelativeKeyWidth) {
val widthToChange = symbolKey!!.mRelativeWidth - mParams.mDefaultRelativeKeyWidth
symbolKey.mRelativeWidth -= widthToChange
spaceKey.mRelativeWidth += widthToChange
}
val actionKey = row.firstOrNull { it.mBackgroundType == Key.BACKGROUND_TYPE_ACTION }
val actionKeyWidth = actionKey?.mRelativeWidth ?: 0f
if (actionKeyWidth > mParams.mDefaultRelativeKeyWidth * 1.1f) { // allow it to stay a little wider
val widthToChange = actionKey!!.mRelativeWidth - mParams.mDefaultRelativeKeyWidth * 1.1f
actionKey.mRelativeWidth -= widthToChange
spaceKey.mRelativeWidth += widthToChange
}
}
private fun startKeyboard() {
mCurrentY += mParams.mTopPadding
mTopEdge = true
}
private fun startRow() {
mLeftEdge = true
mRightEdgeKey = null
}
private fun endRow() {
val rightEdgeKey = mRightEdgeKey
if (rightEdgeKey != null) {
rightEdgeKey.markAsRightEdge(mParams)
mCurrentY += rightEdgeKey.height + rightEdgeKey.verticalGap
mRightEdgeKey = null
}
mLeftEdge = false
mTopEdge = false
}
private fun endKey(key: Key) {
mParams.onAddKey(key)
if (mLeftEdge) {
key.markAsLeftEdge(mParams)
mLeftEdge = false
}
if (mTopEdge) {
key.markAsTopEdge(mParams)
}
mRightEdgeKey = key
}
private fun endKeyboard() {
mParams.removeRedundantMoreKeys()
// {@link #parseGridRows(XmlPullParser,boolean)} may populate keyboard rows higher than
// previously expected.
val actualHeight = mCurrentY - mParams.mVerticalGap + mParams.mBottomPadding
mParams.mOccupiedHeight = Math.max(mParams.mOccupiedHeight, actualHeight)
}
private fun 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 (row in keysInRows) {
startRow()
for (keyParams in row) {
endKey(keyParams.createKey())
}
endRow()
}
endKeyboard()
}
companion object {
private const val BUILDER_TAG = "Keyboard.Builder"
}
}
// adapted from Kotlin source: https://github.com/JetBrains/kotlin/blob/7a7d392b3470b38d42f80c896b7270678d0f95c3/libraries/stdlib/common/src/generated/_Collections.kt#L3004
private inline fun <T> Iterable<T>.sumOf(selector: (T) -> Float): Float {
var sum = 0f
for (element in this) {
sum += selector(element)
}
return sum
}

View file

@ -217,11 +217,10 @@ public class KeyboardParams {
mRightPadding = (int) keyboardAttr.getFraction(
R.styleable.Keyboard_keyboardRightPadding, width, width, 0);
final int baseWidth = mOccupiedWidth - mLeftPadding - mRightPadding;
mBaseWidth = baseWidth;
mBaseWidth = mOccupiedWidth - mLeftPadding - mRightPadding;
mDefaultRelativeKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
1, 1, 1f / DEFAULT_KEYBOARD_COLUMNS);
mDefaultKeyWidth = (int) (mDefaultRelativeKeyWidth * baseWidth);
mDefaultKeyWidth = (int) (mDefaultRelativeKeyWidth * mBaseWidth);
// todo: maybe settings should not be accessed from here?
if (Settings.getInstance().getCurrent().mNarrowKeyGaps) {
@ -241,15 +240,14 @@ public class KeyboardParams {
mHorizontalGap = (int) (mRelativeHorizontalGap * width);
mVerticalGap = (int) (mRelativeVerticalGap * height);
final int baseHeight = mOccupiedHeight - mTopPadding - mBottomPadding + mVerticalGap;
mBaseHeight = baseHeight;
mBaseHeight = mOccupiedHeight - mTopPadding - mBottomPadding + mVerticalGap;
mDefaultRelativeRowHeight = ResourceUtils.getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_rowHeight, 1, 1f / DEFAULT_KEYBOARD_ROWS);
if (mDefaultRelativeRowHeight > 1) { // can be absolute size, in that case will be > 1
mDefaultRowHeight = (int) mDefaultRelativeRowHeight;
mDefaultRelativeRowHeight *= -1; // make it negative when it's absolute
} else {
mDefaultRowHeight = (int) (mDefaultRelativeRowHeight * baseHeight);
mDefaultRowHeight = (int) (mDefaultRelativeRowHeight * mBaseHeight);
}
mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);

View file

@ -38,8 +38,8 @@ public final class XmlKeyboardRow {
// TODO: Add keyActionFlags.
private static class RowAttributes {
/** Default width of a key in this row. */
public final float mDefaultKeyWidth;
/** Default width of a key in this row, relative to keyboard width */
public final float mDefaultRelativeKeyWidth;
/** Default keyLabelFlags in this row. */
public final int mDefaultKeyLabelFlags;
/** Default backgroundType for this row */
@ -49,13 +49,11 @@ public final class XmlKeyboardRow {
* 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.
* @param defaultRelativeKeyWidth a default key width relative to keyboardWidth.
*/
public RowAttributes(final TypedArray keyAttr, final float defaultKeyWidth,
final int keyboardWidth) {
mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
keyboardWidth, keyboardWidth, defaultKeyWidth);
public RowAttributes(final TypedArray keyAttr, final float defaultRelativeKeyWidth) {
mDefaultRelativeKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
1, 1, defaultRelativeKeyWidth);
mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0);
mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
Key.BACKGROUND_TYPE_NORMAL);
@ -67,12 +65,10 @@ public final class XmlKeyboardRow {
*
* @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);
public RowAttributes(final TypedArray keyAttr, final RowAttributes defaultRowAttr) {
mDefaultRelativeKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
1, 1, defaultRowAttr.mDefaultRelativeKeyWidth);
mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0)
| defaultRowAttr.mDefaultKeyLabelFlags;
mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
@ -101,8 +97,7 @@ public final class XmlKeyboardRow {
}
final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_Key);
mRowAttributesStack.push(new RowAttributes(
keyAttr, params.mDefaultKeyWidth, params.mBaseWidth));
mRowAttributesStack.push(new RowAttributes(keyAttr, params.mDefaultRelativeKeyWidth));
keyAttr.recycle();
mCurrentY = y;
@ -114,8 +109,7 @@ public final class XmlKeyboardRow {
}
public void pushRowAttributes(final TypedArray keyAttr) {
final RowAttributes newAttributes = new RowAttributes(
keyAttr, mRowAttributesStack.peek(), mParams.mBaseWidth);
final RowAttributes newAttributes = new RowAttributes(keyAttr, mRowAttributesStack.peek());
mRowAttributesStack.push(newAttributes);
}
@ -123,8 +117,8 @@ public final class XmlKeyboardRow {
mRowAttributesStack.pop();
}
public float getDefaultKeyWidth() {
return mRowAttributesStack.peek().mDefaultKeyWidth;
public float getDefaultRelativeKeyWidth() {
return mRowAttributesStack.peek().mDefaultRelativeKeyWidth;
}
public int getDefaultKeyLabelFlags() {
@ -167,30 +161,24 @@ public final class XmlKeyboardRow {
public float getRelativeKeyWidth(final TypedArray keyAttr) {
if (keyAttr == null)
return mParams.mDefaultRelativeKeyWidth;
return getDefaultRelativeKeyWidth();
final int widthType = ResourceUtils.getEnumValue(keyAttr,
R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
if (widthType == KEYWIDTH_FILL_RIGHT)
return -1;
return -1f;
// else KEYWIDTH_NOT_ENUM
return keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
1, 1, mParams.mDefaultRelativeKeyWidth);
1, 1, getDefaultRelativeKeyWidth());
}
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:
final float relativeWidth = getRelativeKeyWidth(keyAttr);
if (relativeWidth == -1f) {
// 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());
}
}
return relativeWidth * mParams.mBaseWidth;
}
}

View file

@ -21,7 +21,6 @@ import java.util.*
* "Appearance" settings sub screen.
*/
class AppearanceSettingsFragment : SubScreenFragment() {
private var needsReload = false
private val stylePref: ListPreference by lazy { preferenceScreen.findPreference(Settings.PREF_THEME_STYLE)!! }
@ -30,17 +29,26 @@ class AppearanceSettingsFragment : SubScreenFragment() {
private val dayNightPref: TwoStatePreference? by lazy { preferenceScreen.findPreference(Settings.PREF_THEME_DAY_NIGHT) }
private val userColorsPref: Preference by lazy { preferenceScreen.findPreference("theme_select_colors")!! }
private val userColorsPrefNight: Preference? by lazy { preferenceScreen.findPreference("theme_select_colors_night") }
private val splitPref: TwoStatePreference? by lazy { preferenceScreen.findPreference(Settings.PREF_ENABLE_SPLIT_KEYBOARD) }
private val splitScalePref: Preference? by lazy { preferenceScreen.findPreference(Settings.PREF_SPLIT_SPACER_SCALE) }
override fun onCreate(icicle: Bundle?) {
super.onCreate(icicle)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.prefs_screen_appearance)
removeUnsuitablePreferences()
setupTheme()
setThemeVariantPrefs(sharedPreferences.getString(Settings.PREF_THEME_STYLE, KeyboardTheme.STYLE_MATERIAL)!!)
setupKeyboardHeight(Settings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
setupScalePrefs(Settings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
if (splitScalePref != null) {
setupScalePrefs(Settings.PREF_SPLIT_SPACER_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
splitScalePref?.isVisible = splitPref?.isChecked == true
splitPref?.setOnPreferenceChangeListener { _, value ->
splitScalePref?.isVisible = value as Boolean
true
}
}
}
override fun onPause() {
@ -52,7 +60,7 @@ class AppearanceSettingsFragment : SubScreenFragment() {
override fun onSharedPreferenceChanged(prefs: SharedPreferences?, key: String?) {
super.onSharedPreferenceChanged(prefs, key)
needsReload = true // may not always be the necessary, but that's ok
needsReload = true // may not always necessary, but that's ok
}
private fun removeUnsuitablePreferences() {
@ -79,6 +87,7 @@ class AppearanceSettingsFragment : SubScreenFragment() {
val heightDp = metrics.heightPixels / metrics.density
if (!ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED || (min(widthDp, heightDp) < 600 && max(widthDp, heightDp) < 720)) {
removePreference(Settings.PREF_ENABLE_SPLIT_KEYBOARD)
removePreference(Settings.PREF_SPLIT_SPACER_SCALE)
}
}
@ -140,7 +149,7 @@ class AppearanceSettingsFragment : SubScreenFragment() {
userColorsPrefNight?.isVisible = dayNightPref?.isChecked == true && colorsNightPref?.value == KeyboardTheme.THEME_USER_NIGHT
}
private fun setupKeyboardHeight(prefKey: String, defaultValue: Float) {
private fun setupScalePrefs(prefKey: String, defaultValue: Float) {
val prefs = sharedPreferences
val pref = findPreference(prefKey) as? SeekBarDialogPreference
pref?.setInterface(object : SeekBarDialogPreference.ValueProxy {
@ -149,13 +158,11 @@ class AppearanceSettingsFragment : SubScreenFragment() {
private fun getPercentageFromValue(floatValue: Float) = (floatValue * PERCENTAGE_FLOAT).toInt()
override fun writeValue(value: Int, key: String) = prefs.edit()
.putFloat(key, getValueFromPercentage(value)).apply()
override fun writeValue(value: Int, key: String) = prefs.edit().putFloat(key, getValueFromPercentage(value)).apply()
override fun writeDefaultValue(key: String) = prefs.edit().remove(key).apply()
override fun readValue(key: String) = getPercentageFromValue(
Settings.readKeyboardHeight(prefs, defaultValue))
override fun readValue(key: String) = getPercentageFromValue(prefs.getFloat(prefKey, defaultValue))
override fun readDefaultValue(key: String) = getPercentageFromValue(defaultValue)

View file

@ -83,6 +83,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_SHOW_EMOJI_KEY = "pref_show_emoji_key";
public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
public static final String PREF_ENABLE_SPLIT_KEYBOARD = "pref_split_keyboard";
public static final String PREF_SPLIT_SPACER_SCALE = "pref_split_spacer_scale";
public static final String PREF_KEYBOARD_HEIGHT_SCALE = "pref_keyboard_height_scale";
public static final String PREF_SPACE_TRACKPAD = "pref_space_trackpad";
public static final String PREF_DELETE_SWIPE = "pref_delete_swipe";
@ -341,13 +342,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return res.getInteger(R.integer.config_clipboard_history_retention_time);
}
public static float readKeyboardHeight(final SharedPreferences prefs,
final float defaultValue) {
final float percentage = prefs.getFloat(
Settings.PREF_KEYBOARD_HEIGHT_SCALE, UNDEFINED_PREFERENCE_VALUE_FLOAT);
return (percentage != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? percentage : defaultValue;
}
public static boolean readSpaceTrackpadEnabled(final SharedPreferences prefs) {
return prefs.getBoolean(PREF_SPACE_TRACKPAD, true);
}

View file

@ -95,6 +95,7 @@ public class SettingsValues {
public final boolean mShouldShowLxxSuggestionUi;
// Use split layout for keyboard.
public final boolean mIsSplitKeyboardEnabled;
public final float mSpacerRelativeWidth;
public final int mScreenMetrics;
public final boolean mAddToPersonalDictionary;
public final boolean mUseContactsDictionary;
@ -162,7 +163,12 @@ public class SettingsValues {
mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration());
mIsSplitKeyboardEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, false);
final float displayWidthDp = res.getDisplayMetrics().widthPixels / res.getDisplayMetrics().density;
mIsSplitKeyboardEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, false) && displayWidthDp > 600; // require display width of 600 dp for split
// determine spacerWidth from display width and scale setting
mSpacerRelativeWidth = mIsSplitKeyboardEnabled
? Math.min(Math.max((displayWidthDp - 600) / 6000f + 0.15f, 0.15f), 0.25f) * prefs.getFloat(Settings.PREF_SPLIT_SPACER_SCALE, DEFAULT_SIZE_SCALE)
: 0f;
mScreenMetrics = Settings.readScreenMetrics(res);
mShouldShowLxxSuggestionUi = Settings.SHOULD_SHOW_LXX_SUGGESTION_UI
@ -188,7 +194,7 @@ public class SettingsValues {
readSuggestionsEnabled(prefs);
mIncognitoModeEnabled = Settings.readAlwaysIncognitoMode(prefs) || mInputAttributes.mNoLearning
|| mInputAttributes.mIsPasswordField;
mKeyboardHeightScale = Settings.readKeyboardHeight(prefs, DEFAULT_SIZE_SCALE);
mKeyboardHeightScale = prefs.getFloat(Settings.PREF_KEYBOARD_HEIGHT_SCALE, DEFAULT_SIZE_SCALE);
mDisplayOrientation = res.getConfiguration().orientation;
mAppWorkarounds = new AsyncResultHolder<>("AppWorkarounds");
final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo(

View file

@ -51,6 +51,8 @@
<string name="settings_category_miscellaneous">Miscellaneous</string>
<!-- Option for enabling or disabling the split keyboard layout. [CHAR LIMIT=65]-->
<string name="enable_split_keyboard">Enable split keyboard</string>
<!-- Option for setting distance for split keyboard -->
<string name="split_spacer_scale">Split distance</string>
<!-- Option name for including other IMEs in the language key switch list [CHAR LIMIT=30] -->
<string name="language_switch_key_switch_input_method">Switch to other input methods</string>
<!-- Option name for switching language / subtype only [CHAR LIMIT=30] -->

View file

@ -63,6 +63,12 @@
android:persistent="true"
android:defaultValue="false" />
<org.dslul.openboard.inputmethod.latin.settings.SeekBarDialogPreference
android:key="pref_split_spacer_scale"
android:title="@string/split_spacer_scale"
latin:minValue="50"
latin:maxValue="200" /> <!-- percentage -->
<SwitchPreferenceCompat
android:key="pref_narrow_key_gaps"
android:title="@string/prefs_narrow_key_gaps"