diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java index 1bd8e6f43..b0d43635f 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java @@ -282,18 +282,24 @@ public class Key implements Comparable { 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 { // 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode, disabledIconId, visualInsetsLeft, visualInsetsRight); mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr); + mEnabled = true; } /** for */ @@ -1170,11 +1156,9 @@ public class Key implements Comparable { @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 { 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 { } 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 { 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; + } } } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java index b857468ac..c812a1754 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java @@ -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); diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.java deleted file mode 100644 index a2f54e08f..000000000 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.java +++ /dev/null @@ -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 { - 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> 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 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 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; - } -} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt new file mode 100644 index 000000000..330dc40b8 --- /dev/null +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt @@ -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(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> + + 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 { + 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) { + 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) { + 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 Iterable.sumOf(selector: (T) -> Float): Float { + var sum = 0f + for (element in this) { + sum += selector(element) + } + return sum +} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardParams.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardParams.java index 1a82779c8..0c65ab129 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardParams.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardParams.java @@ -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); diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardRow.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardRow.java index b8c90031c..6bfe95741 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardRow.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/XmlKeyboardRow.java @@ -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; } } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AppearanceSettingsFragment.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AppearanceSettingsFragment.kt index 5408aeec2..fefff9517 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AppearanceSettingsFragment.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AppearanceSettingsFragment.kt @@ -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) diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java index 8a304310f..fa6580d6f 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java @@ -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); } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java index e50cbdb1e..a9ba50ecf 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java @@ -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( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa6a2326c..45b5395d4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -51,6 +51,8 @@ Miscellaneous Enable split keyboard + + Split distance Switch to other input methods diff --git a/app/src/main/res/xml/prefs_screen_appearance.xml b/app/src/main/res/xml/prefs_screen_appearance.xml index d7ed9b642..57bfd9835 100644 --- a/app/src/main/res/xml/prefs_screen_appearance.xml +++ b/app/src/main/res/xml/prefs_screen_appearance.xml @@ -63,6 +63,12 @@ android:persistent="true" android:defaultValue="false" /> + +