From 34d8bd16f0e55605b70ea992747a6cf7e294caaf Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 11 May 2024 15:41:00 +0200 Subject: [PATCH 01/58] Migrate functional key layouts to json (#778) Now the functional key layouts should be (mostly) compatible to FlorisBoard Not yet customizable, this is a large step towards customizable functional key layouts --- .../main/assets/layouts/functional_keys.json | 21 + .../layouts/functional_keys_tablet.json | 36 + .../accessibility/KeyCodeDescriptionMapper.kt | 4 +- .../java/helium314/keyboard/keyboard/Key.java | 66 +- .../keyboard/keyboard/KeyboardId.java | 4 + .../keyboard/keyboard/KeyboardView.java | 4 +- .../keyboard/keyboard/MainKeyboardView.java | 2 +- .../keyboard/keyboard/PointerTracker.java | 2 +- .../keyboard/keyboard/PopupKeysKeyboard.java | 20 +- .../keyboard/internal/KeyboardBuilder.kt | 66 +- .../keyboard/internal/KeyboardCodesSet.java | 2 +- .../keyboard/internal/KeyboardParams.java | 23 +- .../keyboard/internal/KeyboardState.java | 6 +- .../keyboard/internal/PopupKeySpec.java | 2 +- .../internal/keyboard_parser/EmojiParser.kt | 11 +- .../keyboard_parser/KeyboardParser.kt | 672 +++++++----------- .../keyboard_parser/floris/KeyCode.kt | 34 +- .../keyboard_parser/floris/KeyLabel.kt | 48 ++ .../keyboard_parser/floris/KeyType.kt | 24 +- .../keyboard_parser/floris/PopupSet.kt | 27 +- .../keyboard_parser/floris/TextKeyData.kt | 353 +++++++-- .../keyboard/latin/common/Constants.java | 2 +- .../keyboard/latin/inputlogic/InputLogic.java | 4 +- .../keyboard/latin/settings/Settings.java | 3 + .../latin/suggestions/MoreSuggestions.java | 11 +- .../helium314/keyboard/latin/utils/Ktx.kt | 37 + .../res/values-sw600dp/functional-keys.xml | 8 - app/src/main/res/values/functional-keys.xml | 22 - .../helium314/keyboard/KeyboardParserTest.kt | 30 +- layouts.md | 99 ++- 30 files changed, 931 insertions(+), 712 deletions(-) create mode 100644 app/src/main/assets/layouts/functional_keys.json create mode 100644 app/src/main/assets/layouts/functional_keys_tablet.json create mode 100644 app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt delete mode 100644 app/src/main/res/values-sw600dp/functional-keys.xml delete mode 100644 app/src/main/res/values/functional-keys.xml diff --git a/app/src/main/assets/layouts/functional_keys.json b/app/src/main/assets/layouts/functional_keys.json new file mode 100644 index 000000000..de7890f44 --- /dev/null +++ b/app/src/main/assets/layouts/functional_keys.json @@ -0,0 +1,21 @@ +[ + [ + { "label": "shift", "width": 0.15 }, + { "type": "placeholder" }, + { "label": "delete", "width": 0.15 } + ], + [ + { "label": "symbol_alpha", "width": 0.15 }, + { "$": "variation_selector", + "default": { "label": "comma" }, + "email": { "label": "@", "groupId": 1, "type": "function" }, + "uri": { "label": "/", "groupId": 1, "type": "function" } + }, + { "label": "language_switch" }, + { "label": "emoji" }, + { "label": "numpad" }, + { "label": "space" }, + { "label": "period" }, + { "label": "action", "width": 0.15 } + ] +] diff --git a/app/src/main/assets/layouts/functional_keys_tablet.json b/app/src/main/assets/layouts/functional_keys_tablet.json new file mode 100644 index 000000000..17966185d --- /dev/null +++ b/app/src/main/assets/layouts/functional_keys_tablet.json @@ -0,0 +1,36 @@ +[ + [ + { "type": "placeholder" }, + { "label": "delete", "width": 0.1 } + ], + [ + { "type": "placeholder" } + ], + [ + { "type": "placeholder" }, + { "label": "action", "width": 0.1 } + ], + [ + { "label": "shift", "width": 0.1 }, + { "type": "placeholder" }, + { "label": "shift" } + ], + [ + { "label": "symbol_alpha" }, + { "$": "variation_selector", + "default": { "label": "comma" }, + "email": { "label": "@", "groupId": 1, "type": "function" }, + "uri": { "label": "/", "groupId": 1, "type": "function" } + }, + { "label": "language_switch" }, + { "label": "emoji" }, + { "label": "numpad" }, + { "label": "space" }, + { "label": "period" }, + { "$": "variation_selector", + "default": { "label": "emoji" }, + "email": { "label": "com" }, + "uri": { "label": "com" } + } + ] +] diff --git a/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt b/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt index 9a5fba56f..cd1841ce6 100644 --- a/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt +++ b/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt @@ -29,7 +29,7 @@ internal class KeyCodeDescriptionMapper private constructor() { put(KeyCode.SETTINGS, R.string.spoken_description_settings) put(KeyCode.SHIFT, R.string.spoken_description_shift) put(KeyCode.VOICE_INPUT, R.string.spoken_description_mic) - put(KeyCode.ALPHA_SYMBOL, R.string.spoken_description_to_symbol) + put(KeyCode.SYMBOL_ALPHA, R.string.spoken_description_to_symbol) put(Constants.CODE_TAB, R.string.spoken_description_tab) put(KeyCode.LANGUAGE_SWITCH, R.string.spoken_description_language_switch) put(KeyCode.ACTION_NEXT, R.string.spoken_description_action_next) @@ -58,7 +58,7 @@ internal class KeyCodeDescriptionMapper private constructor() { */ fun getDescriptionForKey(context: Context, keyboard: Keyboard?, key: Key, shouldObscure: Boolean): String? { val code = key.code - if (code == KeyCode.ALPHA_SYMBOL || code == KeyCode.SYMBOL || code == KeyCode.ALPHA) { + if (code == KeyCode.SYMBOL_ALPHA || code == KeyCode.SYMBOL || code == KeyCode.ALPHA) { val description = getDescriptionForSwitchAlphaSymbol(context, keyboard) if (description != null) { return description diff --git a/app/src/main/java/helium314/keyboard/keyboard/Key.java b/app/src/main/java/helium314/keyboard/keyboard/Key.java index 46ddc0fa7..078b3e2c5 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/Key.java +++ b/app/src/main/java/helium314/keyboard/keyboard/Key.java @@ -308,17 +308,17 @@ private Key(KeyParams keyParams) { final float horizontalGapFloat = isSpacer() ? 0 : (keyParams.mKeyboardParams.mRelativeHorizontalGap * keyParams.mKeyboardParams.mOccupiedWidth); mHorizontalGap = Math.round(horizontalGapFloat); mVerticalGap = Math.round(keyParams.mKeyboardParams.mRelativeVerticalGap * keyParams.mKeyboardParams.mOccupiedHeight); - mWidth = Math.round(keyParams.mFullWidth - horizontalGapFloat); + mWidth = Math.round(keyParams.mAbsoluteWidth - horizontalGapFloat); // height is always rounded down, because rounding up may make the keyboard too high to fit, leading to issues - mHeight = (int) (keyParams.mFullHeight - keyParams.mKeyboardParams.mVerticalGap); + mHeight = (int) (keyParams.mAbsoluteHeight - keyParams.mKeyboardParams.mVerticalGap); if (!isSpacer() && (mWidth == 0 || mHeight == 0)) { throw new IllegalStateException("key needs positive width and height"); } // Horizontal gap is divided equally to both sides of the key. mX = Math.round(keyParams.xPos + horizontalGapFloat / 2); mY = Math.round(keyParams.yPos); - mHitBox.set(Math.round(keyParams.xPos), Math.round(keyParams.yPos), Math.round(keyParams.xPos + keyParams.mFullWidth) + 1, - Math.round(keyParams.yPos + keyParams.mFullHeight)); + mHitBox.set(Math.round(keyParams.xPos), Math.round(keyParams.yPos), Math.round(keyParams.xPos + keyParams.mAbsoluteWidth) + 1, + Math.round(keyParams.yPos + keyParams.mAbsoluteHeight)); mHashCode = computeHashCode(this); } @@ -504,7 +504,7 @@ public final boolean isSpacer() { return this instanceof Spacer; } - public final boolean isActionKey() { + public final boolean hasActionKeyBackground() { return mBackgroundType == BACKGROUND_TYPE_ACTION; } @@ -513,7 +513,7 @@ public final boolean isShift() { } public final boolean isModifier() { - return mCode == KeyCode.SHIFT || mCode == KeyCode.ALPHA_SYMBOL || mCode == KeyCode.ALPHA || mCode == KeyCode.SYMBOL; + return mCode == KeyCode.SHIFT || mCode == KeyCode.SYMBOL_ALPHA || mCode == KeyCode.ALPHA || mCode == KeyCode.SYMBOL; } public final boolean isRepeatable() { @@ -906,7 +906,7 @@ public final Drawable selectBackgroundDrawable(@NonNull final Drawable keyBackgr final Drawable background; if (isAccentColored()) { background = actionKeyBackground; - } else if (isFunctional()) { + } else if (hasFunctionalBackground()) { background = functionalKeyBackground; } else if (mBackgroundType == BACKGROUND_TYPE_SPACEBAR) { background = spacebarBackground; @@ -919,7 +919,7 @@ public final Drawable selectBackgroundDrawable(@NonNull final Drawable keyBackgr } public final boolean isAccentColored() { - if (isActionKey()) return true; + if (hasActionKeyBackground()) return true; final String iconName = KeyboardIconsSet.getIconName(getIconId()); return iconName.equals(KeyboardIconsSet.NAME_NEXT_KEY) || iconName.equals(KeyboardIconsSet.NAME_PREVIOUS_KEY) @@ -927,7 +927,7 @@ public final boolean isAccentColored() { || iconName.equals(KeyboardIconsSet.NAME_EMOJI_ACTION_KEY); } - public boolean isFunctional() { + public boolean hasFunctionalBackground() { return mBackgroundType == BACKGROUND_TYPE_FUNCTIONAL || mBackgroundType == BACKGROUND_TYPE_STICKY_OFF || mBackgroundType == BACKGROUND_TYPE_STICKY_ON; @@ -954,12 +954,12 @@ public static class KeyParams { // params for building public 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 + public float mWidth; + public float mHeight; // also should allow negative values, indicating absolute height is defined // params that may change - public float mFullWidth; - public float mFullHeight; + public float mAbsoluteWidth; + public float mAbsoluteHeight; public float xPos; public float yPos; @@ -977,10 +977,10 @@ public static class KeyParams { @Nullable public final OptionalAttributes mOptionalAttributes; public final boolean mEnabled; - public static KeyParams newSpacer(final KeyboardParams params, final float relativeWidth) { + public static KeyParams newSpacer(final KeyboardParams params, final float width) { final KeyParams spacer = new KeyParams(params); - spacer.mRelativeWidth = relativeWidth; - spacer.mRelativeHeight = params.mDefaultRelativeRowHeight; + spacer.mWidth = width; + spacer.mHeight = params.mDefaultRowHeight; return spacer; } @@ -989,18 +989,18 @@ public Key createKey() { return new Key(this); } - public void setDimensionsFromRelativeSize(final float newX, final float newY) { - if (mRelativeHeight == 0) - mRelativeHeight = mKeyboardParams.mDefaultRelativeRowHeight; - if (!isSpacer && mRelativeWidth == 0) - mRelativeWidth = mKeyboardParams.mDefaultRelativeKeyWidth; - if (mRelativeHeight < 0) + public void setAbsoluteDimensions(final float newX, final float newY) { + if (mHeight == 0) + mHeight = mKeyboardParams.mDefaultRowHeight; + if (!isSpacer && mWidth == 0) + throw new IllegalStateException("width = 0 should have been evaluated already"); + if (mHeight < 0) // todo (later): deal with it properly when it needs to be adjusted, i.e. when changing popupKeys or moreSuggestions throw new IllegalStateException("can't (yet) deal with absolute height"); xPos = newX; yPos = newY; - mFullWidth = mRelativeWidth * mKeyboardParams.mBaseWidth; - mFullHeight = mRelativeHeight * mKeyboardParams.mBaseHeight; + mAbsoluteWidth = mWidth * mKeyboardParams.mBaseWidth; + mAbsoluteHeight = mHeight * mKeyboardParams.mBaseHeight; } private static int getPopupKeysColumnAndFlagsAndSetNullInArray(final KeyboardParams params, final String[] popupKeys) { @@ -1052,7 +1052,7 @@ public KeyParams( @NonNull final String keySpec, // key text or some special string for KeySpecParser, e.g. "!icon/shift_key|!code/key_shift" (avoid using !text, should be removed) final int code, @NonNull final KeyboardParams params, - final float relativeWidth, + final float width, final int labelFlags, final int backgroundType, @Nullable final PopupSet popupSet @@ -1060,8 +1060,8 @@ public KeyParams( mKeyboardParams = params; mBackgroundType = backgroundType; mLabelFlags = labelFlags; - mRelativeWidth = relativeWidth; - mRelativeHeight = params.mDefaultRelativeRowHeight; + mWidth = width; + mHeight = params.mDefaultRowHeight; mIconId = KeySpecParser.getIconId(keySpec); final boolean needsToUpcase = needsToUpcase(mLabelFlags, params.mId.mElementId); @@ -1144,9 +1144,9 @@ public KeyParams( } // action flags don't need to be specified, they can be deduced from the key - if (backgroundType == BACKGROUND_TYPE_SPACEBAR + if (mCode == Constants.CODE_SPACE || mCode == KeyCode.LANGUAGE_SWITCH - || (mCode == KeyCode.ALPHA_SYMBOL && !params.mId.isAlphabetKeyboard()) + || (mCode == KeyCode.SYMBOL_ALPHA && !params.mId.isAlphabetKeyboard()) ) actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS; if (mCode <= Constants.CODE_SPACE && mCode != KeyCode.MULTIPLE_CODE_POINTS) @@ -1237,8 +1237,8 @@ private KeyParams(final KeyboardParams params) { public KeyParams(final KeyParams keyParams) { xPos = keyParams.xPos; yPos = keyParams.yPos; - mRelativeWidth = keyParams.mRelativeWidth; - mRelativeHeight = keyParams.mRelativeHeight; + mWidth = keyParams.mWidth; + mHeight = keyParams.mHeight; isSpacer = keyParams.isSpacer; mKeyboardParams = keyParams.mKeyboardParams; mEnabled = keyParams.mEnabled; @@ -1248,8 +1248,8 @@ public KeyParams(final KeyParams keyParams) { mHintLabel = keyParams.mHintLabel; mLabelFlags = keyParams.mLabelFlags; mIconId = keyParams.mIconId; - mFullWidth = keyParams.mFullWidth; - mFullHeight = keyParams.mFullHeight; + mAbsoluteWidth = keyParams.mAbsoluteWidth; + mAbsoluteHeight = keyParams.mAbsoluteHeight; mPopupKeys = keyParams.mPopupKeys; mPopupKeysColumnAndFlags = keyParams.mPopupKeysColumnAndFlags; mBackgroundType = keyParams.mBackgroundType; diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java index 8250d7527..7826c4d84 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java @@ -149,6 +149,10 @@ private static boolean isAlphabetKeyboard(final int elementId) { return elementId < ELEMENT_SYMBOLS; } + public boolean isAlphaOrSymbolKeyboard() { + return mElementId <= ELEMENT_SYMBOLS_SHIFTED; + } + public boolean isAlphabetKeyboard() { return isAlphabetKeyboard(mElementId); } diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java index 3b006d23e..f2bcb1431 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java @@ -472,7 +472,7 @@ protected void onDrawKeyTopVisuals(@NonNull final Key key, @NonNull final Canvas blendAlpha(paint, params.mAnimAlpha); final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); - final boolean isFunctionalKeyAndRoundedStyle = mColors.getThemeStyle().equals(STYLE_ROUNDED) && key.isFunctional(); + final boolean isFunctionalKeyAndRoundedStyle = mColors.getThemeStyle().equals(STYLE_ROUNDED) && key.hasFunctionalBackground(); final float hintX, hintBaseline; if (key.hasHintLabel()) { // The hint label is placed just right of the key label. Used mainly on @@ -555,7 +555,7 @@ protected void drawKeyPopupHint(@NonNull final Key key, @NonNull final Canvas ca if (key.getBackgroundType() == Key.BACKGROUND_TYPE_SPACEBAR) hintX = keyWidth + hintBaseline + labelCharWidth * 0.1f; else - hintX = key.isFunctional() || key.isActionKey() ? keyWidth / 2.0f : keyWidth - mKeyHintLetterPadding - labelCharWidth / 2.0f; + hintX = key.hasFunctionalBackground() || key.hasActionKeyBackground() ? keyWidth / 2.0f : keyWidth - mKeyHintLetterPadding - labelCharWidth / 2.0f; } else { hintX = keyWidth - mKeyHintLetterPadding - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f; } diff --git a/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java index d6a31e066..f18905b6b 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java @@ -540,7 +540,7 @@ public PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key, mPopupKeysKeyboardCache.put(key, popupKeysKeyboard); } - final View container = key.isActionKey() ? mPopupKeysKeyboardForActionContainer + final View container = key.hasActionKeyBackground() ? mPopupKeysKeyboardForActionContainer : mPopupKeysKeyboardContainer; final PopupKeysKeyboardView popupKeysKeyboardView = container.findViewById(R.id.popup_keys_keyboard_view); diff --git a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java index dbde2efb9..36260fd7a 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java @@ -1107,7 +1107,7 @@ public void onLongPressed() { return; } } - if (code == KeyCode.ALPHA_SYMBOL && Settings.getInstance().getCurrent().mLongPressSymbolsForNumpad) { + if (code == KeyCode.SYMBOL_ALPHA && Settings.getInstance().getCurrent().mLongPressSymbolsForNumpad) { sListener.onCodeInput(KeyCode.NUMPAD, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false); return; } diff --git a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboard.java b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboard.java index 5ff793eea..c3c6251fb 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboard.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboard.java @@ -23,7 +23,7 @@ public final class PopupKeysKeyboard extends Keyboard { PopupKeysKeyboard(final PopupKeysKeyboardParams params) { super(params); - mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2; + mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultAbsoluteKeyWidth / 2; } public int getDefaultCoordX() { @@ -71,8 +71,8 @@ public void setParameters(final int numKeys, final int numColumn, final int keyW throw new IllegalArgumentException("Keyboard is too small to hold popup keys: " + parentKeyboardWidth + " " + keyWidth + " " + numKeys + " " + numColumn); } - mDefaultKeyWidth = keyWidth; - mDefaultRowHeight = rowHeight; + mDefaultAbsoluteKeyWidth = keyWidth; + mDefaultAbsoluteRowHeight = rowHeight; mNumRows = (numKeys + numColumn - 1) / numColumn; final int numColumns = isPopupKeysFixedColumn ? Math.min(numKeys, numColumn) @@ -116,10 +116,10 @@ public void setParameters(final int numKeys, final int numColumn, final int keyW mTopRowAdjustment = isPopupKeysFixedOrder ? getFixedOrderTopRowAdjustment() : getAutoOrderTopRowAdjustment(); mDividerWidth = dividerWidth; - mColumnWidth = mDefaultKeyWidth + mDividerWidth; + mColumnWidth = mDefaultAbsoluteKeyWidth + mDividerWidth; mBaseWidth = mOccupiedWidth = mNumColumns * mColumnWidth - mDividerWidth; // Need to subtract the bottom row's gutter only. - mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap + mBaseHeight = mOccupiedHeight = mNumRows * mDefaultAbsoluteRowHeight - mVerticalGap + mTopPadding + mBottomPadding; } @@ -227,7 +227,7 @@ public int getX(final int n, final int row) { } public int getY(final int row) { - return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding; + return (mNumRows - 1 - row) * mDefaultAbsoluteRowHeight + mTopPadding; } public void markAsEdgeKey(final Key key, final int row) { @@ -287,8 +287,8 @@ public Builder(final Context context, final Key key, final Keyboard keyboard, final float padding = context.getResources().getDimension( R.dimen.config_popup_keys_keyboard_key_horizontal_padding) + (key.hasLabelsInPopupKeys() - ? mParams.mDefaultKeyWidth * LABEL_PADDING_RATIO : 0.0f); - keyWidth = getMaxKeyWidth(key, mParams.mDefaultKeyWidth, padding, paintToMeasure); + ? mParams.mDefaultAbsoluteKeyWidth * LABEL_PADDING_RATIO : 0.0f); + keyWidth = getMaxKeyWidth(key, mParams.mDefaultAbsoluteKeyWidth, padding, paintToMeasure); rowHeight = keyboard.mMostCommonKeyHeight; } final int dividerWidth; @@ -342,9 +342,9 @@ public PopupKeysKeyboard build() { // left of the default position. if (params.mDividerWidth > 0 && pos != 0) { final int dividerX = (pos > 0) ? x - params.mDividerWidth - : x + params.mDefaultKeyWidth; + : x + params.mDefaultAbsoluteKeyWidth; final Key divider = new PopupKeyDivider( - params, dividerX, y, params.mDividerWidth, params.mDefaultRowHeight); + params, dividerX, y, params.mDividerWidth, params.mDefaultAbsoluteRowHeight); params.onAddKey(divider); } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt index 448e2f204..f29e6ba79 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt @@ -111,12 +111,12 @@ open class KeyboardBuilder(protected val mContext: Context, fillGapsWithSpacers(row) var currentX = mParams.mLeftPadding.toFloat() row.forEach { - it.setDimensionsFromRelativeSize(currentX, currentY) + it.setAbsoluteDimensions(currentX, currentY) if (DebugFlags.DEBUG_ENABLED) - Log.d(TAG, "setting size and position for ${it.mLabel}, ${it.mCode}: x ${currentX.toInt()}, w ${it.mFullWidth.toInt()}") - currentX += it.mFullWidth + Log.d(TAG, "setting size and position for ${it.mLabel}, ${it.mCode}: x ${currentX.toInt()}, w ${it.mAbsoluteWidth.toInt()}") + currentX += it.mAbsoluteWidth } - currentY += row.first().mFullHeight + currentY += row.first().mAbsoluteHeight } } @@ -140,7 +140,7 @@ open class KeyboardBuilder(protected val mContext: Context, i++ currentX += currentKeyXPos - currentX } - currentX += row[i].mFullWidth + currentX += row[i].mAbsoluteWidth i++ } if (currentX < mParams.mOccupiedWidth) { @@ -161,48 +161,48 @@ open class KeyboardBuilder(protected val mContext: Context, 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 relativeWidthSum = row.sumOf { it.mWidth } // sum up relative widths val spacer = KeyParams.newSpacer(mParams, spacerRelativeWidth) // insert spacer before first key that starts right of the center (also consider gap) - var insertIndex = row.indexOfFirst { it.xPos + it.mFullWidth / 3 > mParams.mOccupiedWidth / 2 } + var insertIndex = row.indexOfFirst { it.xPos + it.mAbsoluteWidth / 3 > 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 + val widthBeforeSpace = row.subList(0, insertIndex - 1).sumOf { it.mWidth } + val widthAfterSpace = row.subList(insertIndex, row.size).sumOf { it.mWidth } + val spaceLeftWidth = (maxWidthBeforeSpacer - widthBeforeSpace).coerceAtLeast(mParams.mDefaultKeyWidth) + val spaceRightWidth = (maxWidthAfterSpacer - widthAfterSpace).coerceAtLeast(mParams.mDefaultKeyWidth) + val spacerWidth = spaceLeft.mWidth + 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 + spaceLeft.mWidth = spaceLeftWidth + spaceRight.mWidth = spaceRightWidth + spacer.mWidth = spacerWidth row.add(insertIndex, spaceRight) row.add(insertIndex, spacer) } else { // otherwise increase space width, so other keys are resized properly - spaceLeft.mRelativeWidth += spacerWidth + spaceLeft.mWidth += spacerWidth } } else { - val widthBeforeSpacer = row.subList(0, insertIndex).sumOf { it.mRelativeWidth } - val widthAfterSpacer = row.subList(insertIndex, row.size).sumOf { it.mRelativeWidth } + val widthBeforeSpacer = row.subList(0, insertIndex).sumOf { it.mWidth } + val widthAfterSpacer = row.subList(insertIndex, row.size).sumOf { it.mWidth } maxWidthBeforeSpacer = maxWidthBeforeSpacer.coerceAtLeast(widthBeforeSpacer) maxWidthAfterSpacer = maxWidthAfterSpacer.coerceAtLeast(widthAfterSpacer) row.add(insertIndex, spacer) } // re-calculate relative widths - val relativeWidthSumNew = row.sumOf { it.mRelativeWidth } + val relativeWidthSumNew = row.sumOf { it.mWidth } 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 + it.mWidth *= widthFactor + it.setAbsoluteDimensions(currentX, y) + currentX += it.mAbsoluteWidth } } } @@ -211,19 +211,19 @@ open class KeyboardBuilder(protected val mContext: Context, // todo: this assumes fixed layout for symbols keys, which will change soon! private fun reduceSymbolAndActionKeyWidth(row: ArrayList) { val spaceKey = row.first { it.mCode == Constants.CODE_SPACE } - val symbolKey = row.firstOrNull { it.mCode == KeyCode.ALPHA_SYMBOL } - val symbolKeyWidth = symbolKey?.mRelativeWidth ?: 0f - if (symbolKeyWidth > mParams.mDefaultRelativeKeyWidth) { - val widthToChange = symbolKey!!.mRelativeWidth - mParams.mDefaultRelativeKeyWidth - symbolKey.mRelativeWidth -= widthToChange - spaceKey.mRelativeWidth += widthToChange + val symbolKey = row.firstOrNull { it.mCode == KeyCode.SYMBOL_ALPHA } + val symbolKeyWidth = symbolKey?.mWidth ?: 0f + if (symbolKeyWidth > mParams.mDefaultKeyWidth) { + val widthToChange = symbolKey!!.mWidth - mParams.mDefaultKeyWidth + symbolKey.mWidth -= widthToChange + spaceKey.mWidth += 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 + val actionKeyWidth = actionKey?.mWidth ?: 0f + if (actionKeyWidth > mParams.mDefaultKeyWidth * 1.1f) { // allow it to stay a little wider + val widthToChange = actionKey!!.mWidth - mParams.mDefaultKeyWidth * 1.1f + actionKey.mWidth -= widthToChange + spaceKey.mWidth += widthToChange } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardCodesSet.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardCodesSet.java index 5a0809e3b..c089b2b62 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardCodesSet.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardCodesSet.java @@ -58,7 +58,7 @@ public static int getCode(final String name) { Constants.CODE_SPACE, KeyCode.SHIFT, KeyCode.CAPS_LOCK, - KeyCode.ALPHA_SYMBOL, + KeyCode.SYMBOL_ALPHA, KeyCode.ALPHA, KeyCode.SYMBOL, KeyCode.MULTIPLE_CODE_POINTS, diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java index 11739ed92..b63d15442 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java @@ -19,7 +19,6 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyboardInfos; import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode; import helium314.keyboard.latin.R; -import helium314.keyboard.latin.common.Constants; import helium314.keyboard.latin.settings.Settings; import helium314.keyboard.latin.utils.ResourceUtils; @@ -55,13 +54,13 @@ public class KeyboardParams { @Nullable public KeyVisualAttributes mKeyVisualAttributes; - public float mDefaultRelativeRowHeight; - public float mDefaultRelativeKeyWidth; + public float mDefaultRowHeight; + public float mDefaultKeyWidth; public float mRelativeHorizontalGap; public float mRelativeVerticalGap; // relative values multiplied with baseHeight / baseWidth - public int mDefaultRowHeight; - public int mDefaultKeyWidth; + public int mDefaultAbsoluteRowHeight; + public int mDefaultAbsoluteKeyWidth; public int mHorizontalGap; public int mVerticalGap; @@ -227,9 +226,9 @@ public void readAttributes(final Context context, @Nullable final AttributeSet a mBaseWidth = mOccupiedWidth - mLeftPadding - mRightPadding; final float defaultKeyWidthFactor = context.getResources().getInteger(R.integer.config_screen_metrics) > 2 ? 0.9f : 1f; - mDefaultRelativeKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth, + mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth, 1, 1, defaultKeyWidthFactor / DEFAULT_KEYBOARD_COLUMNS); - mDefaultKeyWidth = (int) (mDefaultRelativeKeyWidth * mBaseWidth); + mDefaultAbsoluteKeyWidth = (int) (mDefaultKeyWidth * mBaseWidth); // todo: maybe settings should not be accessed from here? if (Settings.getInstance().getCurrent().mNarrowKeyGaps) { @@ -250,13 +249,13 @@ public void readAttributes(final Context context, @Nullable final AttributeSet a mVerticalGap = (int) (mRelativeVerticalGap * height); mBaseHeight = mOccupiedHeight - mTopPadding - mBottomPadding + mVerticalGap; - mDefaultRelativeRowHeight = ResourceUtils.getDimensionOrFraction(keyboardAttr, + mDefaultRowHeight = 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 + if (mDefaultRowHeight > 1) { // can be absolute size, in that case will be > 1 + mDefaultAbsoluteRowHeight = (int) mDefaultRowHeight; + mDefaultRowHeight *= -1; // make it negative when it's absolute } else { - mDefaultRowHeight = (int) (mDefaultRelativeRowHeight * mBaseHeight); + mDefaultAbsoluteRowHeight = (int) (mDefaultRowHeight * mBaseHeight); } mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr); diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardState.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardState.java index c6579e58a..b74b809c0 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardState.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardState.java @@ -422,7 +422,7 @@ public void onPressKey(final int code, final boolean isSinglePointer, final int onPressShift(); } else if (code == KeyCode.CAPS_LOCK) { // Nothing to do here. See {@link #onReleaseKey(int,boolean)}. - } else if (code == KeyCode.ALPHA_SYMBOL) { + } else if (code == KeyCode.SYMBOL_ALPHA) { onPressAlphaSymbol(autoCapsFlags, recapitalizeMode); } else if (code == KeyCode.SYMBOL) { // don't start sliding, causes issues with fully customizable layouts @@ -463,7 +463,7 @@ public void onReleaseKey(final int code, final boolean withSliding, final int au onReleaseShift(withSliding, autoCapsFlags, recapitalizeMode); } else if (code == KeyCode.CAPS_LOCK) { setShiftLocked(!mAlphabetShiftState.isShiftLocked()); - } else if (code == KeyCode.ALPHA_SYMBOL) { + } else if (code == KeyCode.SYMBOL_ALPHA) { onReleaseAlphaSymbol(withSliding, autoCapsFlags, recapitalizeMode); } else if (code == KeyCode.SYMBOL) { onReleaseSymbol(withSliding, autoCapsFlags, recapitalizeMode); @@ -700,7 +700,7 @@ public void onEvent(final Event event, final int autoCapsFlags, final int recapi switch (mSwitchState) { case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: - if (code == KeyCode.ALPHA_SYMBOL) { + if (code == KeyCode.SYMBOL_ALPHA) { // Detected only the mode change key has been pressed, and then released. if (mMode == MODE_ALPHABET) { mSwitchState = SWITCH_STATE_ALPHA; diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/PopupKeySpec.java b/app/src/main/java/helium314/keyboard/keyboard/internal/PopupKeySpec.java index 9f88f78c6..645e002c9 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/PopupKeySpec.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/PopupKeySpec.java @@ -70,7 +70,7 @@ public PopupKeySpec(@NonNull final String popupKeySpec, boolean needsToUpperCase public Key buildKey(final int x, final int y, final int labelFlags, @NonNull final KeyboardParams params) { return new Key(mLabel, mIconId, mCode, mOutputText, null /* hintLabel */, labelFlags, - Key.BACKGROUND_TYPE_NORMAL, x, y, params.mDefaultKeyWidth, params.mDefaultRowHeight, + Key.BACKGROUND_TYPE_NORMAL, x, y, params.mDefaultAbsoluteKeyWidth, params.mDefaultAbsoluteRowHeight, params.mHorizontalGap, params.mVerticalGap); } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/EmojiParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/EmojiParser.kt index ad4f1022a..66bdafb7b 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/EmojiParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/EmojiParser.kt @@ -9,7 +9,6 @@ import helium314.keyboard.keyboard.KeyboardId import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode import helium314.keyboard.latin.R -import helium314.keyboard.latin.common.Constants import helium314.keyboard.latin.common.StringUtils import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.ResourceUtils @@ -44,20 +43,20 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte // determine key width for default settings (no number row, no one-handed mode, 100% height and bottom padding scale) // this is a bit long, but ensures that emoji size stays the same, independent of these settings - val defaultKeyWidth = (ResourceUtils.getDefaultKeyboardWidth(context.resources) - params.mLeftPadding - params.mRightPadding) * params.mDefaultRelativeKeyWidth + val defaultKeyWidth = (ResourceUtils.getDefaultKeyboardWidth(context.resources) - params.mLeftPadding - params.mRightPadding) * params.mDefaultKeyWidth val keyWidth = defaultKeyWidth * sqrt(Settings.getInstance().current.mKeyboardHeightScale) val defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(context.resources, false) val defaultBottomPadding = context.resources.getFraction(R.fraction.config_keyboard_bottom_padding_holo, defaultKeyboardHeight, defaultKeyboardHeight) val emojiKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(context.resources, false) * 0.75f + params.mVerticalGap - defaultBottomPadding - context.resources.getDimensionPixelSize(R.dimen.config_emoji_category_page_id_height) - val keyHeight = emojiKeyboardHeight * params.mDefaultRelativeRowHeight * Settings.getInstance().current.mKeyboardHeightScale // still apply height scale to key + val keyHeight = emojiKeyboardHeight * params.mDefaultRowHeight * Settings.getInstance().current.mKeyboardHeightScale // still apply height scale to key emojiArray.forEachIndexed { i, codeArraySpec -> val keyParams = parseEmojiKey(codeArraySpec, popupEmojisArray?.get(i)?.takeIf { it.isNotEmpty() }) ?: return@forEachIndexed keyParams.xPos = currentX keyParams.yPos = currentY - keyParams.mFullWidth = keyWidth - keyParams.mFullHeight = keyHeight - currentX += keyParams.mFullWidth + keyParams.mAbsoluteWidth = keyWidth + keyParams.mAbsoluteHeight = keyHeight + currentX += keyParams.mAbsoluteWidth row.add(keyParams) } return arrayListOf(row) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index 1eb892212..591a10134 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -9,18 +9,17 @@ import androidx.annotation.StringRes import helium314.keyboard.keyboard.Key import helium314.keyboard.keyboard.Key.KeyParams import helium314.keyboard.keyboard.KeyboardId -import helium314.keyboard.keyboard.KeyboardTheme import helium314.keyboard.keyboard.internal.KeyboardIconsSet import helium314.keyboard.keyboard.internal.KeyboardParams -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyLabel import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyType import helium314.keyboard.keyboard.internal.keyboard_parser.floris.SimplePopups +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData import helium314.keyboard.latin.R import helium314.keyboard.latin.common.Constants import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.common.isEmoji -import helium314.keyboard.latin.common.splitOnWhitespace import helium314.keyboard.latin.define.DebugFlags import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.spellcheck.AndroidSpellCheckerService @@ -31,7 +30,10 @@ import helium314.keyboard.latin.utils.POPUP_KEYS_NUMBER import helium314.keyboard.latin.utils.ScriptUtils import helium314.keyboard.latin.utils.ScriptUtils.script import helium314.keyboard.latin.utils.getLayoutFile +import helium314.keyboard.latin.utils.removeFirst +import helium314.keyboard.latin.utils.replaceFirst import helium314.keyboard.latin.utils.runInLocale +import helium314.keyboard.latin.utils.splitAt import helium314.keyboard.latin.utils.sumOf import java.io.File @@ -65,103 +67,260 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co params.mTouchPositionCorrection.load(context.resources.getStringArray(infos.touchPositionCorrectionData)) val baseKeys: MutableList> = parseCoreLayout(layoutContent) - val keysInRows: ArrayList> - if (params.mId.mElementId <= KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { - keysInRows = createAlphaSymbolRows(baseKeys) + val keysInRows = if (params.mId.isAlphaOrSymbolKeyboard) { + createAlphaSymbolRows(baseKeys) } else if (params.mId.isNumberLayout) { - keysInRows = createNumericRows(baseKeys) + createNumericRows(baseKeys) } else { throw(UnsupportedOperationException("creating KeyboardId ${params.mId.mElementId} not supported")) } // rescale height if we have more than 4 rows val heightRescale = if (keysInRows.size > 4) 4f / keysInRows.size else 1f if (heightRescale != 1f) { - keysInRows.forEach { row -> row.forEach { it.mRelativeHeight *= heightRescale } } + keysInRows.forEach { row -> row.forEach { it.mHeight *= heightRescale } } } return keysInRows } + // this should be ready for customizable functional layouts, but needs cleanup + // todo (later): remove this as part of adding a cache for parsed layouts + private fun getFunctionalKeyLayoutText(): String { + if (!params.mId.isAlphaOrSymbolKeyboard) throw IllegalStateException("functional key layout only for aloha and symbol layouts") + val layouts = Settings.getLayoutsDir(context).list() ?: emptyArray() + if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { + if ("functional_keys_symbols_shifted.json" in layouts) + return getLayoutFile("functional_keys_symbols_shifted.json", context).readText() + } + if (!params.mId.isAlphabetKeyboard) { + if ("functional_keys_symbols.json" in layouts) + return getLayoutFile("functional_keys_symbols.json", context).readText() + } + if ("functional_keys.json" in layouts) + return getLayoutFile("functional_keys.json", context).readText() + val fileName = if (Settings.getInstance().isTablet) "functional_keys_tablet.json" else "functional_keys.json" + return context.readAssetsLayoutFile(fileName) + } + private fun createAlphaSymbolRows(baseKeys: MutableList>): ArrayList> { addNumberRowOrPopupKeys(baseKeys) if (params.mId.isAlphabetKeyboard) addSymbolPopupKeys(baseKeys) - val bottomRow = getBottomRowAndAdjustBaseKeys(baseKeys) // not really fast, but irrelevant compared to the loop + if (params.mId.mNumberRowEnabled) + baseKeys.add( + 0, + params.mLocaleKeyboardInfos.getNumberRow() + .map { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) }) - val keysInRows = ArrayList>() - val functionalKeys = parseFunctionalKeys(R.string.key_def_functional) - val functionalKeysTop = parseFunctionalKeys(R.string.key_def_functional_top_row) + val allFunctionalKeys = JsonKeyboardParser(params, context).parseCoreLayout(getFunctionalKeyLayoutText()) + adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys, baseKeys) - // todo: this loop could use some performance improvements - baseKeys.forEachIndexed { i, it -> - val row: List = if (i == baseKeys.lastIndex && isTablet()) { + if (allFunctionalKeys.none { it.singleOrNull()?.isKeyPlaceholder() == true }) + // add a placeholder so splitAt does what we really want + allFunctionalKeys.add(0, listOf(TextKeyData(type = KeyType.PLACEHOLDER))) + + val (functionalKeysTop, functionalKeysBottom) = allFunctionalKeys.splitAt { it.singleOrNull()?.isKeyPlaceholder() == true } + + // offset for bottom, relevant for getting correct functional key rows + val bottomIndexOffset = baseKeys.size - functionalKeysBottom.size + + val functionalKeys = mutableListOf, List>>() + val baseKeyParams = baseKeys.mapIndexed { i, it -> + val row: List = if (i == baseKeys.lastIndex - 1 && Settings.getInstance().isTablet) { // add bottom row extra keys + // todo (later): this can make very customized layouts look awkward + // decide when to (not) add it + // when not adding, consider that punctuation popup keys should not remove those keys! val tabletExtraKeys = params.mLocaleKeyboardInfos.getTabletExtraKeys(params.mId.mElementId) tabletExtraKeys.first + it + tabletExtraKeys.second } else { it } - // parse functional keys for this row (if any) - val offset = baseKeys.size - functionalKeys.size - val functionalKeysDefs = if (i >= offset) functionalKeys[i - offset] // functional keys are aligned to bottom - else emptyList() to emptyList() - val outerFunctionalKeyDefs = if (i == 0 && functionalKeysTop.isNotEmpty()) functionalKeysTop.first() // top row - else emptyList() to emptyList() - // if we have a top row and top row entries from normal functional key defs, use top row as outer keys - val functionalKeysLeft = outerFunctionalKeyDefs.first.map { getFunctionalKeyParams(it) } + functionalKeysDefs.first.map { getFunctionalKeyParams(it) } - val functionalKeysRight = functionalKeysDefs.second.map { getFunctionalKeyParams(it) } + outerFunctionalKeyDefs.second.map { getFunctionalKeyParams(it) } - val paramsRow = ArrayList(functionalKeysLeft) - - // determine key width, maybe scale factor for keys, and spacers to add - val usedKeyWidth = params.mDefaultRelativeKeyWidth * row.size - val functionalKeyWidth = (functionalKeysLeft.sumOf { it.mRelativeWidth }) + (functionalKeysRight.sumOf { it.mRelativeWidth }) - val availableWidth = 1f - functionalKeyWidth - var keyWidth: Float - val spacerWidth: Float - if (availableWidth - usedKeyWidth > 0.0001f) { // don't add spacers if only a tiny bit is empty - // width available, add spacer - keyWidth = params.mDefaultRelativeKeyWidth - spacerWidth = (availableWidth - usedKeyWidth) / 2 - } else { - // need more width, re-scale - spacerWidth = 0f - keyWidth = availableWidth / row.size - } - if (spacerWidth != 0f) { - paramsRow.add(KeyParams.newSpacer(params, spacerWidth)) - } - if (keyWidth < params.mDefaultRelativeKeyWidth * 0.82 && spacerWidth == 0f) { - // keys are very narrow, also rescale the functional keys to make keys a little wider - // 0.82 is just some guess for "too narrow" - val allKeyScale = 1f / (functionalKeyWidth + row.size * params.mDefaultRelativeKeyWidth) - keyWidth = params.mDefaultRelativeKeyWidth * allKeyScale - functionalKeysLeft.forEach { it.mRelativeWidth *= allKeyScale } - functionalKeysRight.forEach { it.mRelativeWidth *= allKeyScale } - } - for (key in row) { + // build list of functional keys of same size as baseKeys + val functionalKeysFromTop = functionalKeysTop.getOrNull(i) ?: emptyList() + val functionalKeysFromBottom = functionalKeysBottom.getOrNull(i - bottomIndexOffset) ?: emptyList() + functionalKeys.add(getFunctionalKeysBySide(functionalKeysFromTop, functionalKeysFromBottom)) + + row.map { key -> val extraFlags = if (key.label.length > 2 && key.label.codePointCount(0, key.label.length) > 2 && !isEmoji(key.label)) Key.LABEL_FLAGS_AUTO_X_SCALE else 0 - val keyData = key.compute(params) if (DebugFlags.DEBUG_ENABLED) - Log.d(TAG, "adding key ${keyData.label}, ${keyData.code}") - val keyParams = keyData.toKeyParams(params, keyWidth, defaultLabelFlags or extraFlags) - paramsRow.add(keyParams) + Log.d(TAG, "adding key ${key.label}, ${key.code}") + key.toKeyParams(params, defaultLabelFlags or extraFlags) } - if (spacerWidth != 0f) { - paramsRow.add(KeyParams.newSpacer(params, spacerWidth)) + } + return setReasonableWidths(baseKeyParams, functionalKeys) + } + + /** interprets key width -1, adjusts row size to nicely fit on screen, adds spacers if necessary */ + private fun setReasonableWidths(bassKeyParams: List>, functionalKeys: List, List>>): ArrayList> { + val keysInRows = ArrayList>() + // expand width = -1 keys and make sure rows fit on screen, insert spacers if necessary + bassKeyParams.forEachIndexed { i, keys -> + val (functionalKeysLeft, functionalKeysRight) = functionalKeys[i] + // sum up width, excluding -1 elements (put those in a separate list) + val varWidthKeys = mutableListOf() + var totalWidth = 0f + val allKeys = (functionalKeysLeft + keys + functionalKeysRight) + allKeys.forEach { + if (it.mWidth == -1f) varWidthKeys.add(it) + else totalWidth += it.mWidth + } + + // set width for varWidthKeys + if (varWidthKeys.isNotEmpty()) { + val width = if (totalWidth + varWidthKeys.size * params.mDefaultKeyWidth > 1) + params.mDefaultKeyWidth // never go below default width + else (1f - totalWidth) / varWidthKeys.size // split remaining space evenly + varWidthKeys.forEach { it.mWidth = width } + + // re-calculate total width + totalWidth = allKeys.sumOf { it.mWidth } + } + + // re-scale total width, or add spacers (or do nothing if totalWidth is near 1) + if (totalWidth < 0.9999f) { // add spacers + val spacerWidth = (1f - totalWidth) / 2 + val paramsRow = ArrayList(functionalKeysLeft + KeyParams.newSpacer(params, spacerWidth) + keys + + KeyParams.newSpacer(params, spacerWidth) + functionalKeysRight) + keysInRows.add(paramsRow) + } else { + if (totalWidth > 1.0001f) { // re-scale total width + val normalKeysWith = keys.sumOf { it.mWidth } + val functionalKeysWidth = totalWidth - normalKeysWith + val scaleFactor = (1f - functionalKeysWidth) / normalKeysWith + // re-scale normal keys if factor is > 0.82, otherwise re-scale all keys + if (scaleFactor > 0.82f) keys.forEach { it.mWidth *= scaleFactor } + else allKeys.forEach { it.mWidth /= totalWidth } + } + keysInRows.add(ArrayList(allKeys)) } - functionalKeysRight.forEach { paramsRow.add(it) } - keysInRows.add(paramsRow) } - resizeLastRowIfNecessaryForAlignment(keysInRows) - keysInRows.add(bottomRow) - if (params.mId.mNumberRowEnabled) - keysInRows.add(0, getNumberRow()) + + // adjust last normal row key widths to be aligned with row above, assuming a reasonably close-to-default alpha / symbol layout + // like in original layouts, e.g. for nordic and swiss layouts + if (!params.mId.isAlphaOrSymbolKeyboard || bassKeyParams.size < 3 || bassKeyParams.last().isNotEmpty()) + return keysInRows + val lastNormalRow = bassKeyParams[bassKeyParams.lastIndex - 1] + val rowAboveLast = bassKeyParams[bassKeyParams.lastIndex - 2] + val lastNormalRowKeyWidth = lastNormalRow.first().mWidth + val rowAboveLastNormalRowKeyWidth = rowAboveLast.first().mWidth + if (lastNormalRowKeyWidth <= rowAboveLastNormalRowKeyWidth + 0.0001f // no need + || lastNormalRowKeyWidth / rowAboveLastNormalRowKeyWidth > 1.1f // don't resize on large size difference + || lastNormalRow.any { it.isSpacer } || rowAboveLast.any { it.isSpacer } // annoying to deal with, and probably no resize wanted anyway + || lastNormalRow.any { it.mWidth != lastNormalRowKeyWidth } || rowAboveLast.any { it.mWidth != rowAboveLastNormalRowKeyWidth }) + return keysInRows + val numberOfKeysInLast = lastNormalRow.count { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL } + val widthBefore = numberOfKeysInLast * lastNormalRowKeyWidth + val widthAfter = numberOfKeysInLast * rowAboveLastNormalRowKeyWidth + val spacerWidth = (widthBefore - widthAfter) / 2 + // resize keys + lastNormalRow.forEach { if (it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL) it.mWidth = rowAboveLastNormalRowKeyWidth } + // add spacers + val lastNormalFullRow = keysInRows[keysInRows.lastIndex - 1] + lastNormalFullRow.add(lastNormalFullRow.indexOfFirst { it == lastNormalRow.first() }, KeyParams.newSpacer(params, spacerWidth)) + lastNormalFullRow.add(lastNormalFullRow.indexOfLast { it == lastNormalRow.last() } + 1, KeyParams.newSpacer(params, spacerWidth)) + return keysInRows } + /** + * adds / removes keys to the bottom row + * assumes a close-to-default bottom row consisting only of functional keys + * does nothing if not isAlphaOrSymbolKeyboard or assumptions not met + * adds an empty row to baseKeys, to have a baseKey row for the bottom functional row + */ + private fun adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys: MutableList>, baseKeys: MutableList>) { + val functionalKeysBottom = allFunctionalKeys.lastOrNull()?.toMutableList() ?: return + if (!params.mId.isAlphaOrSymbolKeyboard || functionalKeysBottom.isEmpty() || functionalKeysBottom.any { it.isKeyPlaceholder() }) + return + if (true /* Settings.getInstance().current.mSingleFunctionalLayout */) { // todo with the customizable functional layout + // remove unwanted keys (emoji, numpad, language switch) + if (!Settings.getInstance().current.mShowsEmojiKey || !params.mId.isAlphabetKeyboard) + functionalKeysBottom.removeFirst { it.label == KeyLabel.EMOJI } + if (!Settings.getInstance().current.isLanguageSwitchKeyEnabled || !params.mId.isAlphabetKeyboard) + functionalKeysBottom.removeFirst { it.label == KeyLabel.LANGUAGE_SWITCH } + if (params.mId.mElementId != KeyboardId.ELEMENT_SYMBOLS) + functionalKeysBottom.removeFirst { it.label == KeyLabel.NUMPAD } + } + // replace comma / period if 2 keys in normal bottom row + if (baseKeys.last().size == 2) { + Log.i("test", "$functionalKeysBottom") + functionalKeysBottom.replaceFirst( + { it.label == KeyLabel.COMMA || it.groupId == KeyData.GROUP_COMMA}, + { baseKeys.last()[0].copy(newGroupId = 1, newType = baseKeys.last()[0].type ?: it.type) } + ) + functionalKeysBottom.replaceFirst( + { it.label == KeyLabel.PERIOD || it.groupId == KeyData.GROUP_PERIOD}, + { baseKeys.last()[1].copy(newGroupId = 2, newType = baseKeys.last()[1].type ?: it.type) } + ) + Log.i("test", "$functionalKeysBottom") + baseKeys.removeLast() + } + // add those extra keys depending on layout (remove later) + val spaceIndex = functionalKeysBottom.indexOfFirst { it.label == KeyLabel.SPACE && it.width <= 0 } // 0 or -1 + if (spaceIndex >= 0) { + if (params.mLocaleKeyboardInfos.hasZwnjKey && params.mId.isAlphabetKeyboard) { + // add zwnj key next to space + functionalKeysBottom.add(spaceIndex + 1, TextKeyData(label = KeyLabel.ZWNJ)) + } else if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) { + // add / key next to space, todo (later): not any more, but keep it so this PR can be released without too many people complaining + functionalKeysBottom.add(spaceIndex + 1, TextKeyData(label = "/", type = KeyType.FUNCTION)) + } else if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { + // add < and > keys next to space, todo (later): not any more, but keep it so this PR can be released without too many people complaining + val key1 = TextKeyData( + label = "<", + popup = SimplePopups(listOf("!fixedColumnOrder!3", "‹", "≤", "«")), + labelFlags = Key.LABEL_FLAGS_HAS_POPUP_HINT, + type = KeyType.FUNCTION + ) + val key2 = TextKeyData( + label = ">", + popup = SimplePopups(listOf("!fixedColumnOrder!3", "›", "≥", "»")), + labelFlags = Key.LABEL_FLAGS_HAS_POPUP_HINT, + type = KeyType.FUNCTION + ) + functionalKeysBottom.add(spaceIndex + 1, key2) + functionalKeysBottom.add(spaceIndex, key1) + } + } + allFunctionalKeys[allFunctionalKeys.lastIndex] = functionalKeysBottom + baseKeys.add(emptyList()) + } + + // ideally we would get all functional keys in a nice list of pairs from the start, but at least it works... + private fun getFunctionalKeysBySide(functionalKeysFromTop: List, functionalKeysFromBottom: List): Pair, List> { + val (functionalKeysFromTopLeft, functionalKeysFromTopRight) = functionalKeysFromTop.splitAt { it.isKeyPlaceholder() } + val (functionalKeysFromBottomLeft, functionalKeysFromBottomRight) = functionalKeysFromBottom.splitAt { it.isKeyPlaceholder() } + // functional keys from top rows are the outermost, if there are some in the same row + functionalKeysFromTopLeft.addAll(functionalKeysFromBottomLeft) + functionalKeysFromBottomRight.addAll(functionalKeysFromTopRight) + val functionalKeysLeft = functionalKeysFromTopLeft.mapNotNull { it.processFunctionalKeys()?.toKeyParams(params) } + val functionalKeysRight = functionalKeysFromBottomRight.mapNotNull { it.processFunctionalKeys()?.toKeyParams(params) } + return functionalKeysLeft to functionalKeysRight + } + + // this is not nice in here, but otherwise we'd need context, and defaultLabelFlags and infos for toKeyParams + // improve it later, but currently this messy way is still ok + private fun KeyData.processFunctionalKeys(): KeyData? { + if (label == KeyLabel.PERIOD) { + // todo: why defaultLabelFlags exactly here? is this for armenian or bengali period labels? try removing also check in holo theme + return copy(newLabelFlags = labelFlags or defaultLabelFlags) + } + if (label == KeyLabel.SHIFT && !infos.hasShiftKey) return null + if (label != KeyLabel.ACTION) return this + return copy( + // todo: evaluating the label should actually only happen in toKeyParams + // this label change already makes it necessary to provide the background in here too, because toKeyParams can't use action as label + newLabel = "${getActionKeyLabel()}|${getActionKeyCode()}", + newPopup = popup.merge(getActionKeyPopupKeys()?.let { SimplePopups(it) }), + // the label change is messing with toKeyParams, so we need to supply the appropriate BG type here + newType = type ?: KeyType.ENTER_EDITING + ) + } + private fun addNumberRowOrPopupKeys(baseKeys: MutableList>) { if (!params.mId.mNumberRowEnabled && params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) { // replace first symbols row with number row, but use the labels as popupKeys @@ -194,7 +353,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co else SimpleKeyboardParser(params, context, false) parser.parseCoreLayout(getLayoutFile(layoutName, context).readText()) } else { - SimpleKeyboardParser(params, context, false).parseCoreLayout(context.readAssetsFile("layouts/$layoutName.txt")) + SimpleKeyboardParser(params, context, false).parseCoreLayout(context.readAssetsLayoutFile("$layoutName.txt")) } layout.forEachIndexed { i, row -> val baseRow = baseKeys.getOrNull(i) ?: return@forEachIndexed @@ -204,34 +363,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } } - // resize keys in last row if they are wider than keys in the row above - // this is done so the keys align with the keys above, like in original layouts - // e.g. for nordic and swiss layouts - private fun resizeLastRowIfNecessaryForAlignment(keysInRows: ArrayList>) { - if (keysInRows.size < 3) - return - val lastRow = keysInRows.last() - val rowAboveLast = keysInRows[keysInRows.lastIndex - 1] - if (lastRow.any { it.isSpacer } || rowAboveLast.any { it.isSpacer }) - return // annoying to deal with, and probably no resize needed anyway - val lastNormalRowKeyWidth = lastRow.first { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL }.mRelativeWidth - val rowAboveLastNormalRowKeyWidth = rowAboveLast.first { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL }.mRelativeWidth - if (lastNormalRowKeyWidth <= rowAboveLastNormalRowKeyWidth + 0.0001f) - return // no need - if (lastNormalRowKeyWidth / rowAboveLastNormalRowKeyWidth > 1.1f) - return // don't resize on large size difference - if (lastRow.any { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL && it.mRelativeWidth != lastNormalRowKeyWidth }) - return // normal keys have different width, don't deal with this - val numberOfNormalKeys = lastRow.count { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL } - val widthBefore = numberOfNormalKeys * lastNormalRowKeyWidth - val widthAfter = numberOfNormalKeys * rowAboveLastNormalRowKeyWidth - val spacerWidth = (widthBefore - widthAfter) / 2 - // resize keys and add spacers - lastRow.forEach { if (it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL) it.mRelativeWidth = rowAboveLastNormalRowKeyWidth } - lastRow.add(lastRow.indexOfFirst { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL }, KeyParams.newSpacer(params, spacerWidth)) - lastRow.add(lastRow.indexOfLast { it.mBackgroundType == Key.BACKGROUND_TYPE_NORMAL } + 1, KeyParams.newSpacer(params, spacerWidth)) - } - private fun createNumericRows(baseKeys: MutableList>): ArrayList> { val keysInRows = ArrayList>() if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && params.mId.mElementId != KeyboardId.ELEMENT_NUMPAD) { @@ -245,16 +376,15 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co row.forEach { key -> var keyParams: KeyParams? = null // try parsing a functional key - // todo: note that this is ignoring code on those keys, if any val functionalKeyName = when (key.label) { // todo (later): maybe add special popupKeys for phone and number layouts? - "." -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) "period" else "." - "," -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) "comma" else "," + "." -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) KeyLabel.PERIOD else "." + "," -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) KeyLabel.COMMA else "," else -> key.label } - if (functionalKeyName.length > 1 && key.type != KeyType.NUMERIC) { // todo: why exception for numeric? + if (functionalKeyName.length > 1 && key.type != KeyType.NUMERIC) { try { - keyParams = getFunctionalKeyParams(functionalKeyName) + keyParams = key.copy(newLabel = functionalKeyName).processFunctionalKeys()!!.toKeyParams(params) } catch (_: Throwable) {} // just use normal label } if (keyParams == null) { @@ -264,11 +394,11 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co KeyboardId.ELEMENT_PHONE_SYMBOLS -> 0 else -> Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO } - key.compute(params).toKeyParams(params, 0.17f, labelFlags or defaultLabelFlags) + key.toKeyParams(params, labelFlags or defaultLabelFlags) } else if (key.label.length == 1 && (params.mId.mElementId == KeyboardId.ELEMENT_PHONE || params.mId.mElementId == KeyboardId.ELEMENT_NUMBER)) - key.compute(params).toKeyParams(params, additionalLabelFlags = Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO or defaultLabelFlags) + key.toKeyParams(params, additionalLabelFlags = Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO or defaultLabelFlags) else - key.compute(params).toKeyParams(params, additionalLabelFlags = defaultLabelFlags) + key.toKeyParams(params, additionalLabelFlags = defaultLabelFlags) } if (key.type != KeyType.NUMERIC && keyParams.mBackgroundType != Key.BACKGROUND_TYPE_ACTION) keyParams.mBackgroundType = Key.BACKGROUND_TYPE_FUNCTIONAL @@ -296,256 +426,24 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co // make those keys same width as numeric keys except in numpad layout // but determine from row size instead of from elementId, in case user wants to adjust numpad layout if (row.size == baseKeys[0].size) { - paramsRow.getOrNull(n - 1)?.mRelativeWidth = paramsRow[n].mRelativeWidth - paramsRow.getOrNull(n + 1)?.mRelativeWidth = paramsRow[n].mRelativeWidth + paramsRow.getOrNull(n - 1)?.mWidth = paramsRow[n].mWidth + paramsRow.getOrNull(n + 1)?.mWidth = paramsRow[n].mWidth } else if (row.size == baseKeys[0].size + 2) { // numpad last row -> make sure the keys next to 0 fit nicely - paramsRow.getOrNull(n - 1)?.mRelativeWidth = paramsRow[n].mRelativeWidth * 0.55f - paramsRow.getOrNull(n - 2)?.mRelativeWidth = paramsRow[n].mRelativeWidth * 0.45f - paramsRow.getOrNull(n + 1)?.mRelativeWidth = paramsRow[n].mRelativeWidth * 0.55f - paramsRow.getOrNull(n + 2)?.mRelativeWidth = paramsRow[n].mRelativeWidth * 0.45f + paramsRow.getOrNull(n - 1)?.mWidth = paramsRow[n].mWidth * 0.55f + paramsRow.getOrNull(n - 2)?.mWidth = paramsRow[n].mWidth * 0.45f + paramsRow.getOrNull(n + 1)?.mWidth = paramsRow[n].mWidth * 0.55f + paramsRow.getOrNull(n + 2)?.mWidth = paramsRow[n].mWidth * 0.45f } } } - val widthSum = paramsRow.sumOf { it.mRelativeWidth } - paramsRow.forEach { it.mRelativeWidth /= widthSum } + val widthSum = paramsRow.sumOf { it.mWidth } + paramsRow.forEach { it.mWidth /= widthSum } keysInRows.add(paramsRow) } return keysInRows } - private fun parseFunctionalKeys(@StringRes id: Int): List, List>> = - context.getString(id).split("\n").mapNotNull { line -> - if (line.isBlank()) return@mapNotNull null - val p = line.split(";") - splitFunctionalKeyDefs(p.first()) to splitFunctionalKeyDefs(p.last()) - } - - private fun splitFunctionalKeyDefs(def: String): List { - if (def.isBlank()) return emptyList() - return def.split(",").filter { infos.hasShiftKey || !it.trim().startsWith("shift") } - } - - private fun getBottomRowAndAdjustBaseKeys(baseKeys: MutableList>): ArrayList { - val adjustableKeyCount = when (params.mId.mElementId) { - KeyboardId.ELEMENT_SYMBOLS -> 3 - KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> 4 - else -> 2 // must be alphabet, parser doesn't work for other elementIds - } - val adjustedKeys = if (baseKeys.last().size == adjustableKeyCount) baseKeys.last() - else null - if (adjustedKeys != null) - baseKeys.removeLast() - val bottomRow = ArrayList() - context.getString(R.string.key_def_bottom_row).split(",").forEach { - val key = it.trim().splitOnWhitespace().first() - val adjustKey = when (key) { - "comma" -> adjustedKeys?.first() - "period" -> adjustedKeys?.last() - else -> null - } - val keyParams = getFunctionalKeyParams(it, adjustKey?.label, adjustKey?.popup?.getPopupKeyLabels(params)) - if (key == "space") { // add the extra keys around space - if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) { - bottomRow.add(getFunctionalKeyParams(FunctionalKey.NUMPAD)) - bottomRow.add(keyParams) - bottomRow.add(KeyParams( - adjustedKeys?.get(1)?.label ?: "/", - params, - params.mDefaultRelativeKeyWidth, - defaultLabelFlags, - Key.BACKGROUND_TYPE_FUNCTIONAL, - adjustedKeys?.get(1)?.popup - )) - } else if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { - bottomRow.add(KeyParams( - (adjustedKeys?.get(1)?.label ?: "<").rtlLabel(params), - params, - params.mDefaultRelativeKeyWidth, - defaultLabelFlags or Key.LABEL_FLAGS_HAS_POPUP_HINT, - Key.BACKGROUND_TYPE_FUNCTIONAL, - adjustedKeys?.get(1)?.popup ?: SimplePopups(listOf("!fixedColumnOrder!3", "‹", "≤", "«")) - )) - bottomRow.add(keyParams) - bottomRow.add(KeyParams( - (adjustedKeys?.get(2)?.label ?: ">").rtlLabel(params), - params, - params.mDefaultRelativeKeyWidth, - defaultLabelFlags or Key.LABEL_FLAGS_HAS_POPUP_HINT, - Key.BACKGROUND_TYPE_FUNCTIONAL, - adjustedKeys?.get(2)?.popup ?: SimplePopups(listOf("!fixedColumnOrder!3", "›", "≥", "»")) - )) - } else { // alphabet - if (params.mId.mLanguageSwitchKeyEnabled) - bottomRow.add(getFunctionalKeyParams(FunctionalKey.LANGUAGE_SWITCH)) - if (params.mId.mEmojiKeyEnabled) - bottomRow.add(getFunctionalKeyParams(FunctionalKey.EMOJI)) - bottomRow.add(keyParams) - if (params.mLocaleKeyboardInfos.hasZwnjKey) - bottomRow.add(getFunctionalKeyParams(FunctionalKey.ZWNJ)) - } - } else { - bottomRow.add(keyParams) - } - } - // set space width - val space = bottomRow.first { it.mBackgroundType == Key.BACKGROUND_TYPE_SPACEBAR } - space.mRelativeWidth = 1f - bottomRow.filter { it != space }.sumOf { it.mRelativeWidth } - return bottomRow - } - - private fun getNumberRow(): ArrayList = - params.mLocaleKeyboardInfos.getNumberRow().mapTo(ArrayList()) { - it.toKeyParams(params, additionalLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) - } - - private fun getFunctionalKeyParams(def: String, label: String? = null, popupKeys: Collection? = null): KeyParams { - val split = def.trim().splitOnWhitespace() - val key = FunctionalKey.valueOf(split[0].uppercase()) - val width = if (split.size == 2) split[1].substringBefore("%").toFloat() / 100f - else params.mDefaultRelativeKeyWidth - return getFunctionalKeyParams(key, width, label, popupKeys) - } - - private fun getFunctionalKeyParams(key: FunctionalKey, relativeWidth: Float? = null, label: String? = null, popupKeys: Collection? = null): KeyParams { - // for comma and period: label will override default, popupKeys will be appended - val width = relativeWidth ?: params.mDefaultRelativeKeyWidth - return when (key) { - FunctionalKey.SYMBOL_ALPHA -> KeyParams( - if (params.mId.isAlphabetKeyboard) getToSymbolLabel() else params.mLocaleKeyboardInfos.labelAlphabet, - KeyCode.ALPHA_SYMBOL, - params, - width, - Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR, - Key.BACKGROUND_TYPE_FUNCTIONAL, - null - ) - FunctionalKey.SYMBOL -> KeyParams( - getToSymbolLabel(), - KeyCode.SYMBOL, - params, - width, - Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR, - Key.BACKGROUND_TYPE_FUNCTIONAL, - null - ) - FunctionalKey.ALPHA -> KeyParams( - params.mLocaleKeyboardInfos.labelAlphabet, - KeyCode.ALPHA, - params, - width, - Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR, - Key.BACKGROUND_TYPE_FUNCTIONAL, - null - ) - FunctionalKey.COMMA -> KeyParams( - label ?: getCommaLabel(), - params, - width, - Key.LABEL_FLAGS_HAS_POPUP_HINT, // previously only if normal comma, but always is more correct - if (label?.first()?.isLetter() == true) Key.BACKGROUND_TYPE_NORMAL // mimic behavior of old dvorak and halmak layouts - else Key.BACKGROUND_TYPE_FUNCTIONAL, - SimplePopups(popupKeys?.let { getCommaPopupKeys() + it } ?: getCommaPopupKeys()) - ) - FunctionalKey.PERIOD -> KeyParams( - label ?: getPeriodLabel(), - params, - width, - Key.LABEL_FLAGS_HAS_POPUP_HINT or defaultLabelFlags, - if (label?.first()?.isLetter() == true) Key.BACKGROUND_TYPE_NORMAL - else Key.BACKGROUND_TYPE_FUNCTIONAL, - SimplePopups(popupKeys?.let { getPunctuationPopupKeys() + it } ?: getPunctuationPopupKeys()) - ) - FunctionalKey.SPACE -> KeyParams( - getSpaceLabel(), - params, - width, // will not be used for normal space (only in number layouts) - if (params.mId.isNumberLayout) Key.LABEL_FLAGS_ALIGN_ICON_TO_BOTTOM else 0, - Key.BACKGROUND_TYPE_SPACEBAR, - null - ) - FunctionalKey.ACTION -> KeyParams( - "${getActionKeyLabel()}|${getActionKeyCode()}", - params, - width, - Key.LABEL_FLAGS_PRESERVE_CASE - or Key.LABEL_FLAGS_AUTO_X_SCALE - or Key.LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO - or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR - or Key.LABEL_FLAGS_HAS_POPUP_HINT - or KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId), - Key.BACKGROUND_TYPE_ACTION, - getActionKeyPopupKeys()?.let { SimplePopups(it) } - ) - FunctionalKey.DELETE -> KeyParams( - "!icon/delete_key|!code/key_delete", - params, - width, - 0, - Key.BACKGROUND_TYPE_FUNCTIONAL, - null - ) - FunctionalKey.SHIFT -> KeyParams( - "${getShiftLabel()}|!code/key_shift", - params, - width, - Key.LABEL_FLAGS_PRESERVE_CASE or if (!params.mId.isAlphabetKeyboard) Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR else 0, - // todo (later): possibly the whole stickyOn/Off stuff can be removed, currently it should only have a very slight effect in holo - if (params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED || params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) - Key.BACKGROUND_TYPE_STICKY_ON - else Key.BACKGROUND_TYPE_STICKY_OFF, - if (params.mId.isAlphabetKeyboard) SimplePopups(listOf("!noPanelAutoPopupKey!", " |!code/key_capslock")) else null // why the alphabet popup keys actually? - ) - FunctionalKey.EMOJI -> KeyParams( - "!icon/emoji_normal_key|!code/key_emoji", - params, - width, - KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId), - Key.BACKGROUND_TYPE_FUNCTIONAL, - null - ) - // tablet layout has an emoji key that changes to com key in url / mail - FunctionalKey.EMOJI_COM -> if (params.mId.mMode == KeyboardId.MODE_URL || params.mId.mMode == KeyboardId.MODE_EMAIL) - getFunctionalKeyParams(FunctionalKey.COM, width) - else getFunctionalKeyParams(FunctionalKey.EMOJI, width) - FunctionalKey.COM -> KeyParams( - // todo (later): label and popupKeys could be in localeKeyTexts, handled similar to currency key - // better not in the text files, because it should be handled per country - ".com", - params, - width, - Key.LABEL_FLAGS_AUTO_X_SCALE or Key.LABEL_FLAGS_FONT_NORMAL or Key.LABEL_FLAGS_HAS_POPUP_HINT or Key.LABEL_FLAGS_PRESERVE_CASE, - Key.BACKGROUND_TYPE_FUNCTIONAL, - SimplePopups(listOf(Key.POPUP_KEYS_HAS_LABELS, ".net", ".org", ".gov", ".edu")) - ) - FunctionalKey.LANGUAGE_SWITCH -> KeyParams( - "!icon/language_switch_key|!code/key_language_switch", - params, - width, - 0, - Key.BACKGROUND_TYPE_FUNCTIONAL, - null - ) - FunctionalKey.NUMPAD -> KeyParams( - "!icon/numpad_key|!code/key_numpad", - params, - width, - 0, - Key.BACKGROUND_TYPE_FUNCTIONAL, - null - ) - FunctionalKey.ZWNJ -> KeyParams( - "!icon/zwnj_key|\u200C", - params, - width, - Key.LABEL_FLAGS_HAS_POPUP_HINT, - // this may not be a good place to make this choice, but probably it's fine (though reading from settings here is not good) - if (Settings.getInstance().current.mColors.hasKeyBorders) Key.BACKGROUND_TYPE_SPACEBAR else Key.BACKGROUND_TYPE_NORMAL, - SimplePopups(listOf("!icon/zwj_key|\u200D")) - ) - } - } - private fun getActionKeyLabel(): String { if (params.mId.isMultiLine && (params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)) return "!icon/enter_key" @@ -627,7 +525,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } // remove emoji shortcut on enter in tablet mode (like original, because bottom row always has an emoji key) // (probably not necessary, but whatever) - if (isTablet() && popupKeys.remove("!icon/emoji_action_key|!code/key_emoji")) { + if (Settings.getInstance().isTablet && popupKeys.remove("!icon/emoji_action_key|!code/key_emoji")) { val i = popupKeys.indexOfFirst { it.startsWith(Key.POPUP_KEYS_FIXED_COLUMN_ORDER) } if (i > -1) { val n = popupKeys[i].substringAfter(Key.POPUP_KEYS_FIXED_COLUMN_ORDER).toIntOrNull() @@ -662,84 +560,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co return runInLocale(context, locale) { it.getString(id) } } - private fun getToSymbolLabel() = - if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS || params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) - params.mLocaleKeyboardInfos.labelAlphabet - else params.mLocaleKeyboardInfos.labelSymbol - - private fun getShiftLabel(): String { - val elementId = params.mId.mElementId - if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) - return params.mLocaleKeyboardInfos.labelSymbol - if (elementId == KeyboardId.ELEMENT_SYMBOLS) - return params.mLocaleKeyboardInfos.getShiftSymbolLabel(isTablet()) - if (elementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED - || elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED || elementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED) - return "!icon/shift_key_shifted" - return "!icon/shift_key" - } - - private fun getPeriodLabel(): String { - if (params.mId.isNumberLayout) return "." - if (params.mId.isAlphabetKeyboard || params.mId.locale.language in listOf("ar", "fa")) // todo: this exception is not so great... - return params.mLocaleKeyboardInfos.labelPeriod - return "." - } - - private fun getCommaLabel(): String { - if (params.mId.mMode == KeyboardId.MODE_URL && params.mId.isAlphabetKeyboard) - return "/" - if (params.mId.mMode == KeyboardId.MODE_EMAIL && params.mId.isAlphabetKeyboard) - return "\\@" - if (params.mId.isNumberLayout) - return "," - return params.mLocaleKeyboardInfos.labelComma - } - - private fun getCommaPopupKeys(): List { - val keys = mutableListOf() - if (!params.mId.mDeviceLocked) - keys.add("!icon/clipboard_normal_key|!code/key_clipboard") - if (!params.mId.mEmojiKeyEnabled && !params.mId.isNumberLayout) - keys.add("!icon/emoji_normal_key|!code/key_emoji") - if (!params.mId.mLanguageSwitchKeyEnabled) - keys.add("!icon/language_switch_key|!code/key_language_switch") - if (!params.mId.mOneHandedModeEnabled) - keys.add("!icon/start_onehanded_mode_key|!code/key_start_onehanded") - if (!params.mId.mDeviceLocked) - keys.add("!icon/settings_key|!code/key_settings") - return keys - } - - private fun getPunctuationPopupKeys(): List { - if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS || params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) - return listOf("…") - if (params.mId.isNumberLayout) - return listOf(":", "…", ";", "∞", "π", "√", "°", "^") - val popupKeys = params.mLocaleKeyboardInfos.getPopupKeys("punctuation")!!.toMutableList() - if (params.mId.mSubtype.isRtlSubtype) { - for (i in popupKeys.indices) - popupKeys[i] = popupKeys[i].rtlLabel(params) // for parentheses - } - if (isTablet() && popupKeys.contains("!") && popupKeys.contains("?")) { - // remove ! and ? keys and reduce number in autoColumnOrder - // this makes use of removal of empty popupKeys in PopupKeySpec.insertAdditionalPopupKeys - popupKeys[popupKeys.indexOf("!")] = "" - popupKeys[popupKeys.indexOf("?")] = "" - val columns = popupKeys[0].substringAfter(Key.POPUP_KEYS_AUTO_COLUMN_ORDER).toIntOrNull() - if (columns != null) - popupKeys[0] = "${Key.POPUP_KEYS_AUTO_COLUMN_ORDER}${columns - 1}" - } - return popupKeys - } - - private fun getSpaceLabel(): String = - if (params.mId.mElementId <= KeyboardId.ELEMENT_SYMBOLS_SHIFTED) - "!icon/space_key|!code/key_space" - else "!icon/space_key_for_number_layout|!code/key_space" - - private fun isTablet() = context.resources.getInteger(R.integer.config_screen_metrics) >= 3 - companion object { private const val TAG = "KeyboardParser" @@ -755,15 +575,15 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } val layoutFileNames = context.assets.list("layouts")!! if (layoutFileNames.contains("$layoutName.json")) { - return JsonKeyboardParser(params, context).parseLayoutString(context.readAssetsFile("layouts${File.separator}$layoutName.json")) + return JsonKeyboardParser(params, context).parseLayoutString(context.readAssetsLayoutFile("$layoutName.json")) } if (layoutFileNames.contains("$layoutName.txt")) { - return SimpleKeyboardParser(params, context).parseLayoutString(context.readAssetsFile("layouts${File.separator}$layoutName.txt")) + return SimpleKeyboardParser(params, context).parseLayoutString(context.readAssetsLayoutFile("$layoutName.txt")) } throw IllegalStateException("can't parse layout $layoutName with id ${params.mId} and elementId ${params.mId.mElementId}") } - private fun Context.readAssetsFile(name: String) = assets.open(name).reader().readText() + private fun Context.readAssetsLayoutFile(name: String) = assets.open("layouts${File.separator}$name").reader().readText() private fun getLayoutFileName(params: KeyboardParams, context: Context, overrideElementId: Int? = null): String { var checkForCustom = true @@ -815,10 +635,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } } - protected enum class FunctionalKey { - EMOJI, LANGUAGE_SWITCH, COM, EMOJI_COM, ACTION, DELETE, PERIOD, COMMA, SPACE, SHIFT, NUMPAD, SYMBOL, ALPHA, SYMBOL_ALPHA, ZWNJ - } - } // todo: actually this should be in some separate file diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt index bd8015ece..d11b206c4 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt @@ -14,7 +14,7 @@ object KeyCode { const val INTERNAL_FLORIS_MIN = -9999 const val INTERNAL_FLORIS_MAX = -1 - val INTERNAL_FLORIS = INTERNAL_FLORIS_MIN..INTERNAL_FLORIS_MAX + val INTERNAL_FLORIS = INTERNAL_FLORIS_MIN..INTERNAL_FLORIS_MAX // do NOT add key codes in this range val INTERNAL_HELI = -19999..-10000 // for keys exclusive to this app val CURRENCY = CURRENCY_SLOT_6..CURRENCY_SLOT_1 } @@ -122,7 +122,7 @@ object KeyCode { const val CJK_SPACE = 12288 // heliboard only codes - const val ALPHA_SYMBOL = -10001 + const val SYMBOL_ALPHA = -10001 const val START_ONE_HANDED_MODE = -10002 const val STOP_ONE_HANDED_MODE = -10003 const val SWITCH_ONE_HANDED_MODE = -10004 @@ -132,8 +132,8 @@ object KeyCode { // Code value representing the code is not specified. const val NOT_SPECIFIED = -10008 // todo: not sure if there is need to have the "old" unspecified keyCode different, just test it and maybe merge const val CLIPBOARD_COPY_ALL = -10009 - const val PAGE_UP = -10010 - const val PAGE_DOWN = -10011 + const val PAGE_UP = -10010 + const val PAGE_DOWN = -10011 /** to make sure a FlorisBoard code works when reading a JSON layout */ fun Int.checkAndConvertCode(): Int = if (this > 0) this else when (this) { @@ -145,8 +145,8 @@ object KeyCode { SHIFT, CAPS_LOCK, MULTIPLE_CODE_POINTS, UNSPECIFIED, // heliboard only - ALPHA_SYMBOL, START_ONE_HANDED_MODE, STOP_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SHIFT_ENTER, - ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED + SYMBOL_ALPHA, START_ONE_HANDED_MODE, STOP_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SHIFT_ENTER, + ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, PAGE_UP, PAGE_DOWN -> this // conversion @@ -156,26 +156,4 @@ object KeyCode { else -> throw IllegalStateException("key code $this not yet supported") } - - /** to make sure a FlorisBoard label works when reading a JSON layout */ - // resulting special labels should be names of FunctionalKey enum, case insensitive - fun String.convertFlorisLabel(): String = when (this) { - "view_characters" -> "alpha" - "view_symbols" -> "symbol" - "view_numeric_advanced" -> "numpad" - "view_phone" -> "alpha" // phone keyboard is treated like alphabet, just with different layout - "view_phone2" -> "symbols" // phone symbols - "ime_ui_mode_media" -> "emoji" - "ime_ui_mode_clipboard" -> "clipboard" // todo: is this supported? when yes -> add to readme, and add a test - "ime_ui_mode_text" -> "alpha" - "currency_slot_1" -> "$$$" - "currency_slot_2" -> "$$$1" - "currency_slot_3" -> "$$$2" - "currency_slot_4" -> "$$$3" - "currency_slot_5" -> "$$$4" - "currency_slot_6" -> "$$$5" - "enter" -> "action" - "half_space" -> "zwnj" - else -> this - } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt new file mode 100644 index 000000000..b444045d1 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt @@ -0,0 +1,48 @@ +package helium314.keyboard.keyboard.internal.keyboard_parser.floris + +/** labels for functional / special keys */ +object KeyLabel { + const val EMOJI = "emoji" + const val COM = "com" + const val LANGUAGE_SWITCH = "language_switch" + const val ACTION = "action" + const val DELETE = "delete" + const val SHIFT = "shift" + const val NUMPAD = "numpad" + const val SYMBOL = "symbol" + const val ALPHA = "alpha" + const val SYMBOL_ALPHA = "symbol_alpha" + const val PERIOD = "period" + const val COMMA = "comma" + const val SPACE = "space" + const val ZWNJ = "zwnj" + const val CURRENCY = "$$$" + const val CURRENCY1 = "$$$1" + const val CURRENCY2 = "$$$2" + const val CURRENCY3 = "$$$3" + const val CURRENCY4 = "$$$4" + const val CURRENCY5 = "$$$5" + + /** to make sure a FlorisBoard label works when reading a JSON layout */ + // resulting special labels should be names of FunctionalKey enum, case insensitive + fun String.convertFlorisLabel(): String = when (this) { + "view_characters" -> ALPHA + "view_symbols" -> SYMBOL + "view_numeric_advanced" -> NUMPAD + "view_phone" -> ALPHA // phone keyboard is treated like alphabet, just with different layout + "view_phone2" -> SYMBOL // phone symbols + "ime_ui_mode_media" -> EMOJI + "ime_ui_mode_clipboard" -> "clipboard" // todo: is this supported? when yes -> add to readme, and add a test + "ime_ui_mode_text" -> ALPHA + "currency_slot_1" -> CURRENCY + "currency_slot_2" -> CURRENCY1 + "currency_slot_3" -> CURRENCY2 + "currency_slot_4" -> CURRENCY3 + "currency_slot_5" -> CURRENCY4 + "currency_slot_6" -> CURRENCY5 + "enter" -> ACTION + "half_space" -> ZWNJ + else -> this + } + +} diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyType.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyType.kt index d6e93c255..41444db99 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyType.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyType.kt @@ -21,18 +21,16 @@ import kotlinx.serialization.encoding.Encoder */ @Serializable(with = KeyTypeSerializer::class) enum class KeyType { - // todo: implement the effect on background - // also, how to get that specific space bar background? CHARACTER, // default - ENTER_EDITING, // enter/insert/delete, gets functional key background (if not action key) + ENTER_EDITING, // should be enter/insert/delete, but always gets action key background FUNCTION, // f1..., gets functional key background - LOCK, // scroll lock, num lock, caps lock, gets functional key background + LOCK, // scroll lock, num lock, caps lock, gets sticky on/off background, which currently is the same as functional background MODIFIER, // alt, ctrl, shift, gets functional key background - NAVIGATION, // home, page up, page down, tab, arrows, geta default background - SYSTEM_GUI, // esc, print, pause, meta, (keyboard layout switch), geta functional background - NUMERIC, // numpad keys, get larger letter and larger width - PLACEHOLDER, // other keys go here, e.g. in shift, placeholder, delete the placeholder gets (typically) replaced by the bottom keyboard row - UNSPECIFIED; // treated like default + NAVIGATION, // home, page up, page down, tab, arrows, gets space background because it'S still the most suitable type + SYSTEM_GUI, // esc, print, pause, meta, (keyboard layout switch), gets functional background + NUMERIC, // numpad keys, get larger letter and larger width in number layouts, and default background + PLACEHOLDER, // spacer, or actual placeholder when used in functional key layouts + UNSPECIFIED; // empty background override fun toString(): String { return super.toString().lowercase() @@ -40,7 +38,13 @@ enum class KeyType { companion object { fun fromString(string: String): KeyType { - return valueOf(string.uppercase()) + // resolve alternative names + return when (string) { + "space" -> NAVIGATION + "action" -> ENTER_EDITING + "shift" -> LOCK + else -> valueOf(string.uppercase()) + } } } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/PopupSet.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/PopupSet.kt index 33347d6a6..4f239d951 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/PopupSet.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/PopupSet.kt @@ -6,30 +6,49 @@ package helium314.keyboard.keyboard.internal.keyboard_parser.floris import kotlinx.serialization.Serializable -import helium314.keyboard.keyboard.internal.KeySpecParser import helium314.keyboard.keyboard.internal.KeyboardParams +import helium314.keyboard.latin.utils.addCollections // only the constructor and name remain from FlorisBoard // we don't care about the difference between main and relevant (at least for now) @Serializable open class PopupSet( open val main: T? = null, - open val relevant: List? = null + open val relevant: Collection? = null ) { // get labels of all popup keys open fun getPopupKeyLabels(params: KeyboardParams): Collection? { if (main == null && relevant == null) return null val popupKeys = mutableListOf() - main?.getPopupLabel(params)?.let { popupKeys.add(it) } - relevant?.let { popupKeys.addAll(it.map { it.getPopupLabel(params) }) } + main?.compute(params)?.getPopupLabel(params)?.let { popupKeys.add(it) } + relevant?.let { popupKeys.addAll(it.mapNotNull { it.compute(params)?.getPopupLabel(params) }) } if (popupKeys.isEmpty()) return null return popupKeys } + open fun isEmpty(): Boolean = main == null && relevant.isNullOrEmpty() var numberIndex: Int? = null var symbol: String? = null // maybe list of keys? + + fun merge(other: PopupSet?): PopupSet { + if (other == null || other.isEmpty()) return this + if (this.isEmpty()) return other + if (this is SimplePopups) { + if (other is SimplePopups) + return SimplePopups(addCollections(popupKeys, other.popupKeys)) + return PopupSet(other.main, addCollections(popupKeys?.map { it.toTextKey() }, other.relevant)) + } else if (other is SimplePopups) { + return PopupSet(main, addCollections(relevant, other.popupKeys?.map { it.toTextKey() })) + } + val newMain = if (main == null) other.main else main + val newRelevant = addCollections(relevant, other.relevant) + if (main != null && other.main != null) + return PopupSet(newMain, addCollections(listOf(other.main!!), newRelevant)) + return PopupSet(newMain, newRelevant) + } } class SimplePopups(val popupKeys: Collection?) : PopupSet() { override fun getPopupKeyLabels(params: KeyboardParams) = popupKeys + override fun isEmpty(): Boolean = popupKeys.isNullOrEmpty() } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index 800792692..1cd18a169 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -9,12 +9,16 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import helium314.keyboard.keyboard.Key +import helium314.keyboard.keyboard.KeyboardId +import helium314.keyboard.keyboard.KeyboardTheme +import helium314.keyboard.keyboard.internal.KeyboardIconsSet import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode.checkAndConvertCode -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode.convertFlorisLabel +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyLabel.convertFlorisLabel import helium314.keyboard.keyboard.internal.keyboard_parser.rtlLabel import helium314.keyboard.latin.common.Constants import helium314.keyboard.latin.common.StringUtils +import helium314.keyboard.latin.settings.Settings // taken from FlorisBoard, small modifications (see also KeyData) // internal keys removed (currently no plan to support them) @@ -32,13 +36,17 @@ import helium314.keyboard.latin.common.StringUtils * @property popup The popups for ths key. Can also dynamically be provided via popup extensions. */ sealed interface KeyData : AbstractKeyData { - val type: KeyType + val type: KeyType? val code: Int val label: String val groupId: Int - val popup: PopupSet // not nullable because can't add number otherwise + val popup: PopupSet // not nullable because can't add number otherwise + val width: Float // in percent of keyboard width, 0 is default (depends on key), -1 is fill (like space bar) val labelFlags: Int + fun copy(newType: KeyType? = type, newCode: Int = code, newLabel: String = label, newGroupId: Int = groupId, + newPopup: PopupSet = popup, newWidth: Float = width, newLabelFlags: Int = labelFlags): KeyData + // groups (currently) not supported companion object { /** @@ -49,15 +57,15 @@ sealed interface KeyData : AbstractKeyData { /** * Constant for the Left modifier key group. Any key belonging to this group will get the - * popups specified for "~left" in the popup mapping. + * popups specified for the comma key. */ - const val GROUP_LEFT: Int = 1 + const val GROUP_COMMA: Int = 1 /** * Constant for the right modifier key group. Any key belonging to this group will get the - * popups specified for "~right" in the popup mapping. + * popups specified for the period key. */ - const val GROUP_RIGHT: Int = 2 + const val GROUP_PERIOD: Int = 2 /** * Constant for the enter modifier key group. Any key belonging to this group will get the @@ -70,100 +78,276 @@ sealed interface KeyData : AbstractKeyData { * popups specified for "~kana" in the popup mapping. */ const val GROUP_KANA: Int = 97 + + private fun getShiftLabel(params: KeyboardParams) = when (params.mId.mElementId) { + KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> params.mLocaleKeyboardInfos.labelSymbol + KeyboardId.ELEMENT_SYMBOLS -> params.mLocaleKeyboardInfos.getShiftSymbolLabel(Settings.getInstance().isTablet) + KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED, KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, + KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED, KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED -> "!icon/${KeyboardIconsSet.NAME_SHIFT_KEY_SHIFTED}" + else -> "!icon/${KeyboardIconsSet.NAME_SHIFT_KEY}" + } + + // todo (later): try avoiding this weirdness + // maybe just remove it and if users want it they can use custom functional layouts? + // but it has been like this "forever" and actually seems to make sense + private fun getPeriodLabel(params: KeyboardParams): String { + if (params.mId.isNumberLayout) return "." + if (params.mId.isAlphabetKeyboard || params.mId.locale.language in listOf("ar", "fa")) + return params.mLocaleKeyboardInfos.labelPeriod + return "." + } + + private fun getSpaceLabel(params: KeyboardParams): String = + if (params.mId.mElementId <= KeyboardId.ELEMENT_SYMBOLS_SHIFTED) + "!icon/space_key|!code/key_space" + else "!icon/space_key_for_number_layout|!code/key_space" + + private fun getCommaPopupKeys(params: KeyboardParams): List { + val keys = mutableListOf() + if (!params.mId.mDeviceLocked) + keys.add("!icon/clipboard_normal_key|!code/key_clipboard") + if (!params.mId.mEmojiKeyEnabled && !params.mId.isNumberLayout) + keys.add("!icon/emoji_normal_key|!code/key_emoji") + if (!params.mId.mLanguageSwitchKeyEnabled) + keys.add("!icon/language_switch_key|!code/key_language_switch") + if (!params.mId.mOneHandedModeEnabled) + keys.add("!icon/start_onehanded_mode_key|!code/key_start_onehanded") + if (!params.mId.mDeviceLocked) + keys.add("!icon/settings_key|!code/key_settings") + return keys + } + + private fun getPunctuationPopupKeys(params: KeyboardParams): List { + if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS || params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) + return listOf("…") + if (params.mId.isNumberLayout) + return listOf(":", "…", ";", "∞", "π", "√", "°", "^") + val popupKeys = params.mLocaleKeyboardInfos.getPopupKeys("punctuation")!!.toMutableList() + if (params.mId.mSubtype.isRtlSubtype) { + for (i in popupKeys.indices) + popupKeys[i] = popupKeys[i].rtlLabel(params) // for parentheses + } + if (Settings.getInstance().isTablet && popupKeys.contains("!") && popupKeys.contains("?")) { + // remove ! and ? keys and reduce number in autoColumnOrder + // this makes use of removal of empty popupKeys in PopupKeySpec.insertAdditionalPopupKeys + popupKeys[popupKeys.indexOf("!")] = "" + popupKeys[popupKeys.indexOf("?")] = "" + val columns = popupKeys[0].substringAfter(Key.POPUP_KEYS_AUTO_COLUMN_ORDER).toIntOrNull() + if (columns != null) + popupKeys[0] = "${Key.POPUP_KEYS_AUTO_COLUMN_ORDER}${columns - 1}" + } + return popupKeys + } } // make it non-nullable for simplicity, and to reflect current implementations override fun compute(params: KeyboardParams): KeyData { + require(groupId <= GROUP_ENTER) { "only groups up to GROUP_ENTER are supported" } + require(label.isNotEmpty() || type == KeyType.PLACEHOLDER || code != KeyCode.UNSPECIFIED) { "non-placeholder key has no code and no label" } val newLabel = label.convertFlorisLabel() val newCode = code.checkAndConvertCode() - // resolve currency keys - if (newLabel.startsWith("$$$") || newCode in KeyCode.Spec.CURRENCY) { - val currencyKey = params.mLocaleKeyboardInfos.currencyKey - val currencyCodeAsString = if (newCode in KeyCode.Spec.CURRENCY) { - when (newCode) { - KeyCode.CURRENCY_SLOT_1 -> "|" + currencyKey.first - KeyCode.CURRENCY_SLOT_2 -> "|" + currencyKey.second[0] - KeyCode.CURRENCY_SLOT_3 -> "|" + currencyKey.second[1] - KeyCode.CURRENCY_SLOT_4 -> "|" + currencyKey.second[2] - KeyCode.CURRENCY_SLOT_5 -> "|" + currencyKey.second[3] - KeyCode.CURRENCY_SLOT_6 -> "|" + currencyKey.second[4] - else -> "" - } - } else "" - if (newLabel == "$$$") { - val finalLabel = currencyKey.first + currencyCodeAsString - // the flag is to match old parser, but why is it there for main currency key and not for others? - return TextKeyData(type, KeyCode.UNSPECIFIED, finalLabel, groupId, SimplePopups(currencyKey.second), labelFlags or Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO) - } - val n = newLabel.substringAfter("$$$").toIntOrNull() - if (n != null && n <= 5 && n > 0) { - val finalLabel = currencyKey.second[n - 1] + currencyCodeAsString - return TextKeyData(type, KeyCode.UNSPECIFIED, finalLabel, groupId, popup, labelFlags) - } - } if (newCode != code || newLabel != label) - return TextKeyData(type, newCode, newLabel, groupId, popup, labelFlags).compute(params) + return copy(newCode = newCode, newLabel = newLabel) return this } fun isSpaceKey(): Boolean { - return type == KeyType.CHARACTER && (code == Constants.CODE_SPACE || code == KeyCode.CJK_SPACE - || code == KeyCode.ZWNJ || code == KeyCode.KESHIDA) + return code == Constants.CODE_SPACE || code == KeyCode.CJK_SPACE || code == KeyCode.ZWNJ || code == KeyCode.KESHIDA } - fun toKeyParams(params: KeyboardParams, width: Float = params.mDefaultRelativeKeyWidth, additionalLabelFlags: Int = 0): Key.KeyParams { - // todo: remove checks here, do only when reading json layouts - // numeric keys are assigned a higher width in number layouts - require(type == KeyType.CHARACTER || type == KeyType.NUMERIC) { "only KeyType CHARACTER or NUMERIC is supported" } - // allow GROUP_ENTER negative codes so original florisboard number layouts can be used, bu actually it's ignored - require(groupId == GROUP_DEFAULT || groupId == GROUP_ENTER) { "currently only GROUP_DEFAULT or GROUP_ENTER is supported" } - require(code != KeyCode.UNSPECIFIED || label.isNotEmpty()) { "key has no code and no label" } + fun isKeyPlaceholder() = type == KeyType.PLACEHOLDER && code == KeyCode.UNSPECIFIED && width == 0f + + /** this expects that codes and labels are already converted from FlorisBoard values, usually through compute */ + fun toKeyParams(params: KeyboardParams, additionalLabelFlags: Int = 0): Key.KeyParams { + if (type == KeyType.PLACEHOLDER) return Key.KeyParams.newSpacer(params, width) + + val newWidth = if (width == 0f) getDefaultWidth(params) else width + val newCode: Int + val newLabel: String + if (code in KeyCode.Spec.CURRENCY) { + // special treatment necessary, because we may need to encode it in the label + // (currency is a string, so might have more than 1 codepoint) + newCode = 0 + val l = processLabel(params) + newLabel = when (code) { + // consider currency codes for label + KeyCode.CURRENCY_SLOT_1 -> "$l|${params.mLocaleKeyboardInfos.currencyKey.first}" + KeyCode.CURRENCY_SLOT_2 -> "$l|${params.mLocaleKeyboardInfos.currencyKey.second[0]}" + KeyCode.CURRENCY_SLOT_3 -> "$l|${params.mLocaleKeyboardInfos.currencyKey.second[1]}" + KeyCode.CURRENCY_SLOT_4 -> "$l|${params.mLocaleKeyboardInfos.currencyKey.second[2]}" + KeyCode.CURRENCY_SLOT_5 -> "$l|${params.mLocaleKeyboardInfos.currencyKey.second[3]}" + KeyCode.CURRENCY_SLOT_6 -> "$l|${params.mLocaleKeyboardInfos.currencyKey.second[4]}" + else -> throw IllegalStateException("code in currency range, but not in currency range?") + } + } else { + newCode = processCode() + newLabel = processLabel(params) + } + val newLabelFlags = labelFlags or additionalLabelFlags or getAdditionalLabelFlags(params) + val newPopupKeys = popup.merge(getAdditionalPopupKeys(params)) - return if (code == KeyCode.UNSPECIFIED || code == KeyCode.MULTIPLE_CODE_POINTS) { + val background = when (type) { + KeyType.CHARACTER, KeyType.NUMERIC -> Key.BACKGROUND_TYPE_NORMAL + KeyType.FUNCTION, KeyType.MODIFIER, KeyType.SYSTEM_GUI -> Key.BACKGROUND_TYPE_FUNCTIONAL + KeyType.PLACEHOLDER, KeyType.UNSPECIFIED -> Key.BACKGROUND_TYPE_EMPTY + KeyType.NAVIGATION -> Key.BACKGROUND_TYPE_SPACEBAR + KeyType.ENTER_EDITING -> Key.BACKGROUND_TYPE_ACTION + KeyType.LOCK -> getShiftBackground(params) + null -> getDefaultBackground(params) + } + + return if (newCode == KeyCode.UNSPECIFIED || newCode == KeyCode.MULTIPLE_CODE_POINTS) { // code will be determined from label if possible (i.e. label is single code point) // but also longer labels should work without issues, also for MultiTextKeyData if (this is MultiTextKeyData) { val outputText = String(codePoints, 0, codePoints.size) Key.KeyParams( - "$label|$outputText", - code, + "$newLabel|$outputText", + newCode, params, - width, - labelFlags or additionalLabelFlags, - Key.BACKGROUND_TYPE_NORMAL, // todo (when supported): determine type - popup, + newWidth, + newLabelFlags, + background, + newPopupKeys, ) } else { Key.KeyParams( - label.rtlLabel(params), // todo (when supported): convert special labels to keySpec + newLabel.rtlLabel(params), // todo (when supported): convert special labels to keySpec params, - width, - labelFlags or additionalLabelFlags, - Key.BACKGROUND_TYPE_NORMAL, // todo (when supported): determine type - popup, + newWidth, + newLabelFlags, + background, + newPopupKeys, ) } } else { Key.KeyParams( - label.ifEmpty { StringUtils.newSingleCodePointString(code) }, - code, + newLabel.ifEmpty { StringUtils.newSingleCodePointString(newCode) }, + newCode, params, - width, - labelFlags or additionalLabelFlags, - Key.BACKGROUND_TYPE_NORMAL, - popup, + newWidth, + newLabelFlags, + background, + newPopupKeys, ) } } + + private fun getDefaultBackground(params: KeyboardParams): Int { + // functional keys + when (label) { // or use code? + KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL, KeyLabel.ALPHA, KeyLabel.COMMA, KeyLabel.PERIOD, KeyLabel.DELETE, + KeyLabel.EMOJI, KeyLabel.COM, KeyLabel.LANGUAGE_SWITCH, KeyLabel.NUMPAD -> return Key.BACKGROUND_TYPE_FUNCTIONAL + KeyLabel.SPACE, KeyLabel.ZWNJ -> return Key.BACKGROUND_TYPE_SPACEBAR + KeyLabel.ACTION -> return Key.BACKGROUND_TYPE_ACTION + KeyLabel.SHIFT -> return getShiftBackground(params) + } + if (type == KeyType.PLACEHOLDER) return Key.BACKGROUND_TYPE_EMPTY + return Key.BACKGROUND_TYPE_NORMAL + } + + // todo (later): possibly the whole stickyOn/Off stuff can be removed, currently it should only have a very slight effect in holo + // but iirc there is some attempt in reviving the sticky thing, right? + private fun getShiftBackground(params: KeyboardParams): Int { + return if (params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED + || params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) Key.BACKGROUND_TYPE_STICKY_ON + else Key.BACKGROUND_TYPE_STICKY_OFF + } + + private fun getDefaultWidth(params: KeyboardParams): Float { + return if (label == KeyLabel.SPACE && params.mId.isAlphaOrSymbolKeyboard) -1f + else if (type == KeyType.NUMERIC && params.mId.isNumberLayout) 0.17f // todo (later) consider making this -1? + else params.mDefaultKeyWidth + } + + // todo (later): encoding the code in the label should be avoided, because we know it already + private fun processLabel(params: KeyboardParams): String = when (label) { + KeyLabel.SYMBOL_ALPHA -> if (params.mId.isAlphabetKeyboard) params.mLocaleKeyboardInfos.labelSymbol else params.mLocaleKeyboardInfos.labelAlphabet + KeyLabel.SYMBOL -> params.mLocaleKeyboardInfos.labelSymbol + KeyLabel.ALPHA -> params.mLocaleKeyboardInfos.labelAlphabet + KeyLabel.COMMA -> params.mLocaleKeyboardInfos.labelComma + KeyLabel.PERIOD -> getPeriodLabel(params) + KeyLabel.SPACE -> getSpaceLabel(params) +// KeyLabel.ACTION -> "${getActionKeyLabel(params)}|${getActionKeyCode(params)}" would need context + KeyLabel.DELETE -> "!icon/delete_key|!code/key_delete" + KeyLabel.SHIFT -> "${getShiftLabel(params)}|!code/key_shift" + KeyLabel.EMOJI -> "!icon/emoji_normal_key|!code/key_emoji" + // todo (later): label and popupKeys for .com should be in localeKeyTexts, handled similar to currency key + KeyLabel.COM -> ".com" + KeyLabel.LANGUAGE_SWITCH -> "!icon/language_switch_key|!code/key_language_switch" + KeyLabel.NUMPAD -> "!icon/numpad_key|!code/key_numpad" + KeyLabel.ZWNJ -> "!icon/zwnj_key|\u200C" + KeyLabel.CURRENCY -> params.mLocaleKeyboardInfos.currencyKey.first + KeyLabel.CURRENCY1 -> params.mLocaleKeyboardInfos.currencyKey.second[0] + KeyLabel.CURRENCY2 -> params.mLocaleKeyboardInfos.currencyKey.second[1] + KeyLabel.CURRENCY3 -> params.mLocaleKeyboardInfos.currencyKey.second[2] + KeyLabel.CURRENCY4 -> params.mLocaleKeyboardInfos.currencyKey.second[3] + KeyLabel.CURRENCY5 -> params.mLocaleKeyboardInfos.currencyKey.second[4] + else -> label + } + + private fun processCode(): Int { + if (code != KeyCode.UNSPECIFIED) return code + return when (label) { + KeyLabel.SYMBOL_ALPHA -> KeyCode.SYMBOL_ALPHA + KeyLabel.SYMBOL -> KeyCode.SYMBOL + KeyLabel.ALPHA -> KeyCode.ALPHA + else -> code + } + } + + // todo (later): add explanations / reasoning, often this is just taken from conversion from AOSP layouts + private fun getAdditionalLabelFlags(params: KeyboardParams): Int { + return when (label) { + KeyLabel.ALPHA, KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL -> Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR + KeyLabel.PERIOD, KeyLabel.COMMA -> Key.LABEL_FLAGS_HAS_POPUP_HINT // todo: period also has defaultLabelFlags -> when is this relevant? + KeyLabel.ACTION -> { + Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_AUTO_X_SCALE or + Key.LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR or + Key.LABEL_FLAGS_HAS_POPUP_HINT or KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) + } + KeyLabel.SPACE -> if (params.mId.isNumberLayout) Key.LABEL_FLAGS_ALIGN_ICON_TO_BOTTOM else 0 + KeyLabel.SHIFT -> Key.LABEL_FLAGS_PRESERVE_CASE or if (!params.mId.isAlphabetKeyboard) Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR else 0 + KeyLabel.EMOJI -> KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) + KeyLabel.COM -> Key.LABEL_FLAGS_AUTO_X_SCALE or Key.LABEL_FLAGS_FONT_NORMAL or Key.LABEL_FLAGS_HAS_POPUP_HINT or Key.LABEL_FLAGS_PRESERVE_CASE + KeyLabel.ZWNJ -> Key.LABEL_FLAGS_HAS_POPUP_HINT + KeyLabel.CURRENCY -> Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO + else -> 0 + } + } + + private fun getAdditionalPopupKeys(params: KeyboardParams): PopupSet? { + if (groupId == GROUP_COMMA) return SimplePopups(getCommaPopupKeys(params)) + if (groupId == GROUP_PERIOD) return SimplePopups(getPunctuationPopupKeys(params)) +// if (groupId == GROUP_ENTER) return getActionKeyPopupKeys(params)?.let { SimplePopups(it) } + return when (label) { + KeyLabel.COMMA -> SimplePopups(getCommaPopupKeys(params)) + KeyLabel.PERIOD -> SimplePopups(getPunctuationPopupKeys(params)) +// KeyLabel.ACTION -> getActionKeyPopupKeys(params)?.let { SimplePopups(it) } + KeyLabel.SHIFT -> { + if (params.mId.isAlphabetKeyboard) SimplePopups( + listOf( + "!noPanelAutoPopupKey!", + " |!code/key_capslock" + ) + ) else null // why the alphabet popup keys actually? + } + KeyLabel.COM -> SimplePopups(listOf(Key.POPUP_KEYS_HAS_LABELS, ".net", ".org", ".gov", ".edu")) + KeyLabel.ZWNJ -> SimplePopups(listOf("!icon/zwj_key|\u200D")) + // only add currency popups if there are none defined on the key + KeyLabel.CURRENCY -> if (popup.isEmpty()) SimplePopups(params.mLocaleKeyboardInfos.currencyKey.second) else null + else -> null + } + } } /** * Data class which describes a single key and its attributes. * * @property type The type of the key. Some actions require both [code] and [type] to match in order - * to be successfully executed. Defaults to [KeyType.CHARACTER]. + * to be successfully executed. Defaults to null. * @property code The UTF-8 encoded code of the character. The code defined here is used as the * data passed to the system. Defaults to 0. * @property label The string used to display the key in the UI. Is not used for the actual data @@ -173,11 +357,12 @@ sealed interface KeyData : AbstractKeyData { @Serializable @SerialName("text_key") class TextKeyData( - override val type: KeyType = KeyType.CHARACTER, + override val type: KeyType? = null, override val code: Int = KeyCode.UNSPECIFIED, override val label: String = "", override val groupId: Int = KeyData.GROUP_DEFAULT, - override val popup: PopupSet = PopupSet(), + override val popup: PopupSet = SimplePopups(null), + override val width: Float = 0f, override val labelFlags: Int = 0 ) : KeyData { override fun asString(isForDisplay: Boolean): String { @@ -197,6 +382,16 @@ class TextKeyData( return "${TextKeyData::class.simpleName} { type=$type code=$code label=\"$label\" groupId=$groupId }" } + override fun copy( + newType: KeyType?, + newCode: Int, + newLabel: String, + newGroupId: Int, + newPopup: PopupSet, + newWidth: Float, + newLabelFlags: Int + ) = TextKeyData(newType, newCode, newLabel, newGroupId, newPopup, newWidth, newLabelFlags) + } // AutoTextKeyData is just for converting case with shift, which HeliBoard always does anyway @@ -204,11 +399,12 @@ class TextKeyData( @Serializable @SerialName("auto_text_key") class AutoTextKeyData( - override val type: KeyType = KeyType.CHARACTER, + override val type: KeyType? = null, override val code: Int = KeyCode.UNSPECIFIED, override val label: String = "", override val groupId: Int = KeyData.GROUP_DEFAULT, - override val popup: PopupSet = PopupSet(), + override val popup: PopupSet = SimplePopups(null), + override val width: Float = 0f, override val labelFlags: Int = 0 ) : KeyData { @@ -228,16 +424,28 @@ class AutoTextKeyData( override fun toString(): String { return "${AutoTextKeyData::class.simpleName} { type=$type code=$code label=\"$label\" groupId=$groupId }" } + + override fun copy( + newType: KeyType?, + newCode: Int, + newLabel: String, + newGroupId: Int, + newPopup: PopupSet, + newWidth: Float, + newLabelFlags: Int + ) = AutoTextKeyData(newType, newCode, newLabel, newGroupId, newPopup, newWidth, newLabelFlags) + } @Serializable @SerialName("multi_text_key") class MultiTextKeyData( - override val type: KeyType = KeyType.CHARACTER, + override val type: KeyType? = null, val codePoints: IntArray = intArrayOf(), override val label: String = "", override val groupId: Int = KeyData.GROUP_DEFAULT, - override val popup: PopupSet = PopupSet(), + override val popup: PopupSet = SimplePopups(null), + override val width: Float = 0f, override val labelFlags: Int = 0 ) : KeyData { @Transient override val code: Int = KeyCode.MULTIPLE_CODE_POINTS @@ -263,6 +471,17 @@ class MultiTextKeyData( override fun toString(): String { return "${MultiTextKeyData::class.simpleName} { type=$type code=$code label=\"$label\" groupId=$groupId }" } + + override fun copy( + newType: KeyType?, + newCode: Int, + newLabel: String, + newGroupId: Int, + newPopup: PopupSet, + newWidth: Float, + newLabelFlags: Int + ) = MultiTextKeyData(newType, codePoints, newLabel, newGroupId, newPopup, newWidth, newLabelFlags) + } fun String.toTextKey(popupKeys: Collection? = null, labelFlags: Int = 0): TextKeyData = diff --git a/app/src/main/java/helium314/keyboard/latin/common/Constants.java b/app/src/main/java/helium314/keyboard/latin/common/Constants.java index e697cd0b2..0779b219f 100644 --- a/app/src/main/java/helium314/keyboard/latin/common/Constants.java +++ b/app/src/main/java/helium314/keyboard/latin/common/Constants.java @@ -201,7 +201,7 @@ public static String printableCode(final int code) { switch (code) { case KeyCode.SHIFT: return "shift"; case KeyCode.CAPS_LOCK: return "capslock"; - case KeyCode.ALPHA_SYMBOL: return "alpha_symbol"; + case KeyCode.SYMBOL_ALPHA: return "symbol_alpha"; case KeyCode.ALPHA: return "alpha"; case KeyCode.SYMBOL: return "symbol"; case KeyCode.MULTIPLE_CODE_POINTS: return "text"; diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java index df1428c59..b1152a3c4 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java @@ -490,7 +490,7 @@ public InputTransaction onCodeInput(final SettingsValues settingsValues, } if (!inputTransaction.didAutoCorrect() && processedEvent.getMKeyCode() != KeyCode.SHIFT && processedEvent.getMKeyCode() != KeyCode.CAPS_LOCK - && processedEvent.getMKeyCode() != KeyCode.ALPHA_SYMBOL + && processedEvent.getMKeyCode() != KeyCode.SYMBOL_ALPHA && processedEvent.getMKeyCode() != KeyCode.ALPHA && processedEvent.getMKeyCode() != KeyCode.SYMBOL) mLastComposedWord.deactivate(); @@ -775,7 +775,7 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp // We need to switch to the shortcut IME. This is handled by LatinIME since the // input logic has no business with IME switching. case KeyCode.CAPS_LOCK: - case KeyCode.ALPHA_SYMBOL: + case KeyCode.SYMBOL_ALPHA: case KeyCode.ALPHA: case KeyCode.SYMBOL: case KeyCode.NUMPAD: diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index 113948e46..5d19431b8 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -706,4 +706,7 @@ public static Context getDayNightContext(final Context context, final boolean wa return wrapper; } + public boolean isTablet() { + return mContext.getResources().getInteger(R.integer.config_screen_metrics) >= 3; + } } diff --git a/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java b/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java index b5ea28357..28e2f57b3 100644 --- a/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java +++ b/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java @@ -19,7 +19,6 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode; import helium314.keyboard.latin.R; import helium314.keyboard.latin.SuggestedWords; -import helium314.keyboard.latin.common.Constants; import helium314.keyboard.latin.utils.TypefaceUtils; public final class MoreSuggestions extends Keyboard { @@ -87,7 +86,7 @@ public int layout(final SuggestedWords suggestedWords, final int fromIndex, mNumRows = row + 1; mBaseWidth = mOccupiedWidth = Math.max( minWidth, calcurateMaxRowWidth(fromIndex, index)); - mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap; + mBaseHeight = mOccupiedHeight = mNumRows * mDefaultAbsoluteRowHeight + mVerticalGap; return index - fromIndex; } @@ -138,7 +137,7 @@ public int getX(final int index) { public int getY(final int index) { final int row = mRowNumbers[index]; - return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding; + return (mNumRows -1 - row) * mDefaultAbsoluteRowHeight + mTopPadding; } public int getWidth(final int index) { @@ -185,7 +184,7 @@ public Builder layout(final SuggestedWords suggestedWords, final int fromIndex, mParams.mId = parentKeyboard.mId; readAttributes(xmlId); mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2; - mPaneView.updateKeyboardGeometry(mParams.mDefaultRowHeight); + mPaneView.updateKeyboardGeometry(mParams.mDefaultAbsoluteRowHeight); final int count = mParams.layout(suggestedWords, fromIndex, maxWidth, minWidth, maxRow, mPaneView.newLabelPaint(null /* key */), mResources); mFromIndex = fromIndex; @@ -218,7 +217,7 @@ public MoreSuggestions build() { final int numColumnInRow = params.getNumColumnInRow(index); if (columnNumber < numColumnInRow - 1) { final Divider divider = new Divider(params, params.mDivider, x + width, y, - params.mDividerWidth, params.mDefaultRowHeight); + params.mDividerWidth, params.mDefaultAbsoluteRowHeight); params.onAddKey(divider); } } @@ -234,7 +233,7 @@ public MoreSuggestionKey(final String word, final String info, final int index, super(word /* label */, KeyboardIconsSet.ICON_UNDEFINED, KeyCode.MULTIPLE_CODE_POINTS, word /* outputText */, info, 0 /* labelFlags */, Key.BACKGROUND_TYPE_NORMAL, params.getX(index), params.getY(index), params.getWidth(index), - params.mDefaultRowHeight, params.mHorizontalGap, params.mVerticalGap); + params.mDefaultAbsoluteRowHeight, params.mHorizontalGap, params.mVerticalGap); mSuggestedWordIndex = index; } } diff --git a/app/src/main/java/helium314/keyboard/latin/utils/Ktx.kt b/app/src/main/java/helium314/keyboard/latin/utils/Ktx.kt index 01467e0d6..98d46c675 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/Ktx.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/Ktx.kt @@ -17,3 +17,40 @@ fun CharSequence.getStringResourceOrName(prefix: String, context: Context): Char val resId = context.resources.getIdentifier(prefix + this, "string", context.packageName) return if (resId == 0) this else context.getString(resId) } + +/** + * Splits the collection into a pair of lists on the first match of [condition], discarding the element first matching the condition. + * If [condition] is not met, all elements are in the first list. + */ +fun Collection.splitAt(condition: (T) -> Boolean): Pair, MutableList> { + var conditionMet = false + val first = mutableListOf() + val second = mutableListOf() + forEach { + if (conditionMet) { + second.add(it) + } else { + conditionMet = condition(it) + if (!conditionMet) + first.add(it) + } + } + return first to second +} + +// like plus, but for nullable collections +fun addCollections(a: Collection?, b: Collection?): Collection? { + if (a.isNullOrEmpty()) return b + if (b.isNullOrEmpty()) return a + return a + b +} + +fun MutableList.removeFirst(predicate: (T) -> Boolean) { + val i = indexOfFirst(predicate) + if (i >= 0) removeAt(i) +} + +fun MutableList.replaceFirst(predicate: (T) -> Boolean, with: (T) -> T) { + val i = indexOfFirst(predicate) + if (i >= 0) this[i] = with(this[i]) +} diff --git a/app/src/main/res/values-sw600dp/functional-keys.xml b/app/src/main/res/values-sw600dp/functional-keys.xml deleted file mode 100644 index a5296108a..000000000 --- a/app/src/main/res/values-sw600dp/functional-keys.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - " -;action 10% -shift 10%; shift" - ";delete 10%" - "symbol_alpha, comma, space, period, emoji_com" - diff --git a/app/src/main/res/values/functional-keys.xml b/app/src/main/res/values/functional-keys.xml deleted file mode 100644 index 6fe085de4..000000000 --- a/app/src/main/res/values/functional-keys.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - "shift 15%; delete 15%" - - "" - - "symbol_alpha 15%, comma, space, period, action 15%" - diff --git a/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt b/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt index 282fe2c56..dec6dd8cd 100644 --- a/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt +++ b/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt @@ -50,6 +50,12 @@ class ParserTest { ShadowLog.stream = System.out } + // todo: add more tests + // (popup) keys with label and code + // (popup) keys with icon + // (popup) keys with that are essentially toolbar keys (yes, this should work at some point!) + // correct background type, depending on key type and maybe sth else + @Test fun simpleParser() { val params = KeyboardParams() params.mId = KeyboardLayoutSet.getFakeKeyboardId(KeyboardId.ELEMENT_ALPHABET) @@ -124,10 +130,11 @@ f""", // no newline at the end params.mId = KeyboardLayoutSet.getFakeKeyboardId(KeyboardId.ELEMENT_ALPHABET) params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT) addLocaleKeyTextsToParams(latinIME, params, POPUP_KEYS_NORMAL) - data class Expected(val label: String, val text: String?, val code: Int, val popups: List?) + data class Expected(val label: String?, val text: String?, val code: Int, val popups: List? = null) val expected = listOf( Expected("a", null, 'a'.code, null), Expected("a", null, 'a'.code, null), + Expected("a", null, 'b'.code, listOf("b")), // todo: should also check whether code is "a" Expected("$", null, '$'.code, listOf("£", "€", "¢", "¥", "₱")), Expected("$", null, '¥'.code, listOf("£", "€", "¢", "¥", "₱")), Expected("i", null, 105, null), @@ -137,11 +144,15 @@ f""", // no newline at the end Expected(".", null, '.'.code, listOf(">")), Expected("'", null, '\''.code, listOf("!", "\"")), Expected("9", null, '9'.code, null), // todo (later): also should have different background or whatever is related to type - Expected("", null, -7, null), // todo: expect an icon - Expected("?123", null, -207, null), - Expected("", null, ' '.code, null), + Expected(null, null, -7, null), // todo: expect an icon + Expected("?123", "?123", -202, null), + Expected(null, null, ' '.code, null), Expected("(", null, '('.code, listOf("<", "[", "{")), Expected("$", null, '$'.code, listOf("£", "₱", "€", "¢", "¥")), + Expected("a", null, ' '.code, null), + Expected("a", null, ' '.code, null), + Expected(null, null, KeyCode.CLIPBOARD, null), // todo: expect an icon + Expected(null, null, KeyCode.MULTIPLE_CODE_POINTS, null), // todo: this works here, but crashes on phone Expected("p", null, 'p'.code, null), ) val layoutString = """ @@ -149,6 +160,7 @@ f""", // no newline at the end [ { "$": "auto_text_key" "label": "a" }, { "$": "text_key" "label": "a" }, + { "$": "text_key" "label": "a|b", "popup": { "main": { "label": "b|a" } } }, { "label": "$$$" }, { "label": "$$$", code: -805 }, { "$": "case_selector", @@ -228,6 +240,10 @@ f""", // no newline at the end { "code": -805, "label": "currency_slot_5" } ] } }, + { "code": 32, "label": "a|!code/key_delete" }, + { "code": 32, "label": "a|b" }, + { "label": "!icon/clipboard_action_key|!code/key_clipboard" }, + { "label": "!icon/clipboard_action_key" }, { "label": "p" } ], [ @@ -254,11 +270,9 @@ f""", // no newline at the end """.trimIndent() val keys = JsonKeyboardParser(params, latinIME).parseCoreLayout(layoutString) keys.first().forEachIndexed { index, keyData -> - println("key ${keyData.label}: code ${keyData.code}, popups: ${keyData.popup.getPopupKeyLabels(params)}") - if (keyData.type == KeyType.ENTER_EDITING || keyData.type == KeyType.SYSTEM_GUI) return@forEachIndexed // todo: currently not accepted, but should be (see below) + println("data: key ${keyData.label}: code ${keyData.code}, popups: ${keyData.popup.getPopupKeyLabels(params)}") val keyParams = keyData.toKeyParams(params) - println("key ${keyParams.mLabel}: code ${keyParams.mCode}, popups: ${keyParams.mPopupKeys?.toList()}") - if (keyParams.outputText == "space") return@forEachIndexed // todo: only works for numeric layouts... idea: parse space anywhere, and otherwise only if special type + println("params: key ${keyParams.mLabel}: code ${keyParams.mCode}, popups: ${keyParams.mPopupKeys?.toList()}") assertEquals(expected[index].label, keyParams.mLabel) assertEquals(expected[index].code, keyParams.mCode) assertEquals(expected[index].popups?.sorted(), keyParams.mPopupKeys?.mapNotNull { it.mLabel }?.sorted()) // todo (later): what's wrong with order? diff --git a/layouts.md b/layouts.md index beb8c19c1..104ed0415 100644 --- a/layouts.md +++ b/layouts.md @@ -1,36 +1,17 @@ A compilation of information about the layout formats usable in this app. There are two distinct formats: -* the _simple_ format is a text file with one key per line, and two consecutive line breaks indicating a switch to the next row, [example](app/src/main/assets/layouts/qwerty.txt) +* the _simple_ format is a text file with one key label per line, and two consecutive line breaks indicating a switch to the next row, [example](app/src/main/assets/layouts/qwerty.txt) * the _json_ format taken from [FlorisBoard](https://github.com/florisboard/florisboard/blob/master/CONTRIBUTING.md#adding-the-layout), but only "normal" keys are supported (i.e. no action keys and similar), [example](app/src/main/assets/layouts/azerty.json) ## General notes -Adding too many keys or too long texts will make the keyboard look awkward or broken, and even crash the app under some specific conditions. +Adding too many keys or too long texts will make the keyboard look awkward or broken, and even crash the app under some specific conditions (popup keys are especially prone for this). There are some sanity checks when adding a layout to avoid such issues, but they do not cover all possible cases. Further there is no check whether the layout actually contains characters of the selected language. If you use an external glide typing library, you likely will have issues if your layout contains duplicate keys, or keys with text longer than a single letter. -There are special key labels that are intended for internal use only, but can (currently) be set on custom layouts too. An example is `!icon/previous_key|!code/key_action_previous`, so it's unlikely you will stumble upon issues here when not intentionally provoking it. -One special label that might be wanted though is `$$$`, which will be replaced by the local currency. `$$$1` - `$$$5` will be replaced by currencies available on long-pressing the currency key. -If you want different key label and use text, set the label to [label]|[text], e.g. `aa|bb` will show `aa`, but pressing the key will input `bb`. - -Some special key labels will be implemented, most are already working in the (currently experimental) customization of number layouts (numpad and similar). Some keys have two names for compatibility to FlorisBoard layouts. -* _alpha_ / _view_characters_: switch to alphabet keyboard (or main phone keyboard in case of phone layout) -* _symbol_ / _view_symbols_: switch to symbol keyboard (or phone symbols keyboard in case of phone layout) -* _symbol_alpha_: toggle alpha / symbol keyboard -* _numpad_ / _view_numeric_advanced_: switch to numpad layout -* _emoji_: switch to emoji view -* _com_: display common TLDs (.com and similar) -* _emoji_com_: emoji key, but in URL and email fields it's a com key -* _language_switch_: language switch key -* _action_ / _enter_: the action (enter) key -* _delete_: delete key -* _shift_: shift key, will change label when in symbols layout -* _period_: `.` key with punctuation popups, will adapt to language-specific period -* _comma_: `,` key with special popups, will adapt to language-specific comma, or display `/` in URL fields and `@` in email fields -* _space_: space key, with icon when using a number layout -* _zwnj_: Zero-width non-joiner (automatically added next to space in alphabet layout for some languages) +If the layout has exactly 2 keys in the bottom row, these keys will replace comma and period keys. More exactly: the first key will replace the first functional key with `"groupId": 1` in the bottom row, and the second key with replace the first key with `"groupId": 2`. ## Simple format * One key per line @@ -44,8 +25,68 @@ Some special key labels will be implemented, most are already working in the (cu * There is no need for specifying a `code`, it will be determined from the label automatically * You can still specify it, but it's only necessary if you want key label and code to be different (please avoid contributing layout with unnecessary codes to HeliBoard) * Note that not all _special codes_ (negative numbers) from FlorisBoard are supported -* You can add the numeric value of a _labelFlag_ to a key for some specific effects, see [here](app/src/main/res/values/attrs.xml) in the section _keyLabelFlags_ for names and numeric values. * More details on the formal will be provided. For now you can check other layouts, often you just need to copy lines and change the labels. +* Key classes: specified with `$`, usually you can omit them in HeliBoard + * `text_key`: normal key, default + * `auto_text_key`: used in FlorisBoard for a key that changes text case when shift is enabled, HeliBoard does that anyway unless disabled with a _labelFlag_ + * `multi_text_key`: key with an array of code points, e.g. `{ "$": "multi_text_key", "codePoints": [2509, 2480], "label": "্র" }` + * there are also selector classes, which allow to change keys conditionally, see the [dvorak layout](https://github.com/Helium314/HeliBoard/blob/main/app/src/main/assets/layouts/dvorak.json) for an example: + * `case_selector`: keys for `lower` and `upper` (both mandatory), similar to `shift_state_selector` + * `shift_state_selector`: keys for `unshifted`, `shifted`, `shiftedManual`, `shiftedAutomatic`, `capsLock`, `manualOrLocked`, `default` (all opttional) + * `variation_selector`: keys for `datetime`, `time`, `date`, `password`, `normal`, `uri`, `email`, `default` (all opttional) + * `layout_direction_selector`: keys for `ltr` and `rtl` (both mandatory) +### Properties +* A (non-selector) key can have the following properties: +* `type`: only specific values, HeliBoard mostly uses this to determine background color and type, determined automatically by default + * `character`: normal key color + * `function`: functional key color + * `space`: space bar color + * `action`: action key color + * `unspecified`: no background color + * `placeholder`: no background color, no label, and pressing the key does nothing + * There are some more values, but they do nothing +* `code`: code point that is entered when the key is pressed, determined from the label by default, not available for `multi_text_key` + * There are special negative values available, e.g. the ones used by functional keys, see [KeyCode.kt](/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt). There are several not yet supported key codes in there, you can see in the function `checkAndConvertCode` which ones are working. +* `codePoints`: when multiple code points should be entered, only available for `multi_text_key` +* `label`: text to display on the key, determined from code if empty + * There are some special values, see the [label section](#labels) +* `groupId`: which additional popup keys to show, `0` is default and does not add anything, `1` adds the comma popup keys, and `2` adds the period popup keys +* `popup`: list of keys to add in the popup, e.g. `"label": ")", "popup": {"relevant": [{ "label": "." }]}` is a `)` key with a `.` popup + * Note that in popup keys, properties are ignored with the exception of `$`, `code`, `codePoints`, and `label` + * When specifying a _selector_ key class in a popup key, it will be evaluated correctly (e.g. for changing popups dependent on shift state) +* `width`: width of the key in units of screen width, e.g. a key with `"width": 0.1` has a width of 10% of the screen, defaults to `0` + * A special value is `-1`, which means the key expands to the available space not already used by other keys (e.g. the space bar) + * `0` is interpreted as follows + * `-1` on the `space` key in alphabet or symbols layouts + * `0.17` for keys with `"type": numeric` in number layouts + * Otherwise the default width is used, which is `0.1` for phones and `0.09` for tablets + * If the sum of widths in a row is greater than 1, keys are rescaled to fit on the screen +* `labelFlags`: allows specific effects, see [here](app/src/main/res/values/attrs.xml) in the section _keyLabelFlags_ for names and numeric values + +## Labels +In the simple format you only specify labels, in json layouts you do it explicitly via the `label` property. +Usually the label is what is displayed on the key. However, there are some special labels: +* Currency keys + * `$$$` will be replaced by the local currency, depending on your current layout language. If you define a key with `$$$` without defining popup keys, it will get the first 4 additional currencies (see below) as popup + * `$$$1` - `$$$5` will be replaced by currencies available on long-pressing the currency key +* Functional keys (incomplete list) + * _alpha_: switch to alphabet keyboard (or main phone keyboard in case of phone layout) + * _symbol_: switch to symbol keyboard (or phone symbols keyboard in case of phone layout) + * _symbol_alpha_: toggle alpha / symbol keyboard + * _numpad_: switch to numpad layout + * _emoji_: switch to emoji view + * _com_: display common TLDs (.com and similar, currently not localized) + * _language_switch_: language switch key + * _action_: the action (enter) key + * _delete_: delete key + * _shift_: shift key, will change label when in symbols layout + * _period_: `.` key with punctuation popups, will adapt to language-specific period + * _comma_: `,` key with special popups, will adapt to language-specific comma, or display `/` in URL fields and `@` in email fields + * _space_: space key, with icon when using a number layout + * _zwnj_: Zero-width non-joiner (automatically added next to space in alphabet layout for some languages) +* If you want different key label and use text, set the label to [label]|[text], e.g. `aa|bb` will show `aa`, but pressing the key will input `bb`. +You can also specify special key codes like `a|!code/key_action_previous`, but it's cleaner to use a json layout and specify the code explicitly. Note that when specifying a code in the label, and a code in a json layout, the code in the label will be ignored. +It's also possible to specify an icon together with a code `!icon/previous_key|!code/key_action_previous`, but this is not fully supported yet. ## Adding new layouts / languages * You need a layout file in one of the formats above, and add it to [layouts](app/src/main/assets/layouts) @@ -66,3 +107,15 @@ Some special key labels will be implemented, most are already working in the (cu * If you add a new language for which Android does not have a display name, it will be displayed using the language tag * Avoiding this currently is more complicated than necessary: add the language tag to [LocaleUtils.getLocaleDisplayNameInSystemLocale](/app/src/main/java/helium314/keyboard/latin/common/LocaleUtils.kt#L181) to have an exception, and add a string named `subtype_` to [`strings.xml`](/app/src/main/res/values/strings.xml). Further you may need to add a `subtype_in_root_locale_` to [donottranslate.xml](/app/src/main/res/values/donottranslate.xml), and add the language tag to `subtype_locale_exception_keys` and `subtype_locale_displayed_in_root_locale`. * If a newly added language does not use latin script, please update the default scripts method `Locale.script` in [ScriptUtils](app/src/main/java/helium314/keyboard/latin/utils/ScriptUtils.kt) + +## Functional key layouts +This is not yet customizable, but will be soon! +Mostly customizing functional keys works like other layouts, with some specific adjustments: +* you can either have a single layout for functional keys (default), or separate layouts for symbols and shift symbols + * when using a single layout + * emoji and language switch keys will only show in alphabet layout and when the option is enabled + * numpad key will only show in symbols layout + * otherwise the layout will be shown as it is in the layout file +* use keys with `"type": "placeholder"` for + * separating left and right functional keys (e.g. shift and delete in default layout) + * separating top and bottom rows in case you want to have functional key rows aligned to the top of the keyboard From 0b7f0adc56a77b69e8ace5028cc59eb42d5d39cc Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 11 May 2024 15:51:55 +0200 Subject: [PATCH 02/58] small refactor / remove unused code --- .../keyboard/keyboard/KeyboardLayoutSet.java | 47 +++++-------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java index 364c29409..397b063a9 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java @@ -8,7 +8,6 @@ import android.app.KeyguardManager; import android.content.Context; -import android.content.res.Resources; import android.os.Build; import android.text.InputType; import android.view.inputmethod.EditorInfo; @@ -40,8 +39,6 @@ public final class KeyboardLayoutSet { private static final String TAG = KeyboardLayoutSet.class.getSimpleName(); private static final boolean DEBUG_CACHE = false; - private static final String KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX = "keyboard_layout_set_"; - private final Context mContext; @NonNull private final Params mParams; @@ -55,8 +52,7 @@ public final class KeyboardLayoutSet { // will stay in the cache. So we forcibly keep some references in an array to prevent // them from disappearing from sKeyboardCache. private static final Keyboard[] sForcibleKeyboardCache = new Keyboard[FORCIBLE_CACHE_SIZE]; - private static final HashMap> sKeyboardCache = - new HashMap<>(); + private static final HashMap> sKeyboardCache = new HashMap<>(); @NonNull private static final UniqueKeysCache sUniqueKeysCache = UniqueKeysCache.newInstance(); @@ -70,7 +66,6 @@ public KeyboardLayoutSetException(final Throwable cause, final KeyboardId keyboa } public static final class Params { - String mKeyboardLayoutSetName; // this is only for xml, can be removed together with xmls int mMode; boolean mDisableTouchPositionCorrectionDataForTest; // remove // TODO: Use {@link InputAttributes} instead of these variables. @@ -116,25 +111,17 @@ private static void clearKeyboardCache() { public Keyboard getKeyboard(final int baseKeyboardLayoutSetElementId) { final int keyboardLayoutSetElementId; switch (mParams.mMode) { - case KeyboardId.MODE_PHONE: + case KeyboardId.MODE_PHONE -> { if (baseKeyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS) { keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS; } else { keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE; } - break; - case KeyboardId.MODE_NUMPAD: - keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMPAD; - break; - case KeyboardId.MODE_NUMBER: - case KeyboardId.MODE_DATE: - case KeyboardId.MODE_TIME: - case KeyboardId.MODE_DATETIME: - keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER; - break; - default: - keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId; - break; + } + case KeyboardId.MODE_NUMPAD -> keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMPAD; + case KeyboardId.MODE_NUMBER, KeyboardId.MODE_DATE, KeyboardId.MODE_TIME, KeyboardId.MODE_DATETIME -> + keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER; + default -> keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId; } // Note: The keyboard for each shift state, and mode are represented as an elementName @@ -196,7 +183,6 @@ public String getScript() { public static final class Builder { private final Context mContext; - private final Resources mResources; private final Params mParams = new Params(); @@ -204,7 +190,6 @@ public static final class Builder { public Builder(final Context context, @Nullable final EditorInfo ei) { mContext = context; - mResources = context.getResources(); final Params params = mParams; final EditorInfo editorInfo = (ei != null) ? ei : EMPTY_EDITOR_INFO; @@ -232,12 +217,9 @@ public Builder setKeyboardGeometry(final int keyboardWidth, final int keyboardHe public Builder setSubtype(@NonNull final RichInputMethodSubtype subtype) { final boolean asciiCapable = subtype.getRawSubtype().isAsciiCapable(); final boolean forceAscii = (mParams.mEditorInfo.imeOptions & EditorInfo.IME_FLAG_FORCE_ASCII) != 0; - final RichInputMethodSubtype keyboardSubtype = (forceAscii && !asciiCapable) + mParams.mSubtype = (forceAscii && !asciiCapable) ? RichInputMethodSubtype.getNoLanguageSubtype() : subtype; - mParams.mSubtype = keyboardSubtype; - mParams.mKeyboardLayoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX - + keyboardSubtype.getKeyboardLayoutSetName(); return this; } @@ -296,14 +278,11 @@ private static int getKeyboardMode(final EditorInfo editorInfo) { case InputType.TYPE_CLASS_NUMBER: return KeyboardId.MODE_NUMBER; case InputType.TYPE_CLASS_DATETIME: - switch (variation) { - case InputType.TYPE_DATETIME_VARIATION_DATE: - return KeyboardId.MODE_DATE; - case InputType.TYPE_DATETIME_VARIATION_TIME: - return KeyboardId.MODE_TIME; - default: // InputType.TYPE_DATETIME_VARIATION_NORMAL - return KeyboardId.MODE_DATETIME; - } + return switch (variation) { + case InputType.TYPE_DATETIME_VARIATION_DATE -> KeyboardId.MODE_DATE; + case InputType.TYPE_DATETIME_VARIATION_TIME -> KeyboardId.MODE_TIME; + default -> KeyboardId.MODE_DATETIME; // must be InputType.TYPE_DATETIME_VARIATION_NORMAL + }; case InputType.TYPE_CLASS_PHONE: return KeyboardId.MODE_PHONE; case InputType.TYPE_CLASS_TEXT: From 6b05d019a1dce72b91b8b63c751b092d7ad28ead Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 11 May 2024 16:12:15 +0200 Subject: [PATCH 03/58] update workflow triggered on PR --- .github/workflows/build-test-auto.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-test-auto.yml b/.github/workflows/build-test-auto.yml index 899067f38..54f3d7fb5 100644 --- a/.github/workflows/build-test-auto.yml +++ b/.github/workflows/build-test-auto.yml @@ -2,11 +2,12 @@ name: Test build # builds only for a single abi and does not produce an APK on: - push: +# disabled on push: when I push to non-main, I do a PR anyway +# push: # don't run on main. I noticed I often don't push commits to avoid unnecessary workflow runs - branches-ignore: [ main ] - paths: - - 'app/**' +# branches-ignore: [ main ] +# paths: +# - 'app/**' pull_request: paths: - 'app/**' @@ -32,7 +33,7 @@ jobs: run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew assembleDebug + run: ./gradlew -Pandroid.injected.build.abi=arm64-v8a assembleDebug - name: Archive reports for failed job uses: actions/upload-artifact@v4 From 9bb64d561ffec8a8b498d73dbb85c434a6c496e2 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 11 May 2024 17:40:49 +0200 Subject: [PATCH 04/58] read use_contacts pref as false if permission was revoked avoids resetting dictinary facilitator on every startInputView --- .../keyboard/latin/settings/SettingsValues.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java index f64e4a905..f60df9715 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java @@ -6,6 +6,7 @@ package helium314.keyboard.latin.settings; +import android.Manifest; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -23,6 +24,7 @@ import helium314.keyboard.latin.R; import helium314.keyboard.latin.RichInputMethodManager; import helium314.keyboard.latin.common.Colors; +import helium314.keyboard.latin.permissions.PermissionsUtil; import helium314.keyboard.latin.utils.InputTypeUtils; import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.PopupKeysUtilsKt; @@ -225,7 +227,7 @@ public SettingsValues(final Context context, final SharedPreferences prefs, fina mPopupKeyLabelSources = PopupKeysUtilsKt.getEnabledPopupKeys(prefs, Settings.PREF_POPUP_KEYS_LABELS_ORDER + "_" + mLocale.toLanguageTag(), popupKeyLabelDefault); mAddToPersonalDictionary = prefs.getBoolean(Settings.PREF_ADD_TO_PERSONAL_DICTIONARY, false); - mUseContactsDictionary = prefs.getBoolean(Settings.PREF_USE_CONTACTS, false); + mUseContactsDictionary = SettingsValues.readUseContactsEnabled(prefs, context); mCustomNavBarColor = prefs.getBoolean(Settings.PREF_NAVBAR_COLOR, false); mNarrowKeyGaps = prefs.getBoolean(Settings.PREF_NARROW_KEY_GAPS, true); mSettingsValuesForSuggestion = new SettingsValuesForSuggestion( @@ -344,6 +346,11 @@ private static float readAutoCorrectionThreshold(final Resources res, return autoCorrectionThreshold; } + private static boolean readUseContactsEnabled(final SharedPreferences prefs, final Context context) { + return prefs.getBoolean(Settings.PREF_USE_CONTACTS, false) + && PermissionsUtil.checkAllPermissionsGranted(context, Manifest.permission.READ_CONTACTS); + } + public String dump() { final StringBuilder sb = new StringBuilder("Current settings :"); sb.append("\n mSpacingAndPunctuations = "); From 38228f8b706953a77ce845c021e13262f0151db9 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 11 May 2024 23:25:47 +0200 Subject: [PATCH 05/58] parse number and alpha/symbol layouts using the same function --- app/src/main/assets/layouts/number.json | 20 +-- app/src/main/assets/layouts/numpad.json | 20 +-- .../main/assets/layouts/numpad_landscape.json | 64 +++---- app/src/main/assets/layouts/phone.json | 8 +- .../main/assets/layouts/phone_symbols.json | 12 +- .../keyboard/internal/KeyboardParams.java | 7 +- .../keyboard_parser/KeyboardParser.kt | 160 +++++------------- .../keyboard_parser/floris/KeyType.kt | 1 + .../keyboard_parser/floris/TextKeyData.kt | 16 +- layouts.md | 10 +- 10 files changed, 129 insertions(+), 189 deletions(-) diff --git a/app/src/main/assets/layouts/number.json b/app/src/main/assets/layouts/number.json index ca36417a2..bc6bae10b 100644 --- a/app/src/main/assets/layouts/number.json +++ b/app/src/main/assets/layouts/number.json @@ -3,13 +3,13 @@ { "label": "1", "type": "numeric" }, { "label": "2", "type": "numeric" }, { "label": "3", "type": "numeric" }, - { "label": "-", "popup": { "main": { "label": "+" } }, "labelFlags": 1073742336 } + { "label": "-", "type": "function", "popup": { "main": { "label": "+" } }, "labelFlags": 64 } ], [ { "label": "4", "type": "numeric" }, { "label": "5", "type": "numeric" }, { "label": "6", "type": "numeric" }, - { "label": "space" } + { "label": "space", "type": "function" } ], [ { "label": "7", "type": "numeric" }, @@ -19,15 +19,15 @@ ], [ { "$": "variation_selector", - "default": { "label": "," }, - "date": { "label": "." }, - "time": { "label": ".", "popup": { "relevant": [ + "default": { "label": ",", "type": "numeric" }, + "date": { "label": ".", "type": "numeric" }, + "time": { "label": ".", "type": "numeric", "popup": { "relevant": [ { "label": "!fixedColumnOrder!2" }, { "label": "!hasLabels!" }, { "label": "AM" }, { "label": "PM" } ] } }, - "datetime": { "label": ".", "popup": { "relevant": [ + "datetime": { "label": ".", "type": "numeric", "popup": { "relevant": [ { "label": "!fixedColumnOrder!2" }, { "label": "!hasLabels!" }, { "label": "AM" }, @@ -36,10 +36,10 @@ }, { "label": "0", "type": "numeric" }, { "$": "variation_selector", - "default": { "label": "." }, - "date": { "label": "/" }, - "time": { "label": ":" }, - "datetime": { "label": "/ :|/", "popup": { "relevant": [ + "default": { "label": ".", "type": "numeric" }, + "date": { "label": "/", "type": "numeric" }, + "time": { "label": ":", "type": "numeric" }, + "datetime": { "label": "/ :|/", "type": "numeric", "popup": { "relevant": [ { "label": "!noPanelAutoPopupKey!" }, { "label": "," } ] } } diff --git a/app/src/main/assets/layouts/numpad.json b/app/src/main/assets/layouts/numpad.json index 1ef9038ee..4edfca2f0 100644 --- a/app/src/main/assets/layouts/numpad.json +++ b/app/src/main/assets/layouts/numpad.json @@ -1,6 +1,6 @@ [ [ - { "label": "+", "popup": { + { "label": "+", "type": "function", "popup": { "relevant": [ { "label": "(" }, { "label": "<" }, @@ -10,10 +10,10 @@ { "label": "1", "type": "numeric" }, { "label": "2", "type": "numeric" }, { "label": "3", "type": "numeric" }, - { "label": "%", "popup": { "main": { "label": "$$$"} }, "labelFlags": 512 } + { "label": "%", "type": "function", "popup": { "main": { "label": "$$$"} }, "labelFlags": 512 } ], [ - { "label": "-", "popup": { + { "label": "-", "type": "function", "popup": { "relevant": [ { "label": ")" }, { "label": ">" }, @@ -23,10 +23,10 @@ { "label": "4", "type": "numeric" }, { "label": "5", "type": "numeric" }, { "label": "6", "type": "numeric" }, - { "label": "space" } + { "label": "space", "type": "function" } ], [ - { "label": "*", "popup": { + { "label": "*", "type": "function", "popup": { "relevant": [ { "label": "/" }, { "label": "×" }, @@ -40,11 +40,11 @@ ], [ { "label": "alpha" }, - { "label": "comma" }, - { "label": "symbol" }, + { "label": "comma", "width": 0.1 }, + { "label": "symbol", "type": "character", "width": 0.12 }, { "label": "0", "type": "numeric" }, - { "label": "=", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } }, - { "label": "period" }, - { "label": "enter" } + { "label": "=", "width": 0.12, "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } }, + { "label": "period", "width": 0.1 }, + { "label": "action" } ] ] diff --git a/app/src/main/assets/layouts/numpad_landscape.json b/app/src/main/assets/layouts/numpad_landscape.json index 662376249..19f3cff02 100644 --- a/app/src/main/assets/layouts/numpad_landscape.json +++ b/app/src/main/assets/layouts/numpad_landscape.json @@ -1,46 +1,46 @@ [ [ - { "label": "(", "popup": { "relevant": [ { "label": "[" }, { "label": "{" } ] } }, - { "label": ")", "popup": { "relevant": [ { "label": "]" }, { "label": "}" } ] } }, - { "label": ":" }, - { "label": "1", "type": "numeric" }, - { "label": "2", "type": "numeric" }, - { "label": "3", "type": "numeric" }, - { "label": "+", "popup": { "main": { "label": "±" } } }, - { "label": "-", "popup": { "main": { "label": "~" } } }, - { "label": "space" } + { "label": "(", "type": "function", "popup": { "relevant": [ { "label": "[" }, { "label": "{" } ] } }, + { "label": ")", "type": "function", "popup": { "relevant": [ { "label": "]" }, { "label": "}" } ] } }, + { "label": ":", "type": "function" }, + { "label": "1", "type": "numeric", "width": 0.3 }, + { "label": "2", "type": "numeric", "width": 0.3 }, + { "label": "3", "type": "numeric", "width": 0.3 }, + { "label": "+", "type": "function", "popup": { "main": { "label": "±" } } }, + { "label": "-", "type": "function", "popup": { "main": { "label": "~" } } }, + { "label": "space", "type": "function" } ], [ - { "label": "!" }, - { "label": "?" }, - { "label": ";" }, - { "label": "4", "type": "numeric" }, - { "label": "5", "type": "numeric" }, - { "label": "6", "type": "numeric" }, - { "label": "*", "popup": { "main": { "label": "×" } } }, - { "label": "/", "popup": { "main": { "label": "÷" } } }, + { "label": "!", "type": "function" }, + { "label": "?", "type": "function" }, + { "label": ";", "type": "function" }, + { "label": "4", "type": "numeric", "width": 0.3 }, + { "label": "5", "type": "numeric", "width": 0.3 }, + { "label": "6", "type": "numeric", "width": 0.3 }, + { "label": "*", "type": "function", "popup": { "main": { "label": "×" } } }, + { "label": "/", "type": "function", "popup": { "main": { "label": "÷" } } }, { "label": "delete" } ], [ - { "label": "|" }, - { "label": "$$$" }, - { "label": "&" }, - { "label": "7", "type": "numeric" }, - { "label": "8", "type": "numeric" }, - { "label": "9", "type": "numeric" }, - { "label": "#" }, - { "label": "%", "popup": { "main": { "label": "‰" } } }, + { "label": "|", "type": "function" }, + { "label": "$$$", "type": "function" }, + { "label": "&", "type": "function" }, + { "label": "7", "type": "numeric", "width": 0.3 }, + { "label": "8", "type": "numeric", "width": 0.3 }, + { "label": "9", "type": "numeric", "width": 0.3 }, + { "label": "#", "type": "function" }, + { "label": "%", "type": "function", "popup": { "main": { "label": "‰" } } }, { "label": "action" } ], [ { "label": "alpha" }, - { "label": "<", "popup": { "main": { "label": "≤" } } }, - { "label": ">", "popup": { "main": { "label": "≥" } } }, - { "label": "comma" }, - { "label": "0", "type": "numeric" }, - { "label": "period" }, - { "label": "=", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } }, - { "label": "=", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } }, + { "label": "<", "type": "function", "popup": { "main": { "label": "≤" } } }, + { "label": ">", "type": "function", "popup": { "main": { "label": "≥" } } }, + { "label": "comma", "type": "numeric", "width": 0.3 }, + { "label": "0", "type": "numeric", "width": 0.3 }, + { "label": "period", "type": "numeric", "width": 0.3 }, + { "label": "=", "type": "function", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } }, + { "label": "=", "type": "function", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } }, { "label": "symbol" } ] ] diff --git a/app/src/main/assets/layouts/phone.json b/app/src/main/assets/layouts/phone.json index a6cccc997..c3ffd6677 100644 --- a/app/src/main/assets/layouts/phone.json +++ b/app/src/main/assets/layouts/phone.json @@ -3,13 +3,13 @@ { "label": "1", "type": "numeric" }, { "label": "2", "type": "numeric", "popup": { "main": { "label": "ABC" } } }, { "label": "3", "type": "numeric", "popup": { "main": { "label": "DEF" } } }, - { "label": "-", "popup": { "main": { "label": "+" } }, "labelFlags": 1073742336 } + { "label": "-", "type": "function", "labelFlags": 1073742400, "popup": { "main": { "label": "+" } } } ], [ { "label": "4", "type": "numeric", "popup": { "main": { "label": "GHI" } } }, { "label": "5", "type": "numeric", "popup": { "main": { "label": "JKL" } } }, { "label": "6", "type": "numeric", "popup": { "main": { "label": "MNO" } } }, - { "label": "space" } + { "label": "space", "type": "function" } ], [ { "label": "7", "type": "numeric", "popup": { "main": { "label": "PQRS" } } }, @@ -18,9 +18,9 @@ { "label": "delete" } ], [ - { "label": "*#|!code/key_switch_alpha_symbol", "labelFlags": 524432 }, + { "label": "*#|!code/key_switch_alpha_symbol", "type": "numeric", "labelFlags": 524432 }, { "label": "0 +|0", "type": "numeric", "popup": { "relevant": [ { "label": "!noPanelAutoPopupKey!" }, { "label": "+" } ] } }, - { "label": "." }, + { "label": ".", "type": "numeric", "labelFlags": 64 }, { "label": "action" } ] ] diff --git a/app/src/main/assets/layouts/phone_symbols.json b/app/src/main/assets/layouts/phone_symbols.json index 140bd4741..d1dd29d16 100644 --- a/app/src/main/assets/layouts/phone_symbols.json +++ b/app/src/main/assets/layouts/phone_symbols.json @@ -3,24 +3,24 @@ { "label": "(", "type": "numeric" }, { "label": "/", "type": "numeric" }, { "label": ")", "type": "numeric" }, - { "label": "-", "popup": { "main": { "label": "+" } } } + { "label": "-", "type": "function", "labelFlags": 1073742400, "popup": { "main": { "label": "+" } } } ], [ { "label": "N", "type": "numeric" }, - { "label": "!string/label_pause_key|,", "type": "numeric" }, + { "label": "!string/label_pause_key", "code": 44, "type": "numeric" }, { "label": ",", "type": "numeric" }, - { "label": "space" } + { "label": "space", "type": "function" } ], [ { "label": "*|*", "type": "numeric" }, - { "label": "!string/label_wait_key|;", "type": "numeric" }, + { "label": "!string/label_wait_key", "code": 59, "type": "numeric" }, { "label": "\\#", "type": "numeric" }, { "label": "delete" } ], [ - { "label": "123|!code/key_switch_alpha_symbol", "labelFlags": 524432 }, + { "label": "123|!code/key_switch_alpha_symbol", "type": "numeric", "labelFlags": 524432 }, { "label": "+", "type": "numeric" }, - { "label": "." }, + { "label": ".", "type": "numeric" }, { "label": "action" } ] ] diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java index b63d15442..0b9f6e9ae 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java @@ -224,9 +224,10 @@ public void readAttributes(final Context context, @Nullable final AttributeSet a R.styleable.Keyboard_keyboardRightPadding, width, width, 0); mBaseWidth = mOccupiedWidth - mLeftPadding - mRightPadding; - final float defaultKeyWidthFactor = context.getResources().getInteger(R.integer.config_screen_metrics) > 2 - ? 0.9f : 1f; - mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth, + final float defaultKeyWidthFactor = context.getResources().getInteger(R.integer.config_screen_metrics) > 2 ? 0.9f : 1f; + mDefaultKeyWidth = mId.isNumberLayout() + ? 0.17f + : keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth, 1, 1, defaultKeyWidthFactor / DEFAULT_KEYBOARD_COLUMNS); mDefaultAbsoluteKeyWidth = (int) (mDefaultKeyWidth * mBaseWidth); diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index 591a10134..75dda2fd2 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -48,11 +48,13 @@ import java.io.File */ abstract class KeyboardParser(private val params: KeyboardParams, private val context: Context) { private val infos = layoutInfos(params) - private val defaultLabelFlags = if (params.mId.isAlphabetKeyboard) { - params.mLocaleKeyboardInfos.labelFlags - } else if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS || params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { - Key.LABEL_FLAGS_DISABLE_HINT_LABEL // reproduce the no-hints in symbol layouts, todo: add setting - } else 0 + private val defaultLabelFlags = when { + params.mId.isAlphabetKeyboard -> params.mLocaleKeyboardInfos.labelFlags + // reproduce the no-hints in symbol layouts + // todo: add setting? or put it in TextKeyData to happen only if no label flags specified explicitly? + params.mId.isAlphaOrSymbolKeyboard -> Key.LABEL_FLAGS_DISABLE_HINT_LABEL + else -> 0 + } abstract fun parseCoreLayout(layoutContent: String): MutableList> @@ -66,16 +68,10 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co else params.mTouchPositionCorrection.load(context.resources.getStringArray(infos.touchPositionCorrectionData)) - val baseKeys: MutableList> = parseCoreLayout(layoutContent) - val keysInRows = if (params.mId.isAlphaOrSymbolKeyboard) { - createAlphaSymbolRows(baseKeys) - } else if (params.mId.isNumberLayout) { - createNumericRows(baseKeys) - } else { - throw(UnsupportedOperationException("creating KeyboardId ${params.mId.mElementId} not supported")) - } - // rescale height if we have more than 4 rows - val heightRescale = if (keysInRows.size > 4) 4f / keysInRows.size else 1f + val baseKeys = parseCoreLayout(layoutContent) + val keysInRows = createRows(baseKeys) + // rescale height if we have anything but the usual 4 rows + val heightRescale = if (keysInRows.size != 4) 4f / keysInRows.size else 1f if (heightRescale != 1f) { keysInRows.forEach { row -> row.forEach { it.mHeight *= heightRescale } } } @@ -86,7 +82,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co // this should be ready for customizable functional layouts, but needs cleanup // todo (later): remove this as part of adding a cache for parsed layouts private fun getFunctionalKeyLayoutText(): String { - if (!params.mId.isAlphaOrSymbolKeyboard) throw IllegalStateException("functional key layout only for aloha and symbol layouts") + if (params.mId.isNumberLayout) return "[]" // empty list val layouts = Settings.getLayoutsDir(context).list() ?: emptyArray() if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { if ("functional_keys_symbols_shifted.json" in layouts) @@ -102,15 +98,21 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co return context.readAssetsLayoutFile(fileName) } - private fun createAlphaSymbolRows(baseKeys: MutableList>): ArrayList> { + private fun createRows(baseKeys: MutableList>): ArrayList> { + // add padding for number layouts in landscape mode (maybe do it some other way later) + if (params.mId.isNumberLayout && params.mId.mElementId != KeyboardId.ELEMENT_NUMPAD + && context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { + params.mLeftPadding = (params.mOccupiedWidth * 0.1f).toInt() + params.mRightPadding = (params.mOccupiedWidth * 0.1f).toInt() + params.mBaseWidth = params.mOccupiedWidth - params.mLeftPadding - params.mRightPadding + } + addNumberRowOrPopupKeys(baseKeys) if (params.mId.isAlphabetKeyboard) addSymbolPopupKeys(baseKeys) - if (params.mId.mNumberRowEnabled) - baseKeys.add( - 0, - params.mLocaleKeyboardInfos.getNumberRow() - .map { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) }) + if (params.mId.isAlphaOrSymbolKeyboard && params.mId.mNumberRowEnabled) + baseKeys.add(0, params.mLocaleKeyboardInfos.getNumberRow() + .map { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) }) val allFunctionalKeys = JsonKeyboardParser(params, context).parseCoreLayout(getFunctionalKeyLayoutText()) adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys, baseKeys) @@ -126,7 +128,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co val functionalKeys = mutableListOf, List>>() val baseKeyParams = baseKeys.mapIndexed { i, it -> - val row: List = if (i == baseKeys.lastIndex - 1 && Settings.getInstance().isTablet) { + val row: List = if (params.mId.isAlphaOrSymbolKeyboard && i == baseKeys.lastIndex - 1 && Settings.getInstance().isTablet) { // add bottom row extra keys // todo (later): this can make very customized layouts look awkward // decide when to (not) add it @@ -142,13 +144,14 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co val functionalKeysFromBottom = functionalKeysBottom.getOrNull(i - bottomIndexOffset) ?: emptyList() functionalKeys.add(getFunctionalKeysBySide(functionalKeysFromTop, functionalKeysFromBottom)) - row.map { key -> + row.mapNotNull { key -> val extraFlags = if (key.label.length > 2 && key.label.codePointCount(0, key.label.length) > 2 && !isEmoji(key.label)) Key.LABEL_FLAGS_AUTO_X_SCALE else 0 + val keyData = key.processFunctionalKeys() ?: return@mapNotNull null // all keys could actually be functional keys... if (DebugFlags.DEBUG_ENABLED) - Log.d(TAG, "adding key ${key.label}, ${key.code}") - key.toKeyParams(params, defaultLabelFlags or extraFlags) + Log.d(TAG, "adding key ${keyData.label}, ${keyData.code}") + keyData.toKeyParams(params, defaultLabelFlags or extraFlags) } } return setReasonableWidths(baseKeyParams, functionalKeys) @@ -247,7 +250,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } // replace comma / period if 2 keys in normal bottom row if (baseKeys.last().size == 2) { - Log.i("test", "$functionalKeysBottom") functionalKeysBottom.replaceFirst( { it.label == KeyLabel.COMMA || it.groupId == KeyData.GROUP_COMMA}, { baseKeys.last()[0].copy(newGroupId = 1, newType = baseKeys.last()[0].type ?: it.type) } @@ -256,7 +258,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co { it.label == KeyLabel.PERIOD || it.groupId == KeyData.GROUP_PERIOD}, { baseKeys.last()[1].copy(newGroupId = 2, newType = baseKeys.last()[1].type ?: it.type) } ) - Log.i("test", "$functionalKeysBottom") baseKeys.removeLast() } // add those extra keys depending on layout (remove later) @@ -304,14 +305,11 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co // this is not nice in here, but otherwise we'd need context, and defaultLabelFlags and infos for toKeyParams // improve it later, but currently this messy way is still ok - private fun KeyData.processFunctionalKeys(): KeyData? { - if (label == KeyLabel.PERIOD) { - // todo: why defaultLabelFlags exactly here? is this for armenian or bengali period labels? try removing also check in holo theme - return copy(newLabelFlags = labelFlags or defaultLabelFlags) - } - if (label == KeyLabel.SHIFT && !infos.hasShiftKey) return null - if (label != KeyLabel.ACTION) return this - return copy( + private fun KeyData.processFunctionalKeys(): KeyData? = when (label) { + // todo: why defaultLabelFlags exactly here? is this for armenian or bengali period labels? try removing also check in holo theme + KeyLabel.PERIOD -> copy(newLabelFlags = labelFlags or defaultLabelFlags) + KeyLabel.SHIFT -> if (infos.hasShiftKey) this else null + KeyLabel.ACTION -> copy( // todo: evaluating the label should actually only happen in toKeyParams // this label change already makes it necessary to provide the background in here too, because toKeyParams can't use action as label newLabel = "${getActionKeyLabel()}|${getActionKeyCode()}", @@ -319,6 +317,15 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co // the label change is messing with toKeyParams, so we need to supply the appropriate BG type here newType = type ?: KeyType.ENTER_EDITING ) + else -> { + // this is ugly... + if (label.length > 8 && label.startsWith("!string/")) { + val id = context.resources.getIdentifier(label.substringAfter("!string/"), "string", context.packageName) + Log.i("test", "id of $label: $id") + if (id != 0) copy(newLabel = getInLocale(id)) + else this + } else this + } } private fun addNumberRowOrPopupKeys(baseKeys: MutableList>) { @@ -363,87 +370,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } } - private fun createNumericRows(baseKeys: MutableList>): ArrayList> { - val keysInRows = ArrayList>() - if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && params.mId.mElementId != KeyboardId.ELEMENT_NUMPAD) { - // add padding here instead of using xml (actually this is not good... todo (later)) - params.mLeftPadding = (params.mOccupiedWidth * 0.1f).toInt() - params.mRightPadding = (params.mOccupiedWidth * 0.1f).toInt() - params.mBaseWidth = params.mOccupiedWidth - params.mLeftPadding - params.mRightPadding - } - baseKeys.forEachIndexed { i, row -> - val paramsRow = ArrayList() - row.forEach { key -> - var keyParams: KeyParams? = null - // try parsing a functional key - val functionalKeyName = when (key.label) { - // todo (later): maybe add special popupKeys for phone and number layouts? - "." -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) KeyLabel.PERIOD else "." - "," -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) KeyLabel.COMMA else "," - else -> key.label - } - if (functionalKeyName.length > 1 && key.type != KeyType.NUMERIC) { - try { - keyParams = key.copy(newLabel = functionalKeyName).processFunctionalKeys()!!.toKeyParams(params) - } catch (_: Throwable) {} // just use normal label - } - if (keyParams == null) { - keyParams = if (key.type == KeyType.NUMERIC) { - val labelFlags = when (params.mId.mElementId) { - KeyboardId.ELEMENT_PHONE -> Key.LABEL_FLAGS_ALIGN_LABEL_OFF_CENTER or Key.LABEL_FLAGS_HAS_HINT_LABEL or Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO - KeyboardId.ELEMENT_PHONE_SYMBOLS -> 0 - else -> Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO - } - key.toKeyParams(params, labelFlags or defaultLabelFlags) - } else if (key.label.length == 1 && (params.mId.mElementId == KeyboardId.ELEMENT_PHONE || params.mId.mElementId == KeyboardId.ELEMENT_NUMBER)) - key.toKeyParams(params, additionalLabelFlags = Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO or defaultLabelFlags) - else - key.toKeyParams(params, additionalLabelFlags = defaultLabelFlags) - } - if (key.type != KeyType.NUMERIC && keyParams.mBackgroundType != Key.BACKGROUND_TYPE_ACTION) - keyParams.mBackgroundType = Key.BACKGROUND_TYPE_FUNCTIONAL - - if (params.mId.mElementId == KeyboardId.ELEMENT_PHONE && key.popup.main?.getPopupLabel(params)?.length?.let { it > 1 } == true) { - keyParams.mPopupKeys = null // the ABC and stuff labels should not create popupKeys - } - if (keyParams.mLabel?.length?.let { it > 1 } == true && keyParams.mLabel?.startsWith("!string/") == true) { - // resolve string label - val id = context.resources.getIdentifier(keyParams.mLabel?.substringAfter("!string/"), "string", context.packageName) - if (id != 0) - keyParams.mLabel = getInLocale(id) - } - paramsRow.add(keyParams) - if (DebugFlags.DEBUG_ENABLED) - Log.d(TAG, "adding key ${keyParams.mLabel}, ${keyParams.mCode}") - } - if (i == baseKeys.lastIndex) { // bottom row needs some adjustments - val n = row.indexOfFirst { it.type == KeyType.NUMERIC } - if (n != -1) { - // make sure the keys next to 0 have normal background - paramsRow.getOrNull(n - 1)?.mBackgroundType = Key.BACKGROUND_TYPE_NORMAL - paramsRow.getOrNull(n + 1)?.mBackgroundType = Key.BACKGROUND_TYPE_NORMAL - - // make those keys same width as numeric keys except in numpad layout - // but determine from row size instead of from elementId, in case user wants to adjust numpad layout - if (row.size == baseKeys[0].size) { - paramsRow.getOrNull(n - 1)?.mWidth = paramsRow[n].mWidth - paramsRow.getOrNull(n + 1)?.mWidth = paramsRow[n].mWidth - } else if (row.size == baseKeys[0].size + 2) { - // numpad last row -> make sure the keys next to 0 fit nicely - paramsRow.getOrNull(n - 1)?.mWidth = paramsRow[n].mWidth * 0.55f - paramsRow.getOrNull(n - 2)?.mWidth = paramsRow[n].mWidth * 0.45f - paramsRow.getOrNull(n + 1)?.mWidth = paramsRow[n].mWidth * 0.55f - paramsRow.getOrNull(n + 2)?.mWidth = paramsRow[n].mWidth * 0.45f - } - } - } - val widthSum = paramsRow.sumOf { it.mWidth } - paramsRow.forEach { it.mWidth /= widthSum } - keysInRows.add(paramsRow) - } - return keysInRows - } - private fun getActionKeyLabel(): String { if (params.mId.isMultiLine && (params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)) return "!icon/enter_key" diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyType.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyType.kt index 41444db99..5ce00a4dc 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyType.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyType.kt @@ -43,6 +43,7 @@ enum class KeyType { "space" -> NAVIGATION "action" -> ENTER_EDITING "shift" -> LOCK + "normal" -> CHARACTER else -> valueOf(string.uppercase()) } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index 1cd18a169..d1d7c2cab 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -144,11 +144,21 @@ sealed interface KeyData : AbstractKeyData { override fun compute(params: KeyboardParams): KeyData { require(groupId <= GROUP_ENTER) { "only groups up to GROUP_ENTER are supported" } require(label.isNotEmpty() || type == KeyType.PLACEHOLDER || code != KeyCode.UNSPECIFIED) { "non-placeholder key has no code and no label" } + require(width >= 0f || width == -1f) { "illegal width $width" } val newLabel = label.convertFlorisLabel() val newCode = code.checkAndConvertCode() + val newLabelFlags = if (labelFlags == 0 && params.mId.isNumberLayout) { + if (type == KeyType.NUMERIC) { + when (params.mId.mElementId) { + KeyboardId.ELEMENT_PHONE -> Key.LABEL_FLAGS_ALIGN_LABEL_OFF_CENTER or Key.LABEL_FLAGS_HAS_HINT_LABEL or Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO + KeyboardId.ELEMENT_PHONE_SYMBOLS -> 0 + else -> Key.LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO + } + } else 0 + } else labelFlags - if (newCode != code || newLabel != label) - return copy(newCode = newCode, newLabel = newLabel) + if (newCode != code || newLabel != label || labelFlags != newLabelFlags) + return copy(newCode = newCode, newLabel = newLabel, newLabelFlags = newLabelFlags) return this } @@ -258,7 +268,7 @@ sealed interface KeyData : AbstractKeyData { private fun getDefaultWidth(params: KeyboardParams): Float { return if (label == KeyLabel.SPACE && params.mId.isAlphaOrSymbolKeyboard) -1f - else if (type == KeyType.NUMERIC && params.mId.isNumberLayout) 0.17f // todo (later) consider making this -1? + else if (type == KeyType.NUMERIC && params.mId.isNumberLayout) -1f else params.mDefaultKeyWidth } diff --git a/layouts.md b/layouts.md index 104ed0415..d3d597276 100644 --- a/layouts.md +++ b/layouts.md @@ -38,12 +38,13 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm ### Properties * A (non-selector) key can have the following properties: * `type`: only specific values, HeliBoard mostly uses this to determine background color and type, determined automatically by default - * `character`: normal key color + * `normal`: normal key color * `function`: functional key color * `space`: space bar color * `action`: action key color * `unspecified`: no background color * `placeholder`: no background color, no label, and pressing the key does nothing + * `numeric`: normal key color, only in number layouts: sets default width to `-1` and sets default label flags if none specified * There are some more values, but they do nothing * `code`: code point that is entered when the key is pressed, determined from the label by default, not available for `multi_text_key` * There are special negative values available, e.g. the ones used by functional keys, see [KeyCode.kt](/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt). There are several not yet supported key codes in there, you can see in the function `checkAndConvertCode` which ones are working. @@ -57,9 +58,10 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm * `width`: width of the key in units of screen width, e.g. a key with `"width": 0.1` has a width of 10% of the screen, defaults to `0` * A special value is `-1`, which means the key expands to the available space not already used by other keys (e.g. the space bar) * `0` is interpreted as follows - * `-1` on the `space` key in alphabet or symbols layouts - * `0.17` for keys with `"type": numeric` in number layouts - * Otherwise the default width is used, which is `0.1` for phones and `0.09` for tablets + * `-1` on the `space` key in alphabet or symbols layouts, and for keys with `"type": numeric` in number layouts + * `0.17` for number layouts + * `0.1` for phones + * `0.09` for tablets * If the sum of widths in a row is greater than 1, keys are rescaled to fit on the screen * `labelFlags`: allows specific effects, see [here](app/src/main/res/values/attrs.xml) in the section _keyLabelFlags_ for names and numeric values From 8f3e4fff8c43c216c6c2038a65d823d36fbb6e7e Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 12 May 2024 00:20:56 +0200 Subject: [PATCH 06/58] allow selecting more icons in custom keys, update layouts.md --- .../keyboard/internal/KeySpecParser.java | 4 +--- .../keyboard/internal/KeyboardIconsSet.java | 21 +++++++++++++++++++ .../keyboard/latin/utils/ToolbarUtils.kt | 2 ++ layouts.md | 7 +++++-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeySpecParser.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeySpecParser.java index 9eb0b82e4..a8515c66c 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeySpecParser.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeySpecParser.java @@ -166,9 +166,7 @@ public static String getOutputText(@Nullable final String keySpec) { // See {@link #getCode(Resources, String)}. return null; } - if (outputText.isEmpty() && DebugFlags.DEBUG_ENABLED) { - throw new KeySpecParserError("Empty outputText: " + keySpec); - } + // also empty output texts are acceptable return outputText; } final String label = getLabel(keySpec); diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java index 58b375597..9c6693788 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java @@ -16,6 +16,7 @@ import androidx.annotation.Nullable; import helium314.keyboard.latin.R; +import helium314.keyboard.latin.utils.ToolbarKey; import java.util.HashMap; @@ -95,6 +96,26 @@ public final class KeyboardIconsSet { NAME_START_ONEHANDED_KEY, R.styleable.Keyboard_iconStartOneHandedMode, NAME_STOP_ONEHANDED_KEY, R.styleable.Keyboard_iconStopOneHandedMode, NAME_SWITCH_ONEHANDED_KEY, R.styleable.Keyboard_iconSwitchOneHandedMode, + ToolbarKey.VOICE.name(), R.styleable.Keyboard_iconShortcutKey, + ToolbarKey.SETTINGS.name(), R.styleable.Keyboard_iconSettingsKey, + ToolbarKey.CLIPBOARD.name(), R.styleable.Keyboard_iconClipboardNormalKey, + ToolbarKey.SELECT_ALL.name(), R.styleable.Keyboard_iconSelectAll, + ToolbarKey.COPY.name(), R.styleable.Keyboard_iconCopyKey, + ToolbarKey.CUT.name(), R.styleable.Keyboard_iconCutKey, + ToolbarKey.ONE_HANDED.name(), R.styleable.Keyboard_iconStartOneHandedMode, + ToolbarKey.LEFT.name(), R.styleable.Keyboard_iconArrowLeft, + ToolbarKey.RIGHT.name(), R.styleable.Keyboard_iconArrowRight, + ToolbarKey.UP.name(), R.styleable.Keyboard_iconArrowUp, + ToolbarKey.DOWN.name(), R.styleable.Keyboard_iconArrowDown, + ToolbarKey.UNDO.name(), R.styleable.Keyboard_iconUndo, + ToolbarKey.REDO.name(), R.styleable.Keyboard_iconRedo, + ToolbarKey.INCOGNITO.name(), R.styleable.Keyboard_iconIncognitoKey, + ToolbarKey.AUTOCORRECT.name(), R.styleable.Keyboard_iconAutoCorrect, + ToolbarKey.CLEAR_CLIPBOARD.name(),R.styleable.Keyboard_iconClearClipboardKey, + ToolbarKey.FULL_LEFT.name(), R.styleable.Keyboard_iconFullLeft, + ToolbarKey.FULL_RIGHT.name(), R.styleable.Keyboard_iconFullRight, + ToolbarKey.SELECT_WORD.name(), R.styleable.Keyboard_iconSelectWord, + ToolbarKey.CLOSE_HISTORY.name(), R.styleable.Keyboard_iconClose, }; private static final int NUM_ICONS = NAMES_AND_ATTR_IDS.length / 2; diff --git a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt index e584e4d79..672cb144f 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt @@ -10,6 +10,7 @@ import android.widget.ImageView import androidx.appcompat.view.ContextThemeWrapper import androidx.core.content.edit import helium314.keyboard.keyboard.KeyboardTheme +import helium314.keyboard.keyboard.internal.KeyboardIconsSet import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode import helium314.keyboard.latin.R import helium314.keyboard.latin.settings.Settings @@ -73,6 +74,7 @@ fun getCodeForToolbarKeyLongClick(key: ToolbarKey) = when (key) { else -> KeyCode.UNSPECIFIED } +// todo: get the icons from KeyboardIconsSet (but currently it's loaded too late) private fun getStyleableIconId(key: ToolbarKey) = when (key) { VOICE -> R.styleable.Keyboard_iconShortcutKey SETTINGS -> R.styleable.Keyboard_iconSettingsKey diff --git a/layouts.md b/layouts.md index d3d597276..e7191082d 100644 --- a/layouts.md +++ b/layouts.md @@ -86,9 +86,12 @@ Usually the label is what is displayed on the key. However, there are some speci * _comma_: `,` key with special popups, will adapt to language-specific comma, or display `/` in URL fields and `@` in email fields * _space_: space key, with icon when using a number layout * _zwnj_: Zero-width non-joiner (automatically added next to space in alphabet layout for some languages) -* If you want different key label and use text, set the label to [label]|[text], e.g. `aa|bb` will show `aa`, but pressing the key will input `bb`. +* If you want different key label and input text, set the label to [label]|[text], e.g. `aa|bb` will show `aa`, but pressing the key will input `bb`. You can also specify special key codes like `a|!code/key_action_previous`, but it's cleaner to use a json layout and specify the code explicitly. Note that when specifying a code in the label, and a code in a json layout, the code in the label will be ignored. -It's also possible to specify an icon together with a code `!icon/previous_key|!code/key_action_previous`, but this is not fully supported yet. +* It's also possible to specify an icon, like `!icon/previous_key|!code/key_action_previous`. + * For normal keys, even if you specify a code, you will need to add a `|` to the label, e.g. `!icon/go_key|` or `!icon/go_key|ignored` (to be fixed). + * For popups keys, you must _not_ add a `|` (to be fixed). + * You can find available icon names in [KeyboardIconsSet](/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java). You can also use toolbar key icons using the uppercase name of the toolbar key, e.g. `!icon/REDO` ## Adding new layouts / languages * You need a layout file in one of the formats above, and add it to [layouts](app/src/main/assets/layouts) From b3aac1a05a8c20fbb830932077bb0ae285df9a85 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 12 May 2024 12:00:49 +0200 Subject: [PATCH 07/58] add input logic test for double space -> period (and deletion) --- .../helium314/keyboard/latin/InputLogicTest.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt index 055294c20..68d36b977 100644 --- a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt +++ b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt @@ -535,6 +535,16 @@ class InputLogicTest { assertEquals("\"this i", text) } + @Test fun `double space results in period and space, and delete removes the period`() { + reset() + chainInput("hello") + input(' ') + input(' ') + assertEquals("hello. ", text) + functionalKeyPress(KeyCode.DELETE) + assertEquals("hello ", text) + } + // ------- helper functions --------- // should be called before every test, so the same state is guaranteed @@ -566,7 +576,9 @@ class InputLogicTest { latinIME.onEvent(Event.createEventForCodePointFromUnknownSource(codePoint)) handleMessages() - if (currentScript != ScriptUtils.SCRIPT_HANGUL) { // check fails if hangul combiner merges symbols + if (currentScript != ScriptUtils.SCRIPT_HANGUL // check fails if hangul combiner merges symbols + && !(codePoint == Constants.CODE_SPACE && oldBefore.lastOrNull() == ' ') // check fails when 2 spaces are converted into a period + ) { if (phantomSpaceToInsert.isEmpty()) assertEquals(oldBefore + insert, textBeforeCursor) else // in some cases autospace might be suppressed From 7f1161218e11f3a4c6095114aac9dbdce49b2ecd Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 12 May 2024 14:09:43 +0200 Subject: [PATCH 08/58] fix some issues with key texts and icons fixes #750 --- .../helium314/keyboard/keyboard/KeyboardView.java | 12 +++++++++++- .../internal/keyboard_parser/KeyboardParser.kt | 6 +++++- .../internal/keyboard_parser/floris/TextKeyData.kt | 12 ++++++------ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java index f2bcb1431..d5b38fb34 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java @@ -430,7 +430,13 @@ protected void onDrawKeyTopVisuals(@NonNull final Key key, @NonNull final Canvas paint.setTextAlign(Align.CENTER); } if (key.needsAutoXScale()) { - final float ratio = Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / TypefaceUtils.getStringWidth(label, paint)); + final int width; + if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags)) { + // make sure the text stays inside bounds of background drawable + Drawable bg = key.selectBackgroundDrawable(mKeyBackground, mFunctionalKeyBackground, mSpacebarBackground, mActionKeyBackground); + width = Math.min(bg.getBounds().bottom, bg.getBounds().right); + } else width = keyWidth; + final float ratio = Math.min(1.0f, (width * MAX_LABEL_RATIO) / TypefaceUtils.getStringWidth(label, paint)); if (key.needsAutoScale()) { final float autoSize = paint.getTextSize() * ratio; paint.setTextSize(autoSize); @@ -442,6 +448,8 @@ protected void onDrawKeyTopVisuals(@NonNull final Key key, @NonNull final Canvas if (key.isEnabled()) { if (StringUtils.mightBeEmoji(label)) paint.setColor(key.selectTextColor(params) | 0xFF000000); // ignore alpha for emojis (though actually color isn't applied anyway and we could just set white) + else if (key.hasActionKeyBackground()) + paint.setColor(mColors.get(ColorType.ACTION_KEY_ICON)); else paint.setColor(key.selectTextColor(params)); // Set a drop shadow for the text if the shadow radius is positive value. @@ -644,6 +652,8 @@ private void setKeyIconColor(Key key, Drawable icon, Keyboard keyboard) { } else if (key.getCode() == Constants.CODE_SPACE || key.getCode() == KeyCode.ZWNJ) { // set color of default number pad space bar icon for Holo style, or for zero-width non-joiner (zwnj) on some layouts like nepal mColors.setColor(icon, ColorType.KEY_ICON); + } else { + mColors.setColor(icon, ColorType.KEY_TEXT); } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index 75dda2fd2..60958811d 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -9,6 +9,7 @@ import androidx.annotation.StringRes import helium314.keyboard.keyboard.Key import helium314.keyboard.keyboard.Key.KeyParams import helium314.keyboard.keyboard.KeyboardId +import helium314.keyboard.keyboard.KeyboardTheme import helium314.keyboard.keyboard.internal.KeyboardIconsSet import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData @@ -315,7 +316,10 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co newLabel = "${getActionKeyLabel()}|${getActionKeyCode()}", newPopup = popup.merge(getActionKeyPopupKeys()?.let { SimplePopups(it) }), // the label change is messing with toKeyParams, so we need to supply the appropriate BG type here - newType = type ?: KeyType.ENTER_EDITING + newType = type ?: KeyType.ENTER_EDITING, + newLabelFlags = Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_AUTO_X_SCALE or + Key.LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR or + Key.LABEL_FLAGS_HAS_POPUP_HINT or KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) ) else -> { // this is ugly... diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index d1d7c2cab..77e7f997e 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -251,7 +251,7 @@ sealed interface KeyData : AbstractKeyData { KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL, KeyLabel.ALPHA, KeyLabel.COMMA, KeyLabel.PERIOD, KeyLabel.DELETE, KeyLabel.EMOJI, KeyLabel.COM, KeyLabel.LANGUAGE_SWITCH, KeyLabel.NUMPAD -> return Key.BACKGROUND_TYPE_FUNCTIONAL KeyLabel.SPACE, KeyLabel.ZWNJ -> return Key.BACKGROUND_TYPE_SPACEBAR - KeyLabel.ACTION -> return Key.BACKGROUND_TYPE_ACTION +// KeyLabel.ACTION -> return Key.BACKGROUND_TYPE_ACTION KeyLabel.SHIFT -> return getShiftBackground(params) } if (type == KeyType.PLACEHOLDER) return Key.BACKGROUND_TYPE_EMPTY @@ -313,11 +313,11 @@ sealed interface KeyData : AbstractKeyData { return when (label) { KeyLabel.ALPHA, KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL -> Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR KeyLabel.PERIOD, KeyLabel.COMMA -> Key.LABEL_FLAGS_HAS_POPUP_HINT // todo: period also has defaultLabelFlags -> when is this relevant? - KeyLabel.ACTION -> { - Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_AUTO_X_SCALE or - Key.LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR or - Key.LABEL_FLAGS_HAS_POPUP_HINT or KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) - } +// KeyLabel.ACTION -> { +// Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_AUTO_X_SCALE or +// Key.LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR or +// Key.LABEL_FLAGS_HAS_POPUP_HINT or KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) +// } KeyLabel.SPACE -> if (params.mId.isNumberLayout) Key.LABEL_FLAGS_ALIGN_ICON_TO_BOTTOM else 0 KeyLabel.SHIFT -> Key.LABEL_FLAGS_PRESERVE_CASE or if (!params.mId.isAlphabetKeyboard) Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR else 0 KeyLabel.EMOJI -> KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) From 789b76b790e51fe26e1d8a2c2beb0ebaf5a09a01 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 12 May 2024 14:27:53 +0200 Subject: [PATCH 09/58] add eo diacritics needed for multilingual typing, fixes #740 --- app/src/main/assets/locale_key_texts/eo.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/assets/locale_key_texts/eo.txt b/app/src/main/assets/locale_key_texts/eo.txt index 6437f715b..f8ca3f4b4 100644 --- a/app/src/main/assets/locale_key_texts/eo.txt +++ b/app/src/main/assets/locale_key_texts/eo.txt @@ -2,11 +2,12 @@ a á à â ä æ ã å ā ă ą ª e é ě è ê ë ę ė ē i í î ï ĩ ì į ī ı ij +j ĵ o ó ö ô ò õ œ ø ō ő º -u ú ů û ü ù ū ũ ű ų µ -s ß š ś ș ş +u ŭ ú ů û ü ù ū ũ ű ų µ +s ŝ ß š ś ș ş n ñ ń ņ ň ʼn ŋ -c ć č ç ċ +c ĉ ć č ç ċ ŭ y ý ŷ ÿ þ d ð ď đ r ř ŕ ŗ @@ -14,7 +15,7 @@ t ť ț ţ ŧ z ź ż ž k ķ ĸ l ĺ ļ ľ ŀ ł -g ğ ġ ģ +g ĝ ğ ġ ģ v w ŵ h ĥ ħ ĝ w ŵ From 3862d1e9b28026e5f51124c7f2a6b58d61a7248a Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 12 May 2024 18:08:15 +0200 Subject: [PATCH 10/58] identify keyboard icons by name only re-work KeyboardIconsSet also fixes a recent regression where some icons are not displayed --- .../java/helium314/keyboard/keyboard/Key.java | 76 ++++--- .../keyboard/keyboard/KeyboardView.java | 1 + .../clipboard/ClipboardHistoryView.kt | 6 +- .../keyboard/emoji/EmojiPalettesView.java | 5 +- .../keyboard/internal/KeyPreviewView.java | 6 +- .../keyboard/internal/KeySpecParser.java | 11 +- .../keyboard/internal/KeyboardIconsSet.java | 196 ------------------ .../keyboard/internal/KeyboardIconsSet.kt | 97 +++++++++ .../keyboard/internal/PopupKeySpec.java | 15 +- .../keyboard_parser/KeyboardParser.kt | 2 +- .../latin/suggestions/MoreSuggestions.java | 4 +- .../keyboard/latin/utils/ToolbarUtils.kt | 4 +- 12 files changed, 158 insertions(+), 265 deletions(-) delete mode 100644 app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java create mode 100644 app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt diff --git a/app/src/main/java/helium314/keyboard/keyboard/Key.java b/app/src/main/java/helium314/keyboard/keyboard/Key.java index 078b3e2c5..231dd05ad 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/Key.java +++ b/app/src/main/java/helium314/keyboard/keyboard/Key.java @@ -26,8 +26,6 @@ import java.util.Arrays; import java.util.Locale; -import static helium314.keyboard.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -80,7 +78,7 @@ public class Key implements Comparable { public static final int LABEL_FLAGS_DISABLE_ADDITIONAL_POPUP_KEYS = 0x80000000; /** Icon to display instead of a label. Icon takes precedence over a label */ - private final int mIconId; + @NonNull private final String mIconName; /** Width of the key, excluding the gap */ private final int mWidth; @@ -156,29 +154,29 @@ private static final class OptionalAttributes { public final String mOutputText; public final int mAltCode; /** Icon for disabled state */ - public final int mDisabledIconId; + public final String mDisabledIconName; /** The visual insets */ public final int mVisualInsetsLeft; public final int mVisualInsetsRight; private OptionalAttributes(final String outputText, final int altCode, - final int disabledIconId, final int visualInsetsLeft, final int visualInsetsRight) { + final String disabledIconName, final int visualInsetsLeft, final int visualInsetsRight) { mOutputText = outputText; mAltCode = altCode; - mDisabledIconId = disabledIconId; + mDisabledIconName = disabledIconName; mVisualInsetsLeft = visualInsetsLeft; mVisualInsetsRight = visualInsetsRight; } @Nullable public static OptionalAttributes newInstance(final String outputText, final int altCode, - final int disabledIconId, final int visualInsetsLeft, final int visualInsetsRight) { + final String disabledIconName, final int visualInsetsLeft, final int visualInsetsRight) { if (outputText == null && altCode == KeyCode.NOT_SPECIFIED - && disabledIconId == ICON_UNDEFINED && visualInsetsLeft == 0 + && disabledIconName.equals(KeyboardIconsSet.NAME_UNDEFINED) && visualInsetsLeft == 0 && visualInsetsRight == 0) { return null; } - return new OptionalAttributes(outputText, altCode, disabledIconId, visualInsetsLeft, + return new OptionalAttributes(outputText, altCode, disabledIconName, visualInsetsLeft, visualInsetsRight); } } @@ -193,7 +191,7 @@ public static OptionalAttributes newInstance(final String outputText, final int /** * Constructor for a key on PopupKeyKeyboard and on MoreSuggestions. */ - public Key(@Nullable final String label, final int iconId, final int code, + public Key(@Nullable final String label, final String iconName, final int code, @Nullable final String outputText, @Nullable final String hintLabel, final int labelFlags, final int backgroundType, final int x, final int y, final int width, final int height, final int horizontalGap, final int verticalGap) { @@ -210,10 +208,10 @@ public Key(@Nullable final String label, final int iconId, final int code, mPopupKeysColumnAndFlags = 0; mLabel = label; mOptionalAttributes = OptionalAttributes.newInstance(outputText, KeyCode.NOT_SPECIFIED, - ICON_UNDEFINED, 0 /* visualInsetsLeft */, 0 /* visualInsetsRight */); + KeyboardIconsSet.NAME_UNDEFINED, 0, 0); mCode = code; mEnabled = (code != KeyCode.NOT_SPECIFIED); - mIconId = iconId; + mIconName = iconName; // Horizontal gap is divided equally to both sides of the key. mX = x + mHorizontalGap / 2; mY = y; @@ -238,7 +236,7 @@ protected Key(@NonNull final Key key, @Nullable final PopupKeySpec[] popupKeys, mLabel = key.mLabel; mHintLabel = labelHint; mLabelFlags = key.mLabelFlags; - mIconId = key.mIconId; + mIconName = key.mIconName; mWidth = key.mWidth; mHeight = key.mHeight; mHorizontalGap = key.mHorizontalGap; @@ -266,7 +264,7 @@ public Key(@NonNull final Key key, @Nullable final PopupKeySpec[] popupKeys, mLabel = outputText == null ? StringUtils.newSingleCodePointString(code) : outputText; mHintLabel = labelHint; mLabelFlags = key.mLabelFlags; - mIconId = key.mIconId; + mIconName = key.mIconName; mWidth = key.mWidth; mHeight = key.mHeight; mHorizontalGap = key.mHorizontalGap; @@ -279,7 +277,8 @@ public Key(@NonNull final Key key, @Nullable final PopupKeySpec[] popupKeys, mBackgroundType = backgroundType; mActionFlags = key.mActionFlags; mKeyVisualAttributes = key.mKeyVisualAttributes; - mOptionalAttributes = outputText == null ? null : Key.OptionalAttributes.newInstance(outputText, KeyCode.NOT_SPECIFIED, ICON_UNDEFINED, 0, 0); + mOptionalAttributes = outputText == null ? null + : Key.OptionalAttributes.newInstance(outputText, KeyCode.NOT_SPECIFIED, KeyboardIconsSet.NAME_UNDEFINED, 0, 0); mHashCode = key.mHashCode; // Key state. mPressed = key.mPressed; @@ -293,7 +292,7 @@ private Key(KeyParams keyParams) { mLabel = keyParams.mLabel; mHintLabel = keyParams.mHintLabel; mLabelFlags = keyParams.mLabelFlags; - mIconId = keyParams.mIconId; + mIconName = keyParams.mIconName; mPopupKeys = keyParams.mPopupKeys; mPopupKeysColumnAndFlags = keyParams.mPopupKeysColumnAndFlags; mBackgroundType = keyParams.mBackgroundType; @@ -328,7 +327,7 @@ private Key(@NonNull final Key key, @Nullable final PopupKeySpec[] popupKeys) { mLabel = key.mLabel; mHintLabel = key.mHintLabel; mLabelFlags = key.mLabelFlags; - mIconId = key.mIconId; + mIconName = key.mIconName; mWidth = key.mWidth; mHeight = key.mHeight; mHorizontalGap = key.mHorizontalGap; @@ -378,7 +377,7 @@ private static int computeHashCode(final Key key) { key.mCode, key.mLabel, key.mHintLabel, - key.mIconId, + key.mIconName, key.mBackgroundType, Arrays.hashCode(key.mPopupKeys), key.getOutputText(), @@ -405,7 +404,7 @@ private boolean equalsInternal(final Key o) { && o.mCode == mCode && TextUtils.equals(o.mLabel, mLabel) && TextUtils.equals(o.mHintLabel, mHintLabel) - && o.mIconId == mIconId + && o.mIconName == mIconName && o.mBackgroundType == mBackgroundType && Arrays.equals(o.mPopupKeys, mPopupKeys) && TextUtils.equals(o.getOutputText(), getOutputText()) @@ -444,9 +443,9 @@ public String toShortString() { } public String toLongString() { - final int iconId = getIconId(); - final String topVisual = (iconId == KeyboardIconsSet.ICON_UNDEFINED) - ? KeyboardIconsSet.PREFIX_ICON + KeyboardIconsSet.getIconName(iconId) : getLabel(); + final String iconName = getIconName(); + final String topVisual = (iconName.equals(KeyboardIconsSet.NAME_UNDEFINED)) + ? KeyboardIconsSet.PREFIX_ICON + iconName : getLabel(); final String hintLabel = getHintLabel(); final String visual = (hintLabel == null) ? topVisual : topVisual + "^" + hintLabel; return toString() + " " + visual + "/" + backgroundName(mBackgroundType); @@ -702,16 +701,15 @@ public final int getAltCode() { return (attrs != null) ? attrs.mAltCode : KeyCode.NOT_SPECIFIED; } - public int getIconId() { - return mIconId; + public String getIconName() { + return mIconName; } @Nullable public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) { final OptionalAttributes attrs = mOptionalAttributes; - final int disabledIconId = (attrs != null) ? attrs.mDisabledIconId : ICON_UNDEFINED; - final int iconId = mEnabled ? getIconId() : disabledIconId; - final Drawable icon = iconSet.getIconDrawable(iconId); + final String iconName = mEnabled ? getIconName() : ((attrs != null) ? attrs.mDisabledIconName : KeyboardIconsSet.NAME_UNDEFINED); + final Drawable icon = iconSet.getIconDrawable(iconName); if (icon != null) { icon.setAlpha(alpha); } @@ -720,7 +718,7 @@ public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) { @Nullable public Drawable getPreviewIcon(final KeyboardIconsSet iconSet) { - return iconSet.getIconDrawable(getIconId()); + return iconSet.getIconDrawable(getIconName()); } /** @@ -902,7 +900,7 @@ public int[] getState(final boolean pressed) { public final Drawable selectBackgroundDrawable(@NonNull final Drawable keyBackground, @NonNull final Drawable functionalKeyBackground, @NonNull final Drawable spacebarBackground, - @NonNull final Drawable actionKeyBackground) { + @NonNull final Drawable actionKeyBackground) { final Drawable background; if (isAccentColored()) { background = actionKeyBackground; @@ -920,7 +918,7 @@ public final Drawable selectBackgroundDrawable(@NonNull final Drawable keyBackgr public final boolean isAccentColored() { if (hasActionKeyBackground()) return true; - final String iconName = KeyboardIconsSet.getIconName(getIconId()); + final String iconName = getIconName(); return iconName.equals(KeyboardIconsSet.NAME_NEXT_KEY) || iconName.equals(KeyboardIconsSet.NAME_PREVIOUS_KEY) || iconName.equals(KeyboardIconsSet.NAME_CLIPBOARD_ACTION_KEY) @@ -943,8 +941,8 @@ private Spacer(KeyParams keyParams) { */ protected Spacer(final KeyboardParams params, final int x, final int y, final int width, final int height) { - super(null /* label */, ICON_UNDEFINED, KeyCode.NOT_SPECIFIED, null /* outputText */, - null /* hintLabel */, 0 /* labelFlags */, BACKGROUND_TYPE_EMPTY, x, y, width, + super(null, KeyboardIconsSet.NAME_UNDEFINED, KeyCode.NOT_SPECIFIED, null, + null, 0, BACKGROUND_TYPE_EMPTY, x, y, width, height, params.mHorizontalGap, params.mVerticalGap); } } @@ -968,7 +966,7 @@ public static class KeyParams { @Nullable public String mLabel; @Nullable public final String mHintLabel; public final int mLabelFlags; - public final int mIconId; + @NonNull public final String mIconName; @Nullable public PopupKeySpec[] mPopupKeys; public final int mPopupKeysColumnAndFlags; public int mBackgroundType; @@ -1062,7 +1060,7 @@ public KeyParams( mLabelFlags = labelFlags; mWidth = width; mHeight = params.mDefaultRowHeight; - mIconId = KeySpecParser.getIconId(keySpec); + mIconName = KeySpecParser.getIconName(keySpec); final boolean needsToUpcase = needsToUpcase(mLabelFlags, params.mId.mElementId); final Locale localeForUpcasing = params.mId.getLocale(); @@ -1167,7 +1165,7 @@ public KeyParams( : altCodeInAttr; mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode, // disabled icon only ever for old version of shortcut key, visual insets can be replaced with spacer - KeyboardIconsSet.ICON_UNDEFINED, 0, 0); + KeyboardIconsSet.NAME_UNDEFINED, 0, 0); // KeyVisualAttributes for a key essentially are what the theme has, but on a per-key base // could be used e.g. for having a color gradient on key color mKeyVisualAttributes = null; @@ -1207,11 +1205,11 @@ public KeyParams(@Nullable final String label, final int code, @Nullable final S mLabel = label; mOptionalAttributes = code == KeyCode.MULTIPLE_CODE_POINTS - ? OptionalAttributes.newInstance(label, KeyCode.NOT_SPECIFIED, ICON_UNDEFINED, 0, 0) + ? OptionalAttributes.newInstance(label, KeyCode.NOT_SPECIFIED, KeyboardIconsSet.NAME_UNDEFINED, 0, 0) : null; mCode = code; mEnabled = (code != KeyCode.NOT_SPECIFIED); - mIconId = KeyboardIconsSet.ICON_UNDEFINED; + mIconName = KeyboardIconsSet.NAME_UNDEFINED; mKeyVisualAttributes = null; } @@ -1225,7 +1223,7 @@ private KeyParams(final KeyboardParams params) { mHintLabel = null; mKeyVisualAttributes = null; mOptionalAttributes = null; - mIconId = KeyboardIconsSet.ICON_UNDEFINED; + mIconName = KeyboardIconsSet.NAME_UNDEFINED; mBackgroundType = BACKGROUND_TYPE_NORMAL; mActionFlags = ACTION_FLAGS_NO_KEY_PREVIEW; mPopupKeys = null; @@ -1247,7 +1245,7 @@ public KeyParams(final KeyParams keyParams) { mLabel = keyParams.mLabel; mHintLabel = keyParams.mHintLabel; mLabelFlags = keyParams.mLabelFlags; - mIconId = keyParams.mIconId; + mIconName = keyParams.mIconName; mAbsoluteWidth = keyParams.mAbsoluteWidth; mAbsoluteHeight = keyParams.mAbsoluteHeight; mPopupKeys = keyParams.mPopupKeys; diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java index d5b38fb34..6eba7a46b 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java @@ -39,6 +39,7 @@ import helium314.keyboard.latin.settings.Settings; import helium314.keyboard.latin.suggestions.MoreSuggestions; import helium314.keyboard.latin.suggestions.PopupSuggestionsView; +import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.TypefaceUtils; import java.util.HashSet; diff --git a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt index ff79eb8e2..2f169242c 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt @@ -141,9 +141,9 @@ class ClipboardHistoryView @JvmOverloads constructor( } } - private fun setupDeleteKey(key: ImageButton, iconId: Int) { + private fun setupDeleteKey(key: ImageButton, icon: Drawable?) { key.apply { - setImageResource(iconId) + setImageDrawable(icon) Settings.getInstance().current.mColors.setBackground(this, ColorType.FUNCTIONAL_KEY_BACKGROUND) Settings.getInstance().current.mColors.setColor(this, ColorType.KEY_ICON) } @@ -189,7 +189,7 @@ class ClipboardHistoryView @JvmOverloads constructor( val params = KeyDrawParams() params.updateParams(clipboardLayoutParams.actionBarContentHeight, keyVisualAttr) setupAlphabetKey(alphabetKey, switchToAlphaLabel, params) - setupDeleteKey(deleteKey, iconSet.getIconResourceId(KeyboardIconsSet.NAME_DELETE_KEY)) + setupDeleteKey(deleteKey, iconSet.getIconDrawable(KeyboardIconsSet.NAME_DELETE_KEY)) setupClipKey(params) placeholderView.apply { diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java index b93137599..a29412b9e 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java @@ -360,10 +360,7 @@ public void startEmojiPalettes(final String switchToAlphaLabel, final KeyVisualAttributes keyVisualAttr, final KeyboardIconsSet iconSet) { initialize(); - final int deleteIconResId = iconSet.getIconResourceId(KeyboardIconsSet.NAME_DELETE_KEY); - if (deleteIconResId != 0) { - mDeleteKey.setImageResource(deleteIconResId); - } + mDeleteKey.setImageDrawable(iconSet.getIconDrawable(KeyboardIconsSet.NAME_DELETE_KEY)); mEmojiLayoutParams.setActionBarProperties(findViewById(R.id.action_bar)); final KeyDrawParams params = new KeyDrawParams(); params.updateParams(mEmojiLayoutParams.getActionBarHeight(), keyVisualAttr); diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyPreviewView.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyPreviewView.java index 7c735810e..98b646f16 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyPreviewView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyPreviewView.java @@ -43,11 +43,9 @@ public KeyPreviewView(final Context context, final AttributeSet attrs, final int setGravity(Gravity.CENTER); } - public void setPreviewVisual(final Key key, final KeyboardIconsSet iconsSet, - final KeyDrawParams drawParams) { + public void setPreviewVisual(final Key key, final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams) { // What we show as preview should match what we show on a key top in onDraw(). - final int iconId = key.getIconId(); - if (iconId != KeyboardIconsSet.ICON_UNDEFINED) { + if (!key.getIconName().equals(KeyboardIconsSet.NAME_UNDEFINED)) { setCompoundDrawables(null, null, null, key.getPreviewIcon(iconsSet)); setText(null); return; diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeySpecParser.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeySpecParser.java index a8515c66c..1c2ba1074 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeySpecParser.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeySpecParser.java @@ -221,18 +221,17 @@ public static int parseCode(@Nullable final String text, final int defaultCode) return defaultCode; } - public static int getIconId(@Nullable final String keySpec) { + @NonNull + public static String getIconName(@Nullable final String keySpec) { if (keySpec == null) { // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory. - return KeyboardIconsSet.ICON_UNDEFINED; + return KeyboardIconsSet.NAME_UNDEFINED; } if (!hasIcon(keySpec)) { - return KeyboardIconsSet.ICON_UNDEFINED; + return KeyboardIconsSet.NAME_UNDEFINED; } final int labelEnd = indexOfLabelEnd(keySpec); - final String iconName = getBeforeLabelEnd(keySpec, labelEnd) - .substring(KeyboardIconsSet.PREFIX_ICON.length()); - return KeyboardIconsSet.getIconId(iconName); + return getBeforeLabelEnd(keySpec, labelEnd).substring(KeyboardIconsSet.PREFIX_ICON.length()).intern(); } public static final class KeySpecParserError extends RuntimeException { diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java deleted file mode 100644 index 9c6693788..000000000 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * modified - * SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only - */ - -package helium314.keyboard.keyboard.internal; - -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import helium314.keyboard.latin.utils.Log; -import android.util.SparseIntArray; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import helium314.keyboard.latin.R; -import helium314.keyboard.latin.utils.ToolbarKey; - -import java.util.HashMap; - -public final class KeyboardIconsSet { - private static final String TAG = KeyboardIconsSet.class.getSimpleName(); - - public static final String PREFIX_ICON = "!icon/"; - public static final int ICON_UNDEFINED = 0; - private static final int ATTR_UNDEFINED = 0; - - private static final String NAME_UNDEFINED = "undefined"; - public static final String NAME_SHIFT_KEY = "shift_key"; - public static final String NAME_SHIFT_KEY_SHIFTED = "shift_key_shifted"; - public static final String NAME_DELETE_KEY = "delete_key"; - public static final String NAME_SETTINGS_KEY = "settings_key"; - public static final String NAME_SPACE_KEY = "space_key"; - public static final String NAME_SPACE_KEY_FOR_NUMBER_LAYOUT = "space_key_for_number_layout"; - public static final String NAME_ENTER_KEY = "enter_key"; - public static final String NAME_GO_KEY = "go_key"; - public static final String NAME_SEARCH_KEY = "search_key"; - public static final String NAME_SEND_KEY = "send_key"; - public static final String NAME_NEXT_KEY = "next_key"; - public static final String NAME_DONE_KEY = "done_key"; - public static final String NAME_PREVIOUS_KEY = "previous_key"; - public static final String NAME_TAB_KEY = "tab_key"; - public static final String NAME_SHORTCUT_KEY = "shortcut_key"; - public static final String NAME_INCOGNITO_KEY = "incognito_key"; - public static final String NAME_SHORTCUT_KEY_DISABLED = "shortcut_key_disabled"; - public static final String NAME_LANGUAGE_SWITCH_KEY = "language_switch_key"; - public static final String NAME_ZWNJ_KEY = "zwnj_key"; - public static final String NAME_ZWJ_KEY = "zwj_key"; - public static final String NAME_EMOJI_ACTION_KEY = "emoji_action_key"; - public static final String NAME_EMOJI_NORMAL_KEY = "emoji_normal_key"; - public static final String NAME_CLIPBOARD_ACTION_KEY = "clipboard_action_key"; - public static final String NAME_CLIPBOARD_NORMAL_KEY = "clipboard_normal_key"; - public static final String NAME_CLEAR_CLIPBOARD_KEY = "clear_clipboard_key"; - public static final String NAME_CUT_KEY = "cut_key"; - public static final String NAME_NUMPAD_KEY = "numpad_key"; - public static final String NAME_START_ONEHANDED_KEY = "start_onehanded_mode_key"; - public static final String NAME_STOP_ONEHANDED_KEY = "stop_onehanded_mode_key"; - public static final String NAME_SWITCH_ONEHANDED_KEY = "switch_onehanded_key"; - - private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray(); - - // Icon name to icon id map. - private static final HashMap sNameToIdsMap = new HashMap<>(); - - private static final Object[] NAMES_AND_ATTR_IDS = { - NAME_UNDEFINED, ATTR_UNDEFINED, - NAME_SHIFT_KEY, R.styleable.Keyboard_iconShiftKey, - NAME_DELETE_KEY, R.styleable.Keyboard_iconDeleteKey, - NAME_SETTINGS_KEY, R.styleable.Keyboard_iconSettingsKey, - NAME_SPACE_KEY, R.styleable.Keyboard_iconSpaceKey, - NAME_ENTER_KEY, R.styleable.Keyboard_iconEnterKey, - NAME_GO_KEY, R.styleable.Keyboard_iconGoKey, - NAME_SEARCH_KEY, R.styleable.Keyboard_iconSearchKey, - NAME_SEND_KEY, R.styleable.Keyboard_iconSendKey, - NAME_NEXT_KEY, R.styleable.Keyboard_iconNextKey, - NAME_DONE_KEY, R.styleable.Keyboard_iconDoneKey, - NAME_PREVIOUS_KEY, R.styleable.Keyboard_iconPreviousKey, - NAME_TAB_KEY, R.styleable.Keyboard_iconTabKey, - NAME_SHORTCUT_KEY, R.styleable.Keyboard_iconShortcutKey, - NAME_INCOGNITO_KEY, R.styleable.Keyboard_iconIncognitoKey, - NAME_SPACE_KEY_FOR_NUMBER_LAYOUT, R.styleable.Keyboard_iconSpaceKeyForNumberLayout, - NAME_SHIFT_KEY_SHIFTED, R.styleable.Keyboard_iconShiftKeyShifted, - NAME_SHORTCUT_KEY_DISABLED, R.styleable.Keyboard_iconShortcutKeyDisabled, - NAME_LANGUAGE_SWITCH_KEY, R.styleable.Keyboard_iconLanguageSwitchKey, - NAME_ZWNJ_KEY, R.styleable.Keyboard_iconZwnjKey, - NAME_ZWJ_KEY, R.styleable.Keyboard_iconZwjKey, - NAME_EMOJI_ACTION_KEY, R.styleable.Keyboard_iconEmojiActionKey, - NAME_EMOJI_NORMAL_KEY, R.styleable.Keyboard_iconEmojiNormalKey, - NAME_CLIPBOARD_ACTION_KEY, R.styleable.Keyboard_iconClipboardActionKey, - NAME_CLIPBOARD_NORMAL_KEY, R.styleable.Keyboard_iconClipboardNormalKey, - NAME_CLEAR_CLIPBOARD_KEY, R.styleable.Keyboard_iconClearClipboardKey, - NAME_CUT_KEY, R.styleable.Keyboard_iconCutKey, - NAME_NUMPAD_KEY, R.styleable.Keyboard_iconNumpadKey, - NAME_START_ONEHANDED_KEY, R.styleable.Keyboard_iconStartOneHandedMode, - NAME_STOP_ONEHANDED_KEY, R.styleable.Keyboard_iconStopOneHandedMode, - NAME_SWITCH_ONEHANDED_KEY, R.styleable.Keyboard_iconSwitchOneHandedMode, - ToolbarKey.VOICE.name(), R.styleable.Keyboard_iconShortcutKey, - ToolbarKey.SETTINGS.name(), R.styleable.Keyboard_iconSettingsKey, - ToolbarKey.CLIPBOARD.name(), R.styleable.Keyboard_iconClipboardNormalKey, - ToolbarKey.SELECT_ALL.name(), R.styleable.Keyboard_iconSelectAll, - ToolbarKey.COPY.name(), R.styleable.Keyboard_iconCopyKey, - ToolbarKey.CUT.name(), R.styleable.Keyboard_iconCutKey, - ToolbarKey.ONE_HANDED.name(), R.styleable.Keyboard_iconStartOneHandedMode, - ToolbarKey.LEFT.name(), R.styleable.Keyboard_iconArrowLeft, - ToolbarKey.RIGHT.name(), R.styleable.Keyboard_iconArrowRight, - ToolbarKey.UP.name(), R.styleable.Keyboard_iconArrowUp, - ToolbarKey.DOWN.name(), R.styleable.Keyboard_iconArrowDown, - ToolbarKey.UNDO.name(), R.styleable.Keyboard_iconUndo, - ToolbarKey.REDO.name(), R.styleable.Keyboard_iconRedo, - ToolbarKey.INCOGNITO.name(), R.styleable.Keyboard_iconIncognitoKey, - ToolbarKey.AUTOCORRECT.name(), R.styleable.Keyboard_iconAutoCorrect, - ToolbarKey.CLEAR_CLIPBOARD.name(),R.styleable.Keyboard_iconClearClipboardKey, - ToolbarKey.FULL_LEFT.name(), R.styleable.Keyboard_iconFullLeft, - ToolbarKey.FULL_RIGHT.name(), R.styleable.Keyboard_iconFullRight, - ToolbarKey.SELECT_WORD.name(), R.styleable.Keyboard_iconSelectWord, - ToolbarKey.CLOSE_HISTORY.name(), R.styleable.Keyboard_iconClose, - }; - - private static final int NUM_ICONS = NAMES_AND_ATTR_IDS.length / 2; - private static final String[] ICON_NAMES = new String[NUM_ICONS]; - private final Drawable[] mIcons = new Drawable[NUM_ICONS]; - private final int[] mIconResourceIds = new int[NUM_ICONS]; - - static { - int iconId = ICON_UNDEFINED; - for (int i = 0; i < NAMES_AND_ATTR_IDS.length; i += 2) { - final String name = (String)NAMES_AND_ATTR_IDS[i]; - final Integer attrId = (Integer)NAMES_AND_ATTR_IDS[i + 1]; - if (attrId != ATTR_UNDEFINED) { - ATTR_ID_TO_ICON_ID.put(attrId, iconId); - } - sNameToIdsMap.put(name, iconId); - ICON_NAMES[iconId] = name; - iconId++; - } - } - - public void loadIcons(final TypedArray keyboardAttrs) { - final int size = ATTR_ID_TO_ICON_ID.size(); - for (int index = 0; index < size; index++) { - final int attrId = ATTR_ID_TO_ICON_ID.keyAt(index); - try { - final Drawable icon = keyboardAttrs.getDrawable(attrId); - setDefaultBounds(icon); - final Integer iconId = ATTR_ID_TO_ICON_ID.get(attrId); - mIcons[iconId] = icon; - mIconResourceIds[iconId] = keyboardAttrs.getResourceId(attrId, 0); - } catch (Resources.NotFoundException e) { - Log.w(TAG, "Drawable resource for icon #" - + keyboardAttrs.getResources().getResourceEntryName(attrId) - + " not found"); - } - } - } - - private static boolean isValidIconId(final int iconId) { - return iconId >= 0 && iconId < ICON_NAMES.length; - } - - @NonNull - public static String getIconName(final int iconId) { - return isValidIconId(iconId) ? ICON_NAMES[iconId] : "unknown<" + iconId + ">"; - } - - public static int getIconId(final String name) { - Integer iconId = sNameToIdsMap.get(name); - if (iconId != null) { - return iconId; - } - throw new RuntimeException("unknown icon name: " + name); - } - - public int getIconResourceId(final String name) { - final int iconId = getIconId(name); - if (isValidIconId(iconId)) { - return mIconResourceIds[iconId]; - } - throw new RuntimeException("unknown icon name: " + name); - } - - @Nullable - public Drawable getIconDrawable(final int iconId) { - if (isValidIconId(iconId)) { - return mIcons[iconId]; - } - throw new RuntimeException("unknown icon id: " + getIconName(iconId)); - } - - private static void setDefaultBounds(final Drawable icon) { - if (icon != null) { - icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); - } - } -} diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt new file mode 100644 index 000000000..f143bdcbb --- /dev/null +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt @@ -0,0 +1,97 @@ +package helium314.keyboard.keyboard.internal + +import android.content.res.Resources +import android.content.res.TypedArray +import android.graphics.drawable.Drawable +import helium314.keyboard.latin.R +import helium314.keyboard.latin.utils.Log +import helium314.keyboard.latin.utils.ToolbarKey +import helium314.keyboard.latin.utils.getStyleableIconId + +class KeyboardIconsSet { + private val iconsByName = HashMap(styleableIdByName.size) + + fun loadIcons(keyboardAttrs: TypedArray) { + styleableIdByName.forEach { (name, id) -> + try { + val icon = keyboardAttrs.getDrawable(id) ?: return@forEach + icon.setBounds(0, 0, icon.intrinsicWidth, icon.intrinsicHeight) + iconsByName[name] = icon + } catch (e: Resources.NotFoundException) { + Log.w(TAG, "Drawable resource for icon #${keyboardAttrs.resources.getResourceEntryName(id)} not found") + } + } + } + + fun getIconDrawable(name: String) = iconsByName[name] + + companion object { + private val TAG = KeyboardIconsSet::class.simpleName + const val PREFIX_ICON = "!icon/" + + const val NAME_UNDEFINED = "undefined" + const val NAME_SHIFT_KEY = "shift_key" + const val NAME_SHIFT_KEY_SHIFTED = "shift_key_shifted" + const val NAME_DELETE_KEY = "delete_key" + const val NAME_SETTINGS_KEY = "settings_key" + const val NAME_SPACE_KEY = "space_key" + const val NAME_SPACE_KEY_FOR_NUMBER_LAYOUT = "space_key_for_number_layout" + const val NAME_ENTER_KEY = "enter_key" + const val NAME_GO_KEY = "go_key" + const val NAME_SEARCH_KEY = "search_key" + const val NAME_SEND_KEY = "send_key" + const val NAME_NEXT_KEY = "next_key" + const val NAME_DONE_KEY = "done_key" + const val NAME_PREVIOUS_KEY = "previous_key" + const val NAME_TAB_KEY = "tab_key" + const val NAME_SHORTCUT_KEY = "shortcut_key" + const val NAME_INCOGNITO_KEY = "incognito_key" + const val NAME_SHORTCUT_KEY_DISABLED = "shortcut_key_disabled" + const val NAME_LANGUAGE_SWITCH_KEY = "language_switch_key" + const val NAME_ZWNJ_KEY = "zwnj_key" + const val NAME_ZWJ_KEY = "zwj_key" + const val NAME_EMOJI_ACTION_KEY = "emoji_action_key" + const val NAME_EMOJI_NORMAL_KEY = "emoji_normal_key" + const val NAME_CLIPBOARD_ACTION_KEY = "clipboard_action_key" + const val NAME_CLIPBOARD_NORMAL_KEY = "clipboard_normal_key" + const val NAME_CLEAR_CLIPBOARD_KEY = "clear_clipboard_key" + const val NAME_CUT_KEY = "cut_key" + const val NAME_NUMPAD_KEY = "numpad_key" + const val NAME_START_ONEHANDED_KEY = "start_onehanded_mode_key" + const val NAME_STOP_ONEHANDED_KEY = "stop_onehanded_mode_key" + const val NAME_SWITCH_ONEHANDED_KEY = "switch_onehanded_key" + + private val styleableIdByName = hashMapOf( + NAME_SHIFT_KEY to R.styleable.Keyboard_iconShiftKey, + NAME_DELETE_KEY to R.styleable.Keyboard_iconDeleteKey, + NAME_SETTINGS_KEY to R.styleable.Keyboard_iconSettingsKey, + NAME_SPACE_KEY to R.styleable.Keyboard_iconSpaceKey, + NAME_ENTER_KEY to R.styleable.Keyboard_iconEnterKey, + NAME_GO_KEY to R.styleable.Keyboard_iconGoKey, + NAME_SEARCH_KEY to R.styleable.Keyboard_iconSearchKey, + NAME_SEND_KEY to R.styleable.Keyboard_iconSendKey, + NAME_NEXT_KEY to R.styleable.Keyboard_iconNextKey, + NAME_DONE_KEY to R.styleable.Keyboard_iconDoneKey, + NAME_PREVIOUS_KEY to R.styleable.Keyboard_iconPreviousKey, + NAME_TAB_KEY to R.styleable.Keyboard_iconTabKey, + NAME_SHORTCUT_KEY to R.styleable.Keyboard_iconShortcutKey, + NAME_INCOGNITO_KEY to R.styleable.Keyboard_iconIncognitoKey, + NAME_SPACE_KEY_FOR_NUMBER_LAYOUT to R.styleable.Keyboard_iconSpaceKeyForNumberLayout, + NAME_SHIFT_KEY_SHIFTED to R.styleable.Keyboard_iconShiftKeyShifted, + NAME_SHORTCUT_KEY_DISABLED to R.styleable.Keyboard_iconShortcutKeyDisabled, + NAME_LANGUAGE_SWITCH_KEY to R.styleable.Keyboard_iconLanguageSwitchKey, + NAME_ZWNJ_KEY to R.styleable.Keyboard_iconZwnjKey, + NAME_ZWJ_KEY to R.styleable.Keyboard_iconZwjKey, + NAME_EMOJI_ACTION_KEY to R.styleable.Keyboard_iconEmojiActionKey, + NAME_EMOJI_NORMAL_KEY to R.styleable.Keyboard_iconEmojiNormalKey, + NAME_CLIPBOARD_ACTION_KEY to R.styleable.Keyboard_iconClipboardActionKey, + NAME_CLIPBOARD_NORMAL_KEY to R.styleable.Keyboard_iconClipboardNormalKey, + NAME_CLEAR_CLIPBOARD_KEY to R.styleable.Keyboard_iconClearClipboardKey, + NAME_CUT_KEY to R.styleable.Keyboard_iconCutKey, + NAME_NUMPAD_KEY to R.styleable.Keyboard_iconNumpadKey, + NAME_START_ONEHANDED_KEY to R.styleable.Keyboard_iconStartOneHandedMode, + NAME_STOP_ONEHANDED_KEY to R.styleable.Keyboard_iconStopOneHandedMode, + NAME_SWITCH_ONEHANDED_KEY to R.styleable.Keyboard_iconSwitchOneHandedMode, + ).apply { ToolbarKey.entries.forEach { put(it.name, getStyleableIconId(it)) } } + } +} diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/PopupKeySpec.java b/app/src/main/java/helium314/keyboard/keyboard/internal/PopupKeySpec.java index 645e002c9..946dc5ba6 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/PopupKeySpec.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/PopupKeySpec.java @@ -40,7 +40,8 @@ public final class PopupKeySpec { public final String mLabel; @Nullable public final String mOutputText; - public final int mIconId; + @NonNull + public final String mIconName; public PopupKeySpec(@NonNull final String popupKeySpec, boolean needsToUpperCase, @NonNull final Locale locale) { @@ -63,13 +64,13 @@ public PopupKeySpec(@NonNull final String popupKeySpec, boolean needsToUpperCase mOutputText = needsToUpperCase ? StringUtils.toTitleCaseOfKeyLabel(outputText, locale) : outputText; } - mIconId = KeySpecParser.getIconId(popupKeySpec); + mIconName = KeySpecParser.getIconName(popupKeySpec); } @NonNull public Key buildKey(final int x, final int y, final int labelFlags, @NonNull final KeyboardParams params) { - return new Key(mLabel, mIconId, mCode, mOutputText, null /* hintLabel */, labelFlags, + return new Key(mLabel, mIconName, mCode, mOutputText, null /* hintLabel */, labelFlags, Key.BACKGROUND_TYPE_NORMAL, x, y, params.mDefaultAbsoluteKeyWidth, params.mDefaultAbsoluteRowHeight, params.mHorizontalGap, params.mVerticalGap); } @@ -77,7 +78,7 @@ public Key buildKey(final int x, final int y, final int labelFlags, @Override public int hashCode() { int hashCode = 31 + mCode; - hashCode = hashCode * 31 + mIconId; + hashCode = hashCode * 31 + mIconName.hashCode(); final String label = mLabel; hashCode = hashCode * 31 + (label == null ? 0 : label.hashCode()); final String outputText = mOutputText; @@ -93,7 +94,7 @@ public boolean equals(final Object o) { if (o instanceof PopupKeySpec) { final PopupKeySpec other = (PopupKeySpec)o; return mCode == other.mCode - && mIconId == other.mIconId + && mIconName.equals(other.mIconName) && TextUtils.equals(mLabel, other.mLabel) && TextUtils.equals(mOutputText, other.mOutputText); } @@ -102,8 +103,8 @@ public boolean equals(final Object o) { @Override public String toString() { - final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel - : KeyboardIconsSet.PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId)); + final String label = (mIconName.equals(KeyboardIconsSet.NAME_UNDEFINED) ? mLabel + : KeyboardIconsSet.PREFIX_ICON + mIconName); final String output = (mCode == KeyCode.MULTIPLE_CODE_POINTS ? mOutputText : Constants.printableCode(mCode)); if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) { diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index 60958811d..dca131e79 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -467,7 +467,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } private fun String.replaceIconWithLabelIfNoDrawable(): String { - if (params.mIconsSet.getIconDrawable(KeyboardIconsSet.getIconId(this)) != null) return this + if (params.mIconsSet.getIconDrawable(this) != null) return this if (params.mId.mWidth == AndroidSpellCheckerService.SPELLCHECKER_DUMMY_KEYBOARD_WIDTH && params.mId.mHeight == AndroidSpellCheckerService.SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT && !params.mId.mSubtype.hasExtraValue(Constants.Subtype.ExtraValue.EMOJI_CAPABLE) diff --git a/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java b/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java index 28e2f57b3..93030def7 100644 --- a/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java +++ b/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java @@ -230,8 +230,8 @@ static final class MoreSuggestionKey extends Key { public MoreSuggestionKey(final String word, final String info, final int index, final MoreSuggestionsParam params) { - super(word /* label */, KeyboardIconsSet.ICON_UNDEFINED, KeyCode.MULTIPLE_CODE_POINTS, - word /* outputText */, info, 0 /* labelFlags */, Key.BACKGROUND_TYPE_NORMAL, + super(word, KeyboardIconsSet.NAME_UNDEFINED, KeyCode.MULTIPLE_CODE_POINTS, + word, info, 0, Key.BACKGROUND_TYPE_NORMAL, params.getX(index), params.getY(index), params.getWidth(index), params.mDefaultAbsoluteRowHeight, params.mHorizontalGap, params.mVerticalGap); mSuggestedWordIndex = index; diff --git a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt index 672cb144f..3ec59fcb7 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt @@ -10,7 +10,6 @@ import android.widget.ImageView import androidx.appcompat.view.ContextThemeWrapper import androidx.core.content.edit import helium314.keyboard.keyboard.KeyboardTheme -import helium314.keyboard.keyboard.internal.KeyboardIconsSet import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode import helium314.keyboard.latin.R import helium314.keyboard.latin.settings.Settings @@ -74,8 +73,7 @@ fun getCodeForToolbarKeyLongClick(key: ToolbarKey) = when (key) { else -> KeyCode.UNSPECIFIED } -// todo: get the icons from KeyboardIconsSet (but currently it's loaded too late) -private fun getStyleableIconId(key: ToolbarKey) = when (key) { +fun getStyleableIconId(key: ToolbarKey) = when (key) { VOICE -> R.styleable.Keyboard_iconShortcutKey SETTINGS -> R.styleable.Keyboard_iconSettingsKey CLIPBOARD -> R.styleable.Keyboard_iconClipboardNormalKey From 1d1e0cc987255164f873fca24d054eb7c46d492a Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 12 May 2024 19:06:04 +0200 Subject: [PATCH 11/58] always set AllowRedundantPopupKeys in the same place --- .../keyboard/keyboard/internal/KeyboardBuilder.kt | 9 ++++----- .../keyboard/internal/keyboard_parser/KeyboardParser.kt | 6 +----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt index f29e6ba79..b3fd54887 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt @@ -43,19 +43,18 @@ open class KeyboardBuilder(protected val mContext: Context, mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height) } - fun setAllowRedundantPopupKeys(enabled: Boolean) { - mParams.mAllowRedundantPopupKeys = enabled - } - fun load(id: KeyboardId): KeyboardBuilder { mParams.mId = id if (id.isEmojiKeyboard) { - setAllowRedundantPopupKeys(true) + mParams.mAllowRedundantPopupKeys = true readAttributes(R.xml.kbd_emoji) keysInRows = EmojiParser(mParams, mContext).parse() } else { try { val sv = Settings.getInstance().current + // previously was false for nordic and serbian_qwertz, true for all others + // todo: add setting? maybe users want it in a custom layout + mParams.mAllowRedundantPopupKeys = mParams.mId.mElementId != KeyboardId.ELEMENT_SYMBOLS addLocaleKeyTextsToParams(mContext, mParams, sv.mShowMorePopupKeys) mParams.mPopupKeyTypes.addAll(sv.mPopupKeyTypes) // add label source only if popup key type enabled diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index dca131e79..51062d742 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -63,7 +63,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co fun parseLayoutString(layoutContent: String): ArrayList> { params.readAttributes(context, null) params.mProximityCharsCorrectionEnabled = infos.enableProximityCharsCorrection - params.mAllowRedundantPopupKeys = infos.allowRedundantPopupKeys if (infos.touchPositionCorrectionData == null) // need to set correctly, as it's not properly done in readAttributes with attr = null params.mTouchPositionCorrection.load(emptyArray()) else @@ -551,7 +550,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET else -> true } - val allowRedundantPopupKeys = params.mId.mElementId != KeyboardId.ELEMENT_SYMBOLS // todo: always set to false? // essentially this is default for 4 row and non-alphabet layouts, maybe this could be determined automatically instead of using a list // todo: check the difference between default (i.e. none) and holo (test behavior on keyboard) val touchPositionCorrectionData = if (params.mId.isAlphabetKeyboard && layout in listOf("armenian_phonetic", "khmer", "lao", "malayalam", "pcqwerty", "thai")) @@ -561,7 +559,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co val hasShiftKey = !params.mId.isAlphabetKeyboard || layout !in listOf("hindi_compact", "bengali", "arabic", "arabic_pc", "hebrew", "kannada", "kannada_extended","malayalam", "marathi", "farsi", "tamil", "telugu") val numbersOnTopRow = layout !in listOf("pcqwerty", "lao", "thai", "korean_sebeolsik_390", "korean_sebeolsik_final") - return LayoutInfos(enableProximityCharsCorrection, allowRedundantPopupKeys, touchPositionCorrectionData, hasShiftKey, numbersOnTopRow) + return LayoutInfos(enableProximityCharsCorrection, touchPositionCorrectionData, hasShiftKey, numbersOnTopRow) } } @@ -572,8 +570,6 @@ data class LayoutInfos( // disabled by default, but enabled for all alphabet layouts // currently set in keyboardLayoutSet val enableProximityCharsCorrection: Boolean = false, - // previously was false for nordic and serbian_qwertz, true for all others - val allowRedundantPopupKeys: Boolean = true, // there is holo, default and null // null only for popupKeys keyboard val touchPositionCorrectionData: Int? = null, From 1bd30f612bb3a8105fa15e224d8719c205fcc22f Mon Sep 17 00:00:00 2001 From: Helium314 Date: Mon, 13 May 2024 19:34:59 +0200 Subject: [PATCH 12/58] fix long-pressing pinned toolbar key when it's not in normal toolbar --- .../keyboard/latin/suggestions/SuggestionStripView.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java index 20fc3ee36..a8b4ffc2b 100644 --- a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java +++ b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java @@ -362,7 +362,7 @@ public void dismissMoreSuggestionsPanel() { @Override public boolean onLongClick(final View view) { AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(Constants.NOT_A_CODE, this); - if (mToolbar.findViewWithTag(view.getTag()) != null) { + if (view.getTag() instanceof ToolbarKey) { onLongClickToolKey(view); return true; } @@ -375,9 +375,9 @@ private void onLongClickToolKey(final View view) { if (!(view.getTag() instanceof ToolbarKey tag)) return; if (view.getParent() == mPinnedKeys) { final int longClickCode = getCodeForToolbarKeyLongClick(tag); -// if (longClickCode != KeyCode.UNSPECIFIED) { + if (longClickCode != KeyCode.UNSPECIFIED) { mListener.onCodeInput(longClickCode, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, false); -// } + } } else if (view.getParent() == mToolbar) { final View pinnedKeyView = mPinnedKeys.findViewWithTag(tag); if (pinnedKeyView == null) { From ed776c38043a87f101890376cd1715e2324e1790 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Tue, 14 May 2024 23:31:55 +0200 Subject: [PATCH 13/58] overhaul style of parsing layouts and add a cache --- .../keyboard/keyboard/KeyboardLayoutSet.java | 2 + .../keyboard/internal/KeyboardBuilder.kt | 2 +- .../keyboard_parser/JsonKeyboardParser.kt | 72 ------- .../keyboard_parser/KeyboardParser.kt | 109 ++-------- .../keyboard_parser/RawKeyboardParser.kt | 199 ++++++++++++++++++ .../keyboard_parser/SimpleKeyboardParser.kt | 48 ----- .../main/java/helium314/keyboard/latin/App.kt | 5 +- .../settings/AdvancedSettingsFragment.kt | 18 +- .../keyboard/latin/settings/Settings.java | 17 -- .../keyboard/latin/utils/CustomLayoutUtils.kt | 23 +- .../keyboard/latin/utils/SubtypeSettings.kt | 2 +- .../helium314/keyboard/KeyboardParserTest.kt | 21 +- 12 files changed, 256 insertions(+), 262 deletions(-) delete mode 100644 app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/JsonKeyboardParser.kt create mode 100644 app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt delete mode 100644 app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/SimpleKeyboardParser.kt diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java index 397b063a9..e77b976f3 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java @@ -17,6 +17,7 @@ import helium314.keyboard.keyboard.internal.UniqueKeysCache; import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyboardInfos; import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyboardInfosKt; +import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser; import helium314.keyboard.latin.RichInputMethodSubtype; import helium314.keyboard.latin.utils.InputTypeUtils; import helium314.keyboard.latin.utils.Log; @@ -99,6 +100,7 @@ public static void onKeyboardThemeChanged() { private static void clearKeyboardCache() { sKeyboardCache.clear(); sUniqueKeysCache.clear(); + RawKeyboardParser.INSTANCE.clearCache(); } KeyboardLayoutSet(final Context context, @NonNull final Params params) { diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt index b3fd54887..855c31d4b 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt @@ -59,7 +59,7 @@ open class KeyboardBuilder(protected val mContext: Context, mParams.mPopupKeyTypes.addAll(sv.mPopupKeyTypes) // add label source only if popup key type enabled sv.mPopupKeyLabelSources.forEach { if (it in sv.mPopupKeyTypes) mParams.mPopupKeyLabelSources.add(it) } - keysInRows = KeyboardParser.parseLayout(mParams, mContext) + keysInRows = KeyboardParser(mParams, mContext).parseLayout() determineAbsoluteValues() } catch (e: Exception) { Log.e(TAG, "error parsing layout $id ${id.mElementId}", e) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/JsonKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/JsonKeyboardParser.kt deleted file mode 100644 index bb7f0d563..000000000 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/JsonKeyboardParser.kt +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -package helium314.keyboard.keyboard.internal.keyboard_parser - -import android.content.Context -import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.polymorphic -import helium314.keyboard.keyboard.internal.KeyboardParams -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AbstractKeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AutoTextKeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CaseSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CharWidthSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KanaSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.LayoutDirectionSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.MultiTextKeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.ShiftStateSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.VariationSelector - -/** - * Parser for json layout files as used in FlorisBoard, see floris directory for classes taken from FlorisBoard. - * Some differences to the FlorisBoard keys: - * (currently) only normal keys supported - * if label or code are missing one is created from the other - * auto_text_key ignored (i.e. interpreted like the default TextKey) - * codes of multi_text_key not used, only the label - * (currently) popups is always read to [number, main, relevant] layoutPopupKeys, no choice of which to use or which hint is provided - */ -class JsonKeyboardParser(private val params: KeyboardParams, context: Context) : KeyboardParser(params, context) { - - override fun parseCoreLayout(layoutContent: String): MutableList> { - val florisKeyData: List> = florisJsonConfig.decodeFromString(layoutContent) - // initially 200 ms parse (debug build on S4 mini) - // after a few parses it's optimized and 20-30 ms - // whole load is 50-70 ms vs 30-55 with simple parser -> it's ok - return florisKeyData.mapTo(mutableListOf()) { it.mapNotNull { it.compute(params) } } - } - -} - -/* - * Copyright (C) 2021 Patrick Goldinger - * modified - * SPDX-License-Identifier: Apache-2.0 - */ -private val florisJsonConfig = Json { - classDiscriminator = "$" - encodeDefaults = true - ignoreUnknownKeys = true - isLenient = true - serializersModule = SerializersModule { - polymorphic(AbstractKeyData::class) { - subclass(TextKeyData::class, TextKeyData.serializer()) - subclass(AutoTextKeyData::class, AutoTextKeyData.serializer()) - subclass(MultiTextKeyData::class, MultiTextKeyData.serializer()) - subclass(CaseSelector::class, CaseSelector.serializer()) - subclass(ShiftStateSelector::class, ShiftStateSelector.serializer()) - subclass(VariationSelector::class, VariationSelector.serializer()) - subclass(LayoutDirectionSelector::class, LayoutDirectionSelector.serializer()) - subclass(CharWidthSelector::class, CharWidthSelector.serializer()) - subclass(KanaSelector::class, KanaSelector.serializer()) - defaultDeserializer { TextKeyData.serializer() } - } - polymorphic(KeyData::class) { - subclass(TextKeyData::class, TextKeyData.serializer()) - subclass(AutoTextKeyData::class, AutoTextKeyData.serializer()) - subclass(MultiTextKeyData::class, MultiTextKeyData.serializer()) - defaultDeserializer { TextKeyData.serializer() } - } - } -} diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index 51062d742..5b9d15809 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -24,19 +24,16 @@ import helium314.keyboard.latin.common.isEmoji import helium314.keyboard.latin.define.DebugFlags import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.spellcheck.AndroidSpellCheckerService -import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.InputTypeUtils import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT import helium314.keyboard.latin.utils.POPUP_KEYS_NUMBER import helium314.keyboard.latin.utils.ScriptUtils import helium314.keyboard.latin.utils.ScriptUtils.script -import helium314.keyboard.latin.utils.getLayoutFile import helium314.keyboard.latin.utils.removeFirst import helium314.keyboard.latin.utils.replaceFirst import helium314.keyboard.latin.utils.runInLocale import helium314.keyboard.latin.utils.splitAt import helium314.keyboard.latin.utils.sumOf -import java.io.File /** * Abstract parser class that handles creation of keyboard from [KeyData] arranged in rows, @@ -47,7 +44,7 @@ import java.io.File * By default, all normal keys have the same width and flags, which may cause issues with the * requirements of certain non-latin languages. */ -abstract class KeyboardParser(private val params: KeyboardParams, private val context: Context) { +class KeyboardParser(private val params: KeyboardParams, private val context: Context) { private val infos = layoutInfos(params) private val defaultLabelFlags = when { params.mId.isAlphabetKeyboard -> params.mLocaleKeyboardInfos.labelFlags @@ -57,10 +54,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co else -> 0 } - abstract fun parseCoreLayout(layoutContent: String): MutableList> - - // this thing does too much... make it more understandable after everything is implemented - fun parseLayoutString(layoutContent: String): ArrayList> { + fun parseLayout(): ArrayList> { params.readAttributes(context, null) params.mProximityCharsCorrectionEnabled = infos.enableProximityCharsCorrection if (infos.touchPositionCorrectionData == null) // need to set correctly, as it's not properly done in readAttributes with attr = null @@ -68,7 +62,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co else params.mTouchPositionCorrection.load(context.resources.getStringArray(infos.touchPositionCorrectionData)) - val baseKeys = parseCoreLayout(layoutContent) + val baseKeys = RawKeyboardParser.parseLayout(params, context) val keysInRows = createRows(baseKeys) // rescale height if we have anything but the usual 4 rows val heightRescale = if (keysInRows.size != 4) 4f / keysInRows.size else 1f @@ -79,26 +73,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co return keysInRows } - // this should be ready for customizable functional layouts, but needs cleanup - // todo (later): remove this as part of adding a cache for parsed layouts - private fun getFunctionalKeyLayoutText(): String { - if (params.mId.isNumberLayout) return "[]" // empty list - val layouts = Settings.getLayoutsDir(context).list() ?: emptyArray() - if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { - if ("functional_keys_symbols_shifted.json" in layouts) - return getLayoutFile("functional_keys_symbols_shifted.json", context).readText() - } - if (!params.mId.isAlphabetKeyboard) { - if ("functional_keys_symbols.json" in layouts) - return getLayoutFile("functional_keys_symbols.json", context).readText() - } - if ("functional_keys.json" in layouts) - return getLayoutFile("functional_keys.json", context).readText() - val fileName = if (Settings.getInstance().isTablet) "functional_keys_tablet.json" else "functional_keys.json" - return context.readAssetsLayoutFile(fileName) - } - - private fun createRows(baseKeys: MutableList>): ArrayList> { + private fun createRows(baseKeys: MutableList>): ArrayList> { // add padding for number layouts in landscape mode (maybe do it some other way later) if (params.mId.isNumberLayout && params.mId.mElementId != KeyboardId.ELEMENT_NUMPAD && context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { @@ -112,14 +87,14 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co addSymbolPopupKeys(baseKeys) if (params.mId.isAlphaOrSymbolKeyboard && params.mId.mNumberRowEnabled) baseKeys.add(0, params.mLocaleKeyboardInfos.getNumberRow() - .map { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) }) + .mapTo(mutableListOf()) { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) }) - val allFunctionalKeys = JsonKeyboardParser(params, context).parseCoreLayout(getFunctionalKeyLayoutText()) + val allFunctionalKeys = RawKeyboardParser.parseLayout(params, context, true) adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys, baseKeys) if (allFunctionalKeys.none { it.singleOrNull()?.isKeyPlaceholder() == true }) // add a placeholder so splitAt does what we really want - allFunctionalKeys.add(0, listOf(TextKeyData(type = KeyType.PLACEHOLDER))) + allFunctionalKeys.add(0, mutableListOf(TextKeyData(type = KeyType.PLACEHOLDER))) val (functionalKeysTop, functionalKeysBottom) = allFunctionalKeys.splitAt { it.singleOrNull()?.isKeyPlaceholder() == true } @@ -235,8 +210,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co * does nothing if not isAlphaOrSymbolKeyboard or assumptions not met * adds an empty row to baseKeys, to have a baseKey row for the bottom functional row */ - private fun adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys: MutableList>, baseKeys: MutableList>) { - val functionalKeysBottom = allFunctionalKeys.lastOrNull()?.toMutableList() ?: return + private fun adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys: MutableList>, baseKeys: MutableList>) { + val functionalKeysBottom = allFunctionalKeys.lastOrNull() ?: return if (!params.mId.isAlphaOrSymbolKeyboard || functionalKeysBottom.isEmpty() || functionalKeysBottom.any { it.isKeyPlaceholder() }) return if (true /* Settings.getInstance().current.mSingleFunctionalLayout */) { // todo with the customizable functional layout @@ -287,8 +262,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co functionalKeysBottom.add(spaceIndex, key1) } } - allFunctionalKeys[allFunctionalKeys.lastIndex] = functionalKeysBottom - baseKeys.add(emptyList()) + baseKeys.add(mutableListOf()) } // ideally we would get all functional keys in a nice list of pairs from the start, but at least it works... @@ -331,12 +305,12 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } } - private fun addNumberRowOrPopupKeys(baseKeys: MutableList>) { + private fun addNumberRowOrPopupKeys(baseKeys: MutableList>) { if (!params.mId.mNumberRowEnabled && params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) { // replace first symbols row with number row, but use the labels as popupKeys val numberRow = params.mLocaleKeyboardInfos.getNumberRow() numberRow.forEachIndexed { index, keyData -> keyData.popup.symbol = baseKeys[0].getOrNull(index)?.label } - baseKeys[0] = numberRow + baseKeys[0] = numberRow.toMutableList() } else if (!params.mId.mNumberRowEnabled && params.mId.isAlphabetKeyboard && infos.numbersOnTopRow) { if (baseKeys[0].any { it.popup.main != null || !it.popup.relevant.isNullOrEmpty() } // first row of baseKeys has any layout popup key && params.mPopupKeyLabelSources.let { @@ -356,15 +330,9 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } } - private fun addSymbolPopupKeys(baseKeys: MutableList>) { - val layoutName = getLayoutFileName(params, context, overrideElementId = KeyboardId.ELEMENT_SYMBOLS) - val layout = if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) { - val parser = if (layoutName.endsWith("json")) JsonKeyboardParser(params, context) - else SimpleKeyboardParser(params, context, false) - parser.parseCoreLayout(getLayoutFile(layoutName, context).readText()) - } else { - SimpleKeyboardParser(params, context, false).parseCoreLayout(context.readAssetsLayoutFile("$layoutName.txt")) - } + private fun addSymbolPopupKeys(baseKeys: MutableList>) { + val layoutName = if (params.mId.locale.script() == ScriptUtils.SCRIPT_ARABIC) LAYOUT_SYMBOLS_ARABIC else LAYOUT_SYMBOLS + val layout = RawKeyboardParser.parseLayout(layoutName, params, context) layout.forEachIndexed { i, row -> val baseRow = baseKeys.getOrNull(i) ?: return@forEachIndexed row.forEachIndexed { j, key -> @@ -492,49 +460,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co companion object { private const val TAG = "KeyboardParser" - // todo: this is somewhat awkward and could be re-organized - // simple and json parser should just parse the core layout - // adding extra keys should be done in KeyboardParser - fun parseLayout(params: KeyboardParams, context: Context): ArrayList> { - val layoutName = getLayoutFileName(params, context) - if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) { - val parser = if (layoutName.endsWith("json")) JsonKeyboardParser(params, context) - else SimpleKeyboardParser(params, context) - return parser.parseLayoutString(getLayoutFile(layoutName, context).readText()) - } - val layoutFileNames = context.assets.list("layouts")!! - if (layoutFileNames.contains("$layoutName.json")) { - return JsonKeyboardParser(params, context).parseLayoutString(context.readAssetsLayoutFile("$layoutName.json")) - } - if (layoutFileNames.contains("$layoutName.txt")) { - return SimpleKeyboardParser(params, context).parseLayoutString(context.readAssetsLayoutFile("$layoutName.txt")) - } - throw IllegalStateException("can't parse layout $layoutName with id ${params.mId} and elementId ${params.mId.mElementId}") - } - - private fun Context.readAssetsLayoutFile(name: String) = assets.open("layouts${File.separator}$name").reader().readText() - - private fun getLayoutFileName(params: KeyboardParams, context: Context, overrideElementId: Int? = null): String { - var checkForCustom = true - val layoutName = when (overrideElementId ?: params.mId.mElementId) { - KeyboardId.ELEMENT_SYMBOLS -> if (params.mId.locale.script() == ScriptUtils.SCRIPT_ARABIC) LAYOUT_SYMBOLS_ARABIC else LAYOUT_SYMBOLS - KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> LAYOUT_SYMBOLS_SHIFTED - KeyboardId.ELEMENT_NUMPAD -> if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) - LAYOUT_NUMPAD_LANDSCAPE - else - LAYOUT_NUMPAD - KeyboardId.ELEMENT_NUMBER -> LAYOUT_NUMBER - KeyboardId.ELEMENT_PHONE -> LAYOUT_PHONE - KeyboardId.ELEMENT_PHONE_SYMBOLS -> LAYOUT_PHONE_SYMBOLS - else -> { - checkForCustom = false // "custom" is already in keyboardLayoutSetName - params.mId.mSubtype.keyboardLayoutSetName.substringBeforeLast("+") - } - } - return if (checkForCustom) Settings.readLayoutName(layoutName, context) - else layoutName - } - // todo: // layoutInfos should be stored in method.xml (imeSubtypeExtraValue) // or somewhere else... some replacement for keyboard_layout_set xml maybe @@ -619,3 +544,7 @@ const val LAYOUT_NUMPAD_LANDSCAPE = "numpad_landscape" const val LAYOUT_NUMBER = "number" const val LAYOUT_PHONE = "phone" const val LAYOUT_PHONE_SYMBOLS = "phone_symbols" +const val FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED = "functional_keys_symbols_shifted" +const val FUNCTIONAL_LAYOUT_SYMBOLS = "functional_keys_symbols" +const val FUNCTIONAL_LAYOUT = "functional_keys" +const val FUNCTIONAL_LAYOUT_TABLET = "functional_keys_tablet" diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt new file mode 100644 index 000000000..3053c488d --- /dev/null +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-3.0-only +package helium314.keyboard.keyboard.internal.keyboard_parser + +import android.content.Context +import android.content.res.Configuration +import helium314.keyboard.keyboard.KeyboardId +import helium314.keyboard.keyboard.internal.KeyboardParams +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AbstractKeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AutoTextKeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CaseSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CharWidthSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KanaSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.LayoutDirectionSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.MultiTextKeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.ShiftStateSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.VariationSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.toTextKey +import helium314.keyboard.latin.common.splitOnWhitespace +import helium314.keyboard.latin.settings.Settings +import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX +import helium314.keyboard.latin.utils.ScriptUtils +import helium314.keyboard.latin.utils.ScriptUtils.script +import helium314.keyboard.latin.utils.getCustomLayoutFile +import helium314.keyboard.latin.utils.getCustomLayoutsDir +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import java.io.File + +object RawKeyboardParser { + private val rawLayoutCache = hashMapOf MutableList>>() + + val symbolAndNumberLayouts = listOf(LAYOUT_SYMBOLS, LAYOUT_SYMBOLS_SHIFTED, LAYOUT_SYMBOLS_ARABIC, + LAYOUT_NUMBER, LAYOUT_NUMPAD, LAYOUT_NUMPAD_LANDSCAPE, LAYOUT_PHONE, LAYOUT_PHONE_SYMBOLS) + + // todo: cache is by layout name, this is inefficient for functional keys by default + fun clearCache() = rawLayoutCache.clear() + + fun parseLayout(params: KeyboardParams, context: Context, isFunctional: Boolean = false): MutableList> { + val layoutName = if (isFunctional) { + if (!params.mId.isAlphaOrSymbolKeyboard) return mutableListOf(mutableListOf()) + else getFunctionalLayoutName(params) + } else { + getLayoutName(params, context) + } + return rawLayoutCache.getOrPut(layoutName) { + createCacheLambda(layoutName, context) + }(params) + } + + fun parseLayout(layoutName: String, params: KeyboardParams, context: Context): MutableList> { + return rawLayoutCache.getOrPut(layoutName) { + createCacheLambda(layoutName, context) + }(params) + } + + /** + * Parse for json layout files as used in FlorisBoard, see floris directory for classes taken from FlorisBoard. + * Some differences to the FlorisBoard keys: + * (currently) only normal keys supported + * if label or code are missing one is created from the other + * auto_text_key ignored (i.e. interpreted like the default TextKey) + * codes of multi_text_key not used, only the label + * (currently) popups is always read to [number, main, relevant] layoutPopupKeys, no choice of which to use or which hint is provided + */ + fun parseJsonString(layoutText: String): List> = florisJsonConfig.decodeFromString(layoutText) + + /** Parse simple layouts, defined only as rows of (normal) keys with popup keys. */ + fun parseSimpleString(layoutText: String): List> { + val rowStrings = layoutText.replace("\r\n", "\n").split("\\n\\s*\\n".toRegex()).filter { it.isNotBlank() } + return rowStrings.map { row -> + row.split("\n").mapNotNull { parseKey(it) } + } + } + + private fun parseKey(key: String): KeyData? { + if (key.isBlank()) return null + val split = key.splitOnWhitespace() + return if (split.size == 1) split.first().toTextKey() + else split.first().toTextKey(split.drop(1)) + } + + private fun createCacheLambda(layoutName: String, context: Context): (KeyboardParams) -> MutableList> { + val layoutFileName = getLayoutFileName(layoutName, context) + val layoutText = if (layoutFileName.startsWith(CUSTOM_LAYOUT_PREFIX)) getCustomLayoutFile(layoutFileName, context).readText() + else context.assets.open("layouts${File.separator}$layoutFileName").reader().use { it.readText() } + if (layoutFileName.endsWith(".json")) { + val florisKeyData = parseJsonString(layoutText) + return { params -> + florisKeyData.mapTo(mutableListOf()) { row -> + row.mapNotNullTo(mutableListOf()) { it.compute(params) } + } + } + } else { + val simpleKeyData = parseSimpleString(layoutText) + return { params -> + simpleKeyData.mapIndexedTo(mutableListOf()) { i, row -> + val newRow = row.toMutableList() + val extraKeys = params.mId.mSubtype.keyboardLayoutSetName.endsWith("+") && params.mId.isAlphabetKeyboard + if (extraKeys) + params.mLocaleKeyboardInfos.getExtraKeys(i+1)?.let { newRow.addAll(it) } + println("${newRow.size}: ${newRow.map { it.label }}") + newRow + } + } + } + } + + private fun getLayoutName(params: KeyboardParams, context: Context) = when (params.mId.mElementId) { + KeyboardId.ELEMENT_SYMBOLS -> if (params.mId.locale.script() == ScriptUtils.SCRIPT_ARABIC) LAYOUT_SYMBOLS_ARABIC else LAYOUT_SYMBOLS + KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> LAYOUT_SYMBOLS_SHIFTED + KeyboardId.ELEMENT_NUMPAD -> if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) + LAYOUT_NUMPAD_LANDSCAPE + else + LAYOUT_NUMPAD + KeyboardId.ELEMENT_NUMBER -> LAYOUT_NUMBER + KeyboardId.ELEMENT_PHONE -> LAYOUT_PHONE + KeyboardId.ELEMENT_PHONE_SYMBOLS -> LAYOUT_PHONE_SYMBOLS + else -> params.mId.mSubtype.keyboardLayoutSetName.substringBeforeLast("+") + } + + private fun getFunctionalLayoutName(params: KeyboardParams) = when (params.mId.mElementId) { + KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED + KeyboardId.ELEMENT_SYMBOLS -> FUNCTIONAL_LAYOUT_SYMBOLS + else -> if (Settings.getInstance().isTablet) FUNCTIONAL_LAYOUT_TABLET else FUNCTIONAL_LAYOUT + } + + /** returns the file name matching the layout name, making sure the file exists (falling back to qwerty.txt) */ + private fun getLayoutFileName(layoutName: String, context: Context): String { + val customFiles = getCustomLayoutsDir(context).list() + if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) { + return if (customFiles?.contains(layoutName) == true) layoutName + else "qwerty.txt" // fallback + } + val assetsFiles by lazy { context.assets.list("layouts")!! } + if (layoutName.startsWith("functional")) { + // return custom match if we have one + val customMatch = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") } + if (customMatch != null) return customMatch + if (layoutName == FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) { + // no custom symbols shifted layout, try custom symbols layout + val customSymbols = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT_SYMBOLS.") } + if (customSymbols != null) return customSymbols + } + // no custom symbols layout, try custom functional layout + if (Settings.getInstance().isTablet) { + val customTablet = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT_TABLET.") } + if (customTablet != null) return customTablet + } + val customFunctional = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT.") } + if (customFunctional != null) return customFunctional + // no custom functional layout, use the default functional layout + return if (Settings.getInstance().isTablet) "$FUNCTIONAL_LAYOUT_TABLET.json" + else "$FUNCTIONAL_LAYOUT.json" + } + return if (layoutName in symbolAndNumberLayouts) { + customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.")} + ?: assetsFiles.first { it.startsWith(layoutName) } + } else { + // can't be custom layout, so it must be in assets + val searchName = layoutName.substringBeforeLast("+") // consider there are layouts ending in "+" for adding extra keys + assetsFiles.firstOrNull { it.startsWith(searchName) } ?: "qwerty.txt" // in case it was removed + } + } + + /* + * Copyright (C) 2021 Patrick Goldinger + * modified + * SPDX-License-Identifier: Apache-2.0 + */ + private val florisJsonConfig = Json { + classDiscriminator = "$" + encodeDefaults = true + ignoreUnknownKeys = true + isLenient = true + serializersModule = SerializersModule { + polymorphic(AbstractKeyData::class) { + subclass(TextKeyData::class, TextKeyData.serializer()) + subclass(AutoTextKeyData::class, AutoTextKeyData.serializer()) + subclass(MultiTextKeyData::class, MultiTextKeyData.serializer()) + subclass(CaseSelector::class, CaseSelector.serializer()) + subclass(ShiftStateSelector::class, ShiftStateSelector.serializer()) + subclass(VariationSelector::class, VariationSelector.serializer()) + subclass(LayoutDirectionSelector::class, LayoutDirectionSelector.serializer()) + subclass(CharWidthSelector::class, CharWidthSelector.serializer()) + subclass(KanaSelector::class, KanaSelector.serializer()) + defaultDeserializer { TextKeyData.serializer() } + } + polymorphic(KeyData::class) { + subclass(TextKeyData::class, TextKeyData.serializer()) + subclass(AutoTextKeyData::class, AutoTextKeyData.serializer()) + subclass(MultiTextKeyData::class, MultiTextKeyData.serializer()) + defaultDeserializer { TextKeyData.serializer() } + } + } + } +} diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/SimpleKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/SimpleKeyboardParser.kt deleted file mode 100644 index cb730b1c0..000000000 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/SimpleKeyboardParser.kt +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -package helium314.keyboard.keyboard.internal.keyboard_parser - -import android.content.Context -import helium314.keyboard.keyboard.internal.KeyboardParams -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.toTextKey -import helium314.keyboard.latin.common.splitOnWhitespace - -/** - * Parser for simple layouts, defined only as rows of (normal) keys with popup keys. - * There may be a short "extra row" for the configurable keys in the bottom row. This is two keys - * for alphabet, 3 keys for symbols and 4 keys for shift symbols. Popup keys on period and comma get - * merged with defaults. - */ -class SimpleKeyboardParser( - private val params: KeyboardParams, - context: Context, - private val addExtraKeys: Boolean = params.mId.mSubtype.keyboardLayoutSetName.endsWith("+") && params.mId.isAlphabetKeyboard -) : KeyboardParser(params, context) { - override fun parseCoreLayout(layoutContent: String): MutableList> { - val rowStrings = layoutContent.replace("\r\n", "\n").split("\\n\\s*\\n".toRegex()) - return rowStrings.mapIndexedNotNullTo(mutableListOf()) { i, row -> - if (row.isBlank()) return@mapIndexedNotNullTo null - if (addExtraKeys) - getExtraKeys(i)?.let { parseRow(row) + it } ?: parseRow(row) - else - parseRow(row) - } - } - - private fun parseRow(row: String): List = - row.split("\n").mapNotNull { - if (it.isBlank()) null - else parseKey(it) - } - - private fun getExtraKeys(rowIndex: Int) = params.mLocaleKeyboardInfos.getExtraKeys(rowIndex + 1) - - private fun parseKey(key: String): KeyData { - val split = key.splitOnWhitespace() - return if (split.size == 1) - split.first().toTextKey() - else - split.first().toTextKey(split.drop(1)) - } - -} diff --git a/app/src/main/java/helium314/keyboard/latin/App.kt b/app/src/main/java/helium314/keyboard/latin/App.kt index 951f76387..a97a168a4 100644 --- a/app/src/main/java/helium314/keyboard/latin/App.kt +++ b/app/src/main/java/helium314/keyboard/latin/App.kt @@ -11,6 +11,7 @@ import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.DictionaryInfoUtils +import helium314.keyboard.latin.utils.getCustomLayoutsDir import helium314.keyboard.latin.utils.upgradeToolbarPrefs import java.io.File @@ -50,7 +51,7 @@ fun checkVersionUpgrade(context: Context) { if (oldVersion == 0) // new install or restoring settings from old app name upgradesWhenComingFromOldAppName(context) if (oldVersion <= 1000) { // upgrade old custom layouts name - val layoutsDir = Settings.getLayoutsDir(context) + val layoutsDir = getCustomLayoutsDir(context) val oldShiftSymbolsFile = File(layoutsDir, "${CUSTOM_LAYOUT_PREFIX}shift_symbols") if (oldShiftSymbolsFile.exists()) { oldShiftSymbolsFile.renameTo(File(layoutsDir, "${CUSTOM_LAYOUT_PREFIX}symbols_shifted")) @@ -79,7 +80,7 @@ fun checkVersionUpgrade(context: Context) { private fun upgradesWhenComingFromOldAppName(context: Context) { // move layout files try { - val layoutsDir = Settings.getLayoutsDir(context) + val layoutsDir = getCustomLayoutsDir(context) File(context.filesDir, "layouts").listFiles()?.forEach { it.copyTo(File(layoutsDir, it.name), true) it.delete() diff --git a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt index 9176b9272..740af6f4c 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt @@ -32,6 +32,7 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_PHONE_SYMBOLS import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS_ARABIC import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS_SHIFTED +import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser import helium314.keyboard.latin.AudioAndHapticFeedbackManager import helium314.keyboard.latin.BuildConfig import helium314.keyboard.latin.R @@ -46,6 +47,7 @@ import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.ExecutorUtils import helium314.keyboard.latin.utils.JniUtils import helium314.keyboard.latin.utils.editCustomLayout +import helium314.keyboard.latin.utils.getCustomLayoutsDir import helium314.keyboard.latin.utils.getStringResourceOrName import helium314.keyboard.latin.utils.infoDialog import helium314.keyboard.latin.utils.reloadEnabledSubtypes @@ -157,27 +159,27 @@ class AdvancedSettingsFragment : SubScreenFragment() { } private fun showCustomizeLayoutsDialog() { - val layouts = listOf(LAYOUT_SYMBOLS, LAYOUT_SYMBOLS_SHIFTED, LAYOUT_SYMBOLS_ARABIC, LAYOUT_NUMBER, LAYOUT_NUMPAD, LAYOUT_NUMPAD_LANDSCAPE, LAYOUT_PHONE, LAYOUT_PHONE_SYMBOLS) - val layoutNames = layouts.map { it.getStringResourceOrName("layout_", requireContext()) }.toTypedArray() + val layoutNames = RawKeyboardParser.symbolAndNumberLayouts.map { it.getStringResourceOrName("layout_", requireContext()) }.toTypedArray() AlertDialog.Builder(requireContext()) .setTitle(R.string.customize_symbols_number_layouts) .setItems(layoutNames) { di, i -> di.dismiss() - customizeLayout(layouts[i]) + customizeSymbolNumberLayout(RawKeyboardParser.symbolAndNumberLayouts[i]) } .setNegativeButton(android.R.string.cancel, null) .show() } - private fun customizeLayout(layout: String) { - val customLayoutName = Settings.readLayoutName(layout, context).takeIf { it.startsWith(CUSTOM_LAYOUT_PREFIX) } + private fun customizeSymbolNumberLayout(layoutName: String) { + val customLayoutName = getCustomLayoutsDir(requireContext()).list() + ?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") } val originalLayout = if (customLayoutName != null) null else { - requireContext().assets.list("layouts")?.firstOrNull { it.startsWith("$layout.") } + requireContext().assets.list("layouts")?.firstOrNull { it.startsWith("$layoutName.") } ?.let { requireContext().assets.open("layouts" + File.separator + it).reader().readText() } } - val displayName = layout.getStringResourceOrName("layout_", requireContext()) - editCustomLayout(customLayoutName ?: "$CUSTOM_LAYOUT_PREFIX$layout.txt", requireContext(), originalLayout, displayName) + val displayName = layoutName.getStringResourceOrName("layout_", requireContext()) + editCustomLayout(customLayoutName ?: "$CUSTOM_LAYOUT_PREFIX$layoutName.txt", requireContext(), originalLayout, displayName) } @SuppressLint("ApplySharedPref") diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index 5d19431b8..9f7b2f97a 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -36,7 +36,6 @@ import helium314.keyboard.latin.common.LocaleUtils; import helium314.keyboard.latin.utils.AdditionalSubtypeUtils; import helium314.keyboard.latin.utils.ColorUtilKt; -import helium314.keyboard.latin.utils.CustomLayoutUtilsKt; import helium314.keyboard.latin.utils.DeviceProtectedUtils; import helium314.keyboard.latin.utils.JniUtils; import helium314.keyboard.latin.utils.Log; @@ -548,22 +547,6 @@ public static int readMorePopupKeysPref(final SharedPreferences prefs) { }; } - /** @return custom layout name if there is one for the given layout, else returns "layout" */ - public static String readLayoutName(final String layout, final Context context) { - String[] layouts = getLayoutsDir(context).list(); - if (layouts != null) { - for (String name : layouts) { - if (name.startsWith(CustomLayoutUtilsKt.CUSTOM_LAYOUT_PREFIX + layout + ".")) - return name; - } - } - return layout; - } - - public static File getLayoutsDir(final Context context) { - return new File(DeviceProtectedUtils.getFilesDir(context), "layouts"); - } - @Nullable public static Drawable readUserBackgroundImage(final Context context, final boolean night) { if (night && sCachedBackgroundNight != null) return sCachedBackgroundNight; if (!night && sCachedBackgroundDay != null) return sCachedBackgroundDay; diff --git a/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt index 84479b637..05283a1f0 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt @@ -13,9 +13,8 @@ import helium314.keyboard.keyboard.KeyboardId import helium314.keyboard.keyboard.KeyboardLayoutSet import helium314.keyboard.keyboard.KeyboardSwitcher import helium314.keyboard.keyboard.internal.KeyboardParams -import helium314.keyboard.keyboard.internal.keyboard_parser.JsonKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL -import helium314.keyboard.keyboard.internal.keyboard_parser.SimpleKeyboardParser +import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams import helium314.keyboard.latin.R import helium314.keyboard.latin.common.FileUtils @@ -65,7 +64,7 @@ fun loadCustomLayout(layoutContent: String, layoutName: String, languageTag: Str .setPositiveButton(android.R.string.ok) { _, _ -> // name must be encoded to avoid issues with validity of subtype extra string or file name name = "$CUSTOM_LAYOUT_PREFIX${languageTag}.${encodeBase36(name)}.${if (isJson) "json" else "txt"}" - val file = getLayoutFile(name, context) + val file = getCustomLayoutFile(name, context) if (file.exists()) file.delete() file.parentFile?.mkdir() @@ -81,21 +80,21 @@ private fun checkLayout(layoutContent: String, context: Context): Boolean? { params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT) addLocaleKeyTextsToParams(context, params, POPUP_KEYS_NORMAL) try { - val keys = JsonKeyboardParser(params, context).parseLayoutString(layoutContent) + val keys = RawKeyboardParser.parseJsonString(layoutContent).map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } if (!checkKeys(keys)) return null return true } catch (e: Exception) { Log.w(TAG, "error parsing custom json layout", e) } try { - val keys = SimpleKeyboardParser(params, context).parseLayoutString(layoutContent) + val keys = RawKeyboardParser.parseSimpleString(layoutContent).map { it.map { it.toKeyParams(params) } } if (!checkKeys(keys)) return null return false } catch (e: Exception) { Log.w(TAG, "error parsing custom simple layout", e) } if (layoutContent.startsWith("[")) { - // layout can't be loaded, assume it's json -> try json layout again because of error message readout + // layout can't be loaded, assume it's json -> load json layout again because the error message shown to the user is from the most recent error try { - JsonKeyboardParser(params, context).parseLayoutString(layoutContent) + RawKeyboardParser.parseJsonString(layoutContent).map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } } catch (e: Exception) { Log.w(TAG, "error parsing custom json layout", e) } } return null @@ -129,8 +128,10 @@ private fun checkKeys(keys: List>): Boolean { return true } -fun getLayoutFile(layoutName: String, context: Context) = - File(Settings.getLayoutsDir(context), layoutName) +fun getCustomLayoutFile(layoutName: String, context: Context) = + File(getCustomLayoutsDir(context), layoutName) + +fun getCustomLayoutsDir(context: Context) = File(DeviceProtectedUtils.getFilesDir(context), "layouts") // undo the name changes in loadCustomLayout when clicking ok fun getLayoutDisplayName(layoutName: String) = @@ -141,11 +142,11 @@ fun getLayoutDisplayName(layoutName: String) = } fun removeCustomLayoutFile(layoutName: String, context: Context) { - getLayoutFile(layoutName, context).delete() + getCustomLayoutFile(layoutName, context).delete() } fun editCustomLayout(layoutName: String, context: Context, startContent: String? = null, displayName: CharSequence? = null) { - val file = getLayoutFile(layoutName, context) + val file = getCustomLayoutFile(layoutName, context) val editText = EditText(context).apply { setText(startContent ?: file.readText()) } diff --git a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt index 7ed43ac0d..a57f1a4e3 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt @@ -245,7 +245,7 @@ private fun loadResourceSubtypes(resources: Resources) { private fun removeInvalidCustomSubtypes(context: Context) { val prefs = DeviceProtectedUtils.getSharedPreferences(context) val additionalSubtypes = Settings.readPrefAdditionalSubtypes(prefs, context.resources).split(";") - val customSubtypeFiles by lazy { Settings.getLayoutsDir(context).list() } + val customSubtypeFiles by lazy { getCustomLayoutsDir(context).list() } val subtypesToRemove = mutableListOf() additionalSubtypes.forEach { val name = it.substringAfter(":").substringBefore(":") diff --git a/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt b/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt index dec6dd8cd..4715ee577 100644 --- a/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt +++ b/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt @@ -12,13 +12,10 @@ import helium314.keyboard.keyboard.internal.KeyboardBuilder import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.TouchPositionCorrection import helium314.keyboard.keyboard.internal.UniqueKeysCache -import helium314.keyboard.keyboard.internal.keyboard_parser.JsonKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL -import helium314.keyboard.keyboard.internal.keyboard_parser.SimpleKeyboardParser +import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyType -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.MultiTextKeyData import helium314.keyboard.latin.LatinIME import helium314.keyboard.latin.RichInputMethodSubtype import helium314.keyboard.latin.utils.AdditionalSubtypeUtils.createEmojiCapableAdditionalSubtype @@ -120,7 +117,7 @@ f""", // no newline at the end val wantedKeyLabels = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) layoutStrings.forEachIndexed { i, layout -> println(i) - val keyLabels = SimpleKeyboardParser(params, latinIME).parseCoreLayout(layout).map { it.map { it.label } } + val keyLabels = RawKeyboardParser.parseSimpleString(layout).map { it.map { it.toKeyParams(params).mLabel } } assertEquals(wantedKeyLabels, keyLabels) } } @@ -268,7 +265,7 @@ f""", // no newline at the end ] ] """.trimIndent() - val keys = JsonKeyboardParser(params, latinIME).parseCoreLayout(layoutString) + val keys = RawKeyboardParser.parseJsonString(layoutString).map { it.mapNotNull { it.compute(params) } } keys.first().forEachIndexed { index, keyData -> println("data: key ${keyData.label}: code ${keyData.code}, popups: ${keyData.popup.getPopupKeyLabels(params)}") val keyParams = keyData.toKeyParams(params) @@ -298,13 +295,13 @@ f""", // no newline at the end val editorInfo = EditorInfo() val subtype = createEmojiCapableAdditionalSubtype(Locale.GERMANY, "qwertz+", true) val (kb, keys) = buildKeyboard(editorInfo, subtype, KeyboardId.ELEMENT_ALPHABET) - assertEquals(keys[0].size, 11) - assertEquals(keys[1].size, 11) - assertEquals(keys[2].size, 10) + assertEquals(11, keys[0].size) + assertEquals(11, keys[1].size) + assertEquals(10, keys[2].size) val (kb2, keys2) = buildKeyboard(editorInfo, subtype, KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) - assertEquals(keys2[0].size, 11) - assertEquals(keys2[1].size, 11) - assertEquals(keys2[2].size, 10) + assertEquals(11, keys2[0].size) + assertEquals(11, keys2[1].size) + assertEquals(10, keys2[2].size) } @Test fun `popup key count does not depend on shift for (for simple layout)`() { From 71ddc200412a3b328840be027eb3481561821ae4 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Tue, 14 May 2024 23:40:56 +0200 Subject: [PATCH 14/58] don't show input method pick on long pressing a custom space key with popups --- .../main/java/helium314/keyboard/keyboard/PointerTracker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java index 36260fd7a..4baed1054 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java @@ -1098,7 +1098,7 @@ public void onLongPressed() { } final int code = key.getCode(); if (code == KeyCode.LANGUAGE_SWITCH - || (code == Constants.CODE_SPACE && Settings.getInstance().getCurrent().mSpaceForLangChange) + || (code == Constants.CODE_SPACE && key.getPopupKeys() == null && Settings.getInstance().getCurrent().mSpaceForLangChange) ) { // Long pressing the space key invokes IME switcher dialog. if (sListener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) { From f825436449dccf62105d62e3a22588f78d7d45f9 Mon Sep 17 00:00:00 2001 From: tenextractor <139619642+tenextractor@users.noreply.github.com> Date: Wed, 15 May 2024 23:26:20 +0530 Subject: [PATCH 15/58] Add Hungarian (Extended) layout (#774) --- .../layouts/hungarian_extended_qwertz.txt | 39 +++++++++++++++++++ app/src/main/res/xml/method.xml | 10 +++++ 2 files changed, 49 insertions(+) create mode 100644 app/src/main/assets/layouts/hungarian_extended_qwertz.txt diff --git a/app/src/main/assets/layouts/hungarian_extended_qwertz.txt b/app/src/main/assets/layouts/hungarian_extended_qwertz.txt new file mode 100644 index 000000000..d38e22ef8 --- /dev/null +++ b/app/src/main/assets/layouts/hungarian_extended_qwertz.txt @@ -0,0 +1,39 @@ +á +é +í +ó +ö +ő +ú +ü +ű +' + +q +w +e +r +t +z +u +i +o +p + +a +s +d +f +g +h +j +k +l + +y +x +c +v +b +n +m \ No newline at end of file diff --git a/app/src/main/res/xml/method.xml b/app/src/main/res/xml/method.xml index 738198c9a..c3c1c96c5 100644 --- a/app/src/main/res/xml/method.xml +++ b/app/src/main/res/xml/method.xml @@ -51,6 +51,7 @@ hi_ZZ: Hinglish/qwerty # This is a preliminary keyboard layout. hr: Croatian/qwertz hu: Hungarian/qwertz + hu: Hungarian/hungarian_qwertz hu_HU: Hungarian/qwerty hy_AM: Armenian (Armenia) Phonetic/armenian_phonetic in: Indonesian/qwerty # "id" is the official language code of Indonesian. @@ -501,6 +502,15 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwertz,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" android:isAsciiCapable="true" /> + Date: Wed, 15 May 2024 22:47:24 +0200 Subject: [PATCH 16/58] upgrade build.gradle and do some code inspection stuff --- app/build.gradle | 12 +- .../accessibility/AccessibilityUtils.kt | 1 - .../KeyboardAccessibilityDelegate.kt | 3 +- .../MainKeyboardAccessibilityDelegate.kt | 3 +- .../helium314/keyboard/event/CombinerChain.kt | 17 +-- .../keyboard/event/InputTransaction.kt | 2 +- .../java/helium314/keyboard/keyboard/Key.java | 7 +- .../keyboard/KeyboardActionListener.java | 10 +- .../keyboard/KeyboardActionListenerImpl.kt | 6 +- .../clipboard/ClipboardHistoryView.kt | 3 +- .../keyboard/emoji/DynamicGridKeyboard.java | 11 +- .../keyboard_parser/RawKeyboardParser.kt | 1 + .../latin/AudioAndHapticFeedbackManager.java | 23 ++-- .../keyboard/latin/ContactsManager.java | 27 ++-- .../keyboard/latin/InputAttributes.java | 119 +++++++----------- .../helium314/keyboard/latin/LatinIME.java | 20 ++- .../latin/PunctuationSuggestions.java | 8 +- .../keyboard/latin/WordComposer.java | 9 +- .../keyboard/latin/common/LocaleUtils.kt | 2 +- .../latin/inputlogic/InputLogicHandler.java | 25 ++-- .../keyboard/latin/makedict/WordProperty.java | 12 +- .../permissions/PermissionsActivity.java | 6 +- .../settings/AdvancedSettingsFragment.kt | 5 +- .../latin/setup/SetupStartIndicatorView.java | 5 +- .../latin/setup/SetupStepIndicatorView.java | 10 +- .../latin/suggestions/MoreSuggestions.java | 17 +-- .../suggestions/SuggestionStripView.java | 18 +-- .../keyboard/latin/utils/CustomLayoutUtils.kt | 15 ++- .../latin/utils/RecapitalizeStatus.java | 37 ++---- .../helium314/keyboard/KeySpecParserTest.kt | 1 - .../keyboard/latin/InputLogicTest.kt | 4 +- .../helium314/keyboard/latin/SuggestTest.kt | 1 + build.gradle | 4 +- 33 files changed, 190 insertions(+), 254 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d4855caa8..1df406d0d 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -90,14 +90,22 @@ android { } dependencies { - implementation 'androidx.core:core-ktx:1.12.0' + // androidx + implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.preference:preference:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.3.2' + implementation 'androidx.autofill:autofill:1.1.0' + + // kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3" + // when kotlin-serialization is enabled, Android Studio stops complaining about "Unresolved reference: serializer", but the build fails +// implementation "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + + // color picker for user-defined colors implementation 'com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0' - implementation 'androidx.autofill:autofill:1.1.0' + // test testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.11.0' testImplementation 'org.mockito:mockito-inline:5.2.0' diff --git a/app/src/main/java/helium314/keyboard/accessibility/AccessibilityUtils.kt b/app/src/main/java/helium314/keyboard/accessibility/AccessibilityUtils.kt index b5d7adb15..dafbfb2bf 100644 --- a/app/src/main/java/helium314/keyboard/accessibility/AccessibilityUtils.kt +++ b/app/src/main/java/helium314/keyboard/accessibility/AccessibilityUtils.kt @@ -7,7 +7,6 @@ package helium314.keyboard.accessibility import android.content.Context -import android.media.AudioDeviceInfo import android.media.AudioDeviceInfo.* import android.media.AudioManager import android.os.Build diff --git a/app/src/main/java/helium314/keyboard/accessibility/KeyboardAccessibilityDelegate.kt b/app/src/main/java/helium314/keyboard/accessibility/KeyboardAccessibilityDelegate.kt index 74c96e923..17edf65d1 100644 --- a/app/src/main/java/helium314/keyboard/accessibility/KeyboardAccessibilityDelegate.kt +++ b/app/src/main/java/helium314/keyboard/accessibility/KeyboardAccessibilityDelegate.kt @@ -47,10 +47,9 @@ open class KeyboardAccessibilityDelegate( /** * Called when the keyboard layout changes. * - * * **Note:** This method will be called even if accessibility is not * enabled. - * @param keyboard The keyboard that is being set to the wrapping view. + * [keyboard]: The keyboard that is being set to the wrapping view. */ open var keyboard: Keyboard? get() = mKeyboard diff --git a/app/src/main/java/helium314/keyboard/accessibility/MainKeyboardAccessibilityDelegate.kt b/app/src/main/java/helium314/keyboard/accessibility/MainKeyboardAccessibilityDelegate.kt index d81bd4ecf..0be99c7c0 100644 --- a/app/src/main/java/helium314/keyboard/accessibility/MainKeyboardAccessibilityDelegate.kt +++ b/app/src/main/java/helium314/keyboard/accessibility/MainKeyboardAccessibilityDelegate.kt @@ -116,8 +116,7 @@ class MainKeyboardAccessibilityDelegate( */ private fun announceKeyboardType(keyboard: Keyboard, lastKeyboard: Keyboard) { val lastElementId = lastKeyboard.mId.mElementId - val resId: Int - resId = when (keyboard.mId.mElementId) { + val resId = when (keyboard.mId.mElementId) { KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, KeyboardId.ELEMENT_ALPHABET -> { if (lastElementId == KeyboardId.ELEMENT_ALPHABET || lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { diff --git a/app/src/main/java/helium314/keyboard/event/CombinerChain.kt b/app/src/main/java/helium314/keyboard/event/CombinerChain.kt index 7862f9ee2..4aa1a1fde 100644 --- a/app/src/main/java/helium314/keyboard/event/CombinerChain.kt +++ b/app/src/main/java/helium314/keyboard/event/CombinerChain.kt @@ -9,7 +9,6 @@ package helium314.keyboard.event import android.text.SpannableStringBuilder import android.text.TextUtils import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode -import helium314.keyboard.latin.common.Constants import java.util.* /** @@ -23,6 +22,12 @@ import java.util.* * finished combining part, will be shown normally as the composing string, while the second is * feedback on the composing state and will typically be shown with different styling such as * a colored background. + * + * The combiner chain takes events as inputs and outputs code points and combining state. + * For example, if the input language is Japanese, the combining chain will typically perform + * kana conversion. This takes a string for initial text, taken to be present before the + * cursor: we'll start after this. + * @param initialText The text that has already been combined so far. */ class CombinerChain(initialText: String) { // The already combined text, as described above @@ -45,16 +50,6 @@ class CombinerChain(initialText: String) { mCombiners.add(HangulCombiner()) } - /** - * Create an combiner chain. - * - * The combiner chain takes events as inputs and outputs code points and combining state. - * For example, if the input language is Japanese, the combining chain will typically perform - * kana conversion. This takes a string for initial text, taken to be present before the - * cursor: we'll start after this. - * - * @param initialText The text that has already been combined so far. - */ init { // The dead key combiner is always active, and always first mCombiners.add(DeadKeyCombiner()) diff --git a/app/src/main/java/helium314/keyboard/event/InputTransaction.kt b/app/src/main/java/helium314/keyboard/event/InputTransaction.kt index c753a739f..b7dbd26dc 100644 --- a/app/src/main/java/helium314/keyboard/event/InputTransaction.kt +++ b/app/src/main/java/helium314/keyboard/event/InputTransaction.kt @@ -28,7 +28,7 @@ class InputTransaction(// Initial conditions * @param updateType What type of shift update this requires. */ fun requireShiftUpdate(updateType: Int) { - requiredShiftUpdate = Math.max(requiredShiftUpdate, updateType) + requiredShiftUpdate = requiredShiftUpdate.coerceAtLeast(updateType) } /** diff --git a/app/src/main/java/helium314/keyboard/keyboard/Key.java b/app/src/main/java/helium314/keyboard/keyboard/Key.java index 231dd05ad..7368b27bc 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/Key.java +++ b/app/src/main/java/helium314/keyboard/keyboard/Key.java @@ -191,7 +191,7 @@ public static OptionalAttributes newInstance(final String outputText, final int /** * Constructor for a key on PopupKeyKeyboard and on MoreSuggestions. */ - public Key(@Nullable final String label, final String iconName, final int code, + public Key(@Nullable final String label, @NonNull final String iconName, final int code, @Nullable final String outputText, @Nullable final String hintLabel, final int labelFlags, final int backgroundType, final int x, final int y, final int width, final int height, final int horizontalGap, final int verticalGap) { @@ -404,7 +404,7 @@ private boolean equalsInternal(final Key o) { && o.mCode == mCode && TextUtils.equals(o.mLabel, mLabel) && TextUtils.equals(o.mHintLabel, mHintLabel) - && o.mIconName == mIconName + && o.mIconName.equals(mIconName) && o.mBackgroundType == mBackgroundType && Arrays.equals(o.mPopupKeys, mPopupKeys) && TextUtils.equals(o.getOutputText(), getOutputText()) @@ -429,6 +429,7 @@ public boolean equals(final Object o) { return o instanceof Key && equalsInternal((Key)o); } + @NonNull @Override public String toString() { return toShortString() + " " + getX() + "," + getY() + " " + getWidth() + "x" + getHeight(); @@ -654,7 +655,7 @@ public final boolean hasCustomActionLabel() { return (mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0; } - private final boolean isShiftedLetterActivated() { + private boolean isShiftedLetterActivated() { return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0 && !TextUtils.isEmpty(mHintLabel); } diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java index cd1763e11..0e3c6c5cb 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java @@ -35,11 +35,11 @@ public interface KeyboardActionListener { * Send a key code to the listener. * * @param primaryCode this is the code of the key that was pressed - * @param x x-coordinate pixel of touched event. If {@link #onCodeInput} is not called by + * @param x x-coordinate pixel of touched event. If onCodeInput is not called by * {@link PointerTracker} or so, the value should be * {@link Constants#NOT_A_COORDINATE}. If it's called on insertion from the * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}. - * @param y y-coordinate pixel of touched event. If {@link #onCodeInput} is not called by + * @param y y-coordinate pixel of touched event. If #onCodeInput is not called by * {@link PointerTracker} or so, the value should be * {@link Constants#NOT_A_COORDINATE}.If it's called on insertion from the * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}. @@ -103,9 +103,9 @@ public interface KeyboardActionListener { KeyboardActionListener EMPTY_LISTENER = new Adapter(); - static int SWIPE_NO_ACTION = 0; - static int SWIPE_MOVE_CURSOR = 1; - static int SWIPE_SWITCH_LANGUAGE = 2; + int SWIPE_NO_ACTION = 0; + int SWIPE_MOVE_CURSOR = 1; + int SWIPE_SWITCH_LANGUAGE = 2; class Adapter implements KeyboardActionListener { @Override diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt index c878a26b0..30c047dbe 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt @@ -97,9 +97,9 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp return true } - private fun onMoveCursorHorizontally(_steps: Int): Boolean { - if (_steps == 0) return false - var steps = _steps + private fun onMoveCursorHorizontally(rawSteps: Int): Boolean { + if (rawSteps == 0) return false + var steps = rawSteps // for RTL languages we want to invert pointer movement if (RichInputMethodManager.getInstance().currentSubtype.isRtlSubtype) steps = -steps val moveSteps: Int diff --git a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt index 2f169242c..6624fb26d 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt @@ -32,6 +32,7 @@ import helium314.keyboard.latin.utils.getCodeForToolbarKey import helium314.keyboard.latin.utils.getCodeForToolbarKeyLongClick import helium314.keyboard.latin.utils.getEnabledClipboardToolbarKeys +@SuppressLint("CustomViewStyleable") class ClipboardHistoryView @JvmOverloads constructor( context: Context, attrs: AttributeSet?, @@ -160,7 +161,7 @@ class ClipboardHistoryView @JvmOverloads constructor( private fun setupToolbarKeys() { // set layout params - val toolbarKeyLayoutParams = LayoutParams(getResources().getDimensionPixelSize(R.dimen.config_suggestions_strip_edge_key_width), LayoutParams.MATCH_PARENT) + val toolbarKeyLayoutParams = LayoutParams(resources.getDimensionPixelSize(R.dimen.config_suggestions_strip_edge_key_width), LayoutParams.MATCH_PARENT) toolbarKeys.forEach { it.layoutParams = toolbarKeyLayoutParams } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java index e51168b8f..39c3f0d65 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java +++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java @@ -236,8 +236,7 @@ public void loadRecentKeys(final Collection keyboards) { if (o instanceof Integer) { final int code = (Integer)o; key = getKeyByCode(keyboards, code); - } else if (o instanceof String) { - final String outputText = (String)o; + } else if (o instanceof final String outputText) { key = getKeyByOutputText(keyboards, outputText); } else { Log.w(TAG, "Invalid object: " + o); @@ -267,18 +266,20 @@ private int getKeyY1(final int index) { return row * mVerticalStep + mVerticalGap / 2; } + @NonNull @Override public List getSortedKeys() { synchronized (mLock) { if (mCachedGridKeys != null) { return mCachedGridKeys; } - final ArrayList cachedKeys = new ArrayList(mGridKeys); + final ArrayList cachedKeys = new ArrayList<>(mGridKeys); mCachedGridKeys = Collections.unmodifiableList(cachedKeys); return mCachedGridKeys; } } + @NonNull @Override public List getNearestKeys(final int x, final int y) { // TODO: Calculate the nearest key index in mGridKeys from x and y. @@ -312,13 +313,13 @@ public int getY() { @Override public boolean equals(final Object o) { - if (!(o instanceof Key)) return false; - final Key key = (Key)o; + if (!(o instanceof final Key key)) return false; if (getCode() != key.getCode()) return false; if (!TextUtils.equals(getLabel(), key.getLabel())) return false; return TextUtils.equals(getOutputText(), key.getOutputText()); } + @NonNull @Override public String toString() { return "GridKey: " + super.toString(); diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt index 3053c488d..f108a5666 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt @@ -121,6 +121,7 @@ object RawKeyboardParser { else -> params.mId.mSubtype.keyboardLayoutSetName.substringBeforeLast("+") } + // todo (later, see also keyboardParser): use Settings.getInstance().current.mSingleFunctionalLayout private fun getFunctionalLayoutName(params: KeyboardParams) = when (params.mId.mElementId) { KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED KeyboardId.ELEMENT_SYMBOLS -> FUNCTIONAL_LAYOUT_SYMBOLS diff --git a/app/src/main/java/helium314/keyboard/latin/AudioAndHapticFeedbackManager.java b/app/src/main/java/helium314/keyboard/latin/AudioAndHapticFeedbackManager.java index 873d080f0..0fcb4f340 100644 --- a/app/src/main/java/helium314/keyboard/latin/AudioAndHapticFeedbackManager.java +++ b/app/src/main/java/helium314/keyboard/latin/AudioAndHapticFeedbackManager.java @@ -18,7 +18,7 @@ /** * This class gathers audio feedback and haptic feedback functions. - * + *

* It offers a consistent and simple interface that allows LatinIME to forget about the * complexity of settings and the like. */ @@ -81,21 +81,12 @@ public void performAudioFeedback(final int code) { if (!mSoundOn) { return; } - final int sound; - switch (code) { - case KeyCode.DELETE: - sound = AudioManager.FX_KEYPRESS_DELETE; - break; - case Constants.CODE_ENTER: - sound = AudioManager.FX_KEYPRESS_RETURN; - break; - case Constants.CODE_SPACE: - sound = AudioManager.FX_KEYPRESS_SPACEBAR; - break; - default: - sound = AudioManager.FX_KEYPRESS_STANDARD; - break; - } + final int sound = switch (code) { + case KeyCode.DELETE -> AudioManager.FX_KEYPRESS_DELETE; + case Constants.CODE_ENTER -> AudioManager.FX_KEYPRESS_RETURN; + case Constants.CODE_SPACE -> AudioManager.FX_KEYPRESS_SPACEBAR; + default -> AudioManager.FX_KEYPRESS_STANDARD; + }; mAudioManager.playSoundEffect(sound, mSettingsValues.mKeypressSoundVolume); } diff --git a/app/src/main/java/helium314/keyboard/latin/ContactsManager.java b/app/src/main/java/helium314/keyboard/latin/ContactsManager.java index 877bcdf50..bb171a2a1 100644 --- a/app/src/main/java/helium314/keyboard/latin/ContactsManager.java +++ b/app/src/main/java/helium314/keyboard/latin/ContactsManager.java @@ -25,7 +25,7 @@ /** * Manages all interactions with Contacts DB. - * + *

* The manager provides an API for listening to meaning full updates by keeping a * measure of the current state of the content provider. */ @@ -65,7 +65,7 @@ float getAffinity() { * - How many times it has been contacted * - How long since the last contact. * - Whether the contact is in the visible group (i.e., Contacts list). - * + *

* Note: This affinity is limited by the fact that some apps currently do not update the * LAST_TIME_CONTACTED or TIMES_CONTACTED counters. As a result, a frequently messaged * contact may still have 0 affinity. @@ -101,13 +101,13 @@ public interface ContactsChangedListener { * The number of contacts observed in the most recent instance of * contacts content provider. */ - private AtomicInteger mContactCountAtLastRebuild = new AtomicInteger(0); + private final AtomicInteger mContactCountAtLastRebuild = new AtomicInteger(0); /** * The hash code of list of valid contacts names in the most recent dictionary * rebuild. */ - private AtomicInteger mHashCodeAtLastRebuild = new AtomicInteger(0); + private final AtomicInteger mHashCodeAtLastRebuild = new AtomicInteger(0); private final Context mContext; private final ContactsContentObserver mObserver; @@ -134,7 +134,7 @@ public int getHashCodeAtLastRebuild() { * Returns all the valid names in the Contacts DB. Callers should also * call {@link #updateLocalState(ArrayList)} after they are done with result * so that the manager can cache local state for determining updates. - * + *

* These names are sorted by their affinity to the user, with favorite * contacts appearing first. */ @@ -183,22 +183,15 @@ public ArrayList getValidNames(final Uri uri) { * Returns the number of contacts in contacts content provider. */ public int getContactCount() { - // TODO: consider switching to a rawQuery("select count(*)...") on the database if - // performance is a bottleneck. - Cursor cursor = null; - try { - cursor = mContext.getContentResolver().query(Contacts.CONTENT_URI, - ContactsDictionaryConstants.PROJECTION_ID_ONLY, null, null, null); - if (null == cursor) { + // TODO: consider switching to a rawQuery("select count(*)...") on the database if performance is a bottleneck. + try (Cursor cursor = mContext.getContentResolver().query(Contacts.CONTENT_URI, + ContactsDictionaryConstants.PROJECTION_ID_ONLY, null, null, null) + ) { + if (null == cursor) return 0; - } return cursor.getCount(); } catch (final SQLiteException e) { Log.e(TAG, "SQLiteException in the remote Contacts process.", e); - } finally { - if (null != cursor) { - cursor.close(); - } } return 0; } diff --git a/app/src/main/java/helium314/keyboard/latin/InputAttributes.java b/app/src/main/java/helium314/keyboard/latin/InputAttributes.java index c57fec006..92c75991b 100644 --- a/app/src/main/java/helium314/keyboard/latin/InputAttributes.java +++ b/app/src/main/java/helium314/keyboard/latin/InputAttributes.java @@ -20,6 +20,8 @@ import static helium314.keyboard.latin.common.Constants.ImeOption.NO_FLOATING_GESTURE_PREVIEW; import static helium314.keyboard.latin.common.Constants.ImeOption.NO_MICROPHONE; +import androidx.annotation.NonNull; + /** * Class to hold attributes of the input field. */ @@ -162,92 +164,60 @@ private void dumpFlags(final int inputType) { } private static String toInputClassString(final int inputClass) { - switch (inputClass) { - case InputType.TYPE_CLASS_TEXT: - return "TYPE_CLASS_TEXT"; - case InputType.TYPE_CLASS_PHONE: - return "TYPE_CLASS_PHONE"; - case InputType.TYPE_CLASS_NUMBER: - return "TYPE_CLASS_NUMBER"; - case InputType.TYPE_CLASS_DATETIME: - return "TYPE_CLASS_DATETIME"; - default: - return String.format("unknownInputClass<0x%08x>", inputClass); - } + return switch (inputClass) { + case InputType.TYPE_CLASS_TEXT -> "TYPE_CLASS_TEXT"; + case InputType.TYPE_CLASS_PHONE -> "TYPE_CLASS_PHONE"; + case InputType.TYPE_CLASS_NUMBER -> "TYPE_CLASS_NUMBER"; + case InputType.TYPE_CLASS_DATETIME -> "TYPE_CLASS_DATETIME"; + default -> String.format("unknownInputClass<0x%08x>", inputClass); + }; } private static String toVariationString(final int inputClass, final int variation) { - switch (inputClass) { - case InputType.TYPE_CLASS_TEXT: - return toTextVariationString(variation); - case InputType.TYPE_CLASS_NUMBER: - return toNumberVariationString(variation); - case InputType.TYPE_CLASS_DATETIME: - return toDatetimeVariationString(variation); - default: - return ""; - } + return switch (inputClass) { + case InputType.TYPE_CLASS_TEXT -> toTextVariationString(variation); + case InputType.TYPE_CLASS_NUMBER -> toNumberVariationString(variation); + case InputType.TYPE_CLASS_DATETIME -> toDatetimeVariationString(variation); + default -> ""; + }; } private static String toTextVariationString(final int variation) { - switch (variation) { - case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS: - return " TYPE_TEXT_VARIATION_EMAIL_ADDRESS"; - case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT: - return "TYPE_TEXT_VARIATION_EMAIL_SUBJECT"; - case InputType.TYPE_TEXT_VARIATION_FILTER: - return "TYPE_TEXT_VARIATION_FILTER"; - case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE: - return "TYPE_TEXT_VARIATION_LONG_MESSAGE"; - case InputType.TYPE_TEXT_VARIATION_NORMAL: - return "TYPE_TEXT_VARIATION_NORMAL"; - case InputType.TYPE_TEXT_VARIATION_PASSWORD: - return "TYPE_TEXT_VARIATION_PASSWORD"; - case InputType.TYPE_TEXT_VARIATION_PERSON_NAME: - return "TYPE_TEXT_VARIATION_PERSON_NAME"; - case InputType.TYPE_TEXT_VARIATION_PHONETIC: - return "TYPE_TEXT_VARIATION_PHONETIC"; - case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS: - return "TYPE_TEXT_VARIATION_POSTAL_ADDRESS"; - case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE: - return "TYPE_TEXT_VARIATION_SHORT_MESSAGE"; - case InputType.TYPE_TEXT_VARIATION_URI: - return "TYPE_TEXT_VARIATION_URI"; - case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD: - return "TYPE_TEXT_VARIATION_VISIBLE_PASSWORD"; - case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT: - return "TYPE_TEXT_VARIATION_WEB_EDIT_TEXT"; - case InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS: - return "TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS"; - case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD: - return "TYPE_TEXT_VARIATION_WEB_PASSWORD"; - default: - return String.format("unknownVariation<0x%08x>", variation); - } + return switch (variation) { + case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS -> " TYPE_TEXT_VARIATION_EMAIL_ADDRESS"; + case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT -> "TYPE_TEXT_VARIATION_EMAIL_SUBJECT"; + case InputType.TYPE_TEXT_VARIATION_FILTER -> "TYPE_TEXT_VARIATION_FILTER"; + case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE -> "TYPE_TEXT_VARIATION_LONG_MESSAGE"; + case InputType.TYPE_TEXT_VARIATION_NORMAL -> "TYPE_TEXT_VARIATION_NORMAL"; + case InputType.TYPE_TEXT_VARIATION_PASSWORD -> "TYPE_TEXT_VARIATION_PASSWORD"; + case InputType.TYPE_TEXT_VARIATION_PERSON_NAME -> "TYPE_TEXT_VARIATION_PERSON_NAME"; + case InputType.TYPE_TEXT_VARIATION_PHONETIC -> "TYPE_TEXT_VARIATION_PHONETIC"; + case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS -> "TYPE_TEXT_VARIATION_POSTAL_ADDRESS"; + case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE -> "TYPE_TEXT_VARIATION_SHORT_MESSAGE"; + case InputType.TYPE_TEXT_VARIATION_URI -> "TYPE_TEXT_VARIATION_URI"; + case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD -> "TYPE_TEXT_VARIATION_VISIBLE_PASSWORD"; + case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT -> "TYPE_TEXT_VARIATION_WEB_EDIT_TEXT"; + case InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS -> "TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS"; + case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD -> "TYPE_TEXT_VARIATION_WEB_PASSWORD"; + default -> String.format("unknownVariation<0x%08x>", variation); + }; } private static String toNumberVariationString(final int variation) { - switch (variation) { - case InputType.TYPE_NUMBER_VARIATION_NORMAL: - return "TYPE_NUMBER_VARIATION_NORMAL"; - case InputType.TYPE_NUMBER_VARIATION_PASSWORD: - return "TYPE_NUMBER_VARIATION_PASSWORD"; - default: - return String.format("unknownVariation<0x%08x>", variation); - } + return switch (variation) { + case InputType.TYPE_NUMBER_VARIATION_NORMAL -> "TYPE_NUMBER_VARIATION_NORMAL"; + case InputType.TYPE_NUMBER_VARIATION_PASSWORD -> "TYPE_NUMBER_VARIATION_PASSWORD"; + default -> String.format("unknownVariation<0x%08x>", variation); + }; } private static String toDatetimeVariationString(final int variation) { - switch (variation) { - case InputType.TYPE_DATETIME_VARIATION_NORMAL: - return "TYPE_DATETIME_VARIATION_NORMAL"; - case InputType.TYPE_DATETIME_VARIATION_DATE: - return "TYPE_DATETIME_VARIATION_DATE"; - case InputType.TYPE_DATETIME_VARIATION_TIME: - return "TYPE_DATETIME_VARIATION_TIME"; - default: - return String.format("unknownVariation<0x%08x>", variation); - } + return switch (variation) { + case InputType.TYPE_DATETIME_VARIATION_NORMAL -> "TYPE_DATETIME_VARIATION_NORMAL"; + case InputType.TYPE_DATETIME_VARIATION_DATE -> "TYPE_DATETIME_VARIATION_DATE"; + case InputType.TYPE_DATETIME_VARIATION_TIME -> "TYPE_DATETIME_VARIATION_TIME"; + default -> String.format("unknownVariation<0x%08x>", variation); + }; } private static String toFlagsString(final int flags) { @@ -272,6 +242,7 @@ private static String toFlagsString(final int flags) { } // Pretty print + @NonNull @Override public String toString() { return String.format( diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java index 4a845a125..0e9bf6e26 100644 --- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java +++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java @@ -6,6 +6,7 @@ package helium314.keyboard.latin; +import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -783,6 +784,7 @@ public void onInitializeInterface() { * Starts from {@link android.os.Build.VERSION_CODES#S_V2}, the returning context object has * became to IME context self since it ends up capable of updating its resources internally. */ + @SuppressWarnings("deprecation") private @NonNull Context getDisplayContext() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S_V2) { // IME context sources is now managed by WindowProviderService from Android 12L. @@ -1104,10 +1106,6 @@ public void onUpdateSelection(final int oldSelStart, final int oldSelEnd, } } - public CharSequence getSelection() { - return mInputLogic.mConnection.getSelectedText(0); - } - /** * This is called when the user has clicked on the extracted text view, * when running in fullscreen mode. The default implementation hides @@ -1667,14 +1665,11 @@ private void loadKeyboard() { */ private void updateStateAfterInputTransaction(final InputTransaction inputTransaction) { switch (inputTransaction.getRequiredShiftUpdate()) { - case InputTransaction.SHIFT_UPDATE_LATER: - mHandler.postUpdateShiftState(); - break; - case InputTransaction.SHIFT_UPDATE_NOW: - mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), - getCurrentRecapitalizeState()); - break; - default: // SHIFT_NO_UPDATE + case InputTransaction.SHIFT_UPDATE_LATER -> mHandler.postUpdateShiftState(); + case InputTransaction.SHIFT_UPDATE_NOW -> mKeyboardSwitcher + .requestUpdatingShiftState(getCurrentAutoCapsState(), getCurrentRecapitalizeState()); + default -> { + } // SHIFT_NO_UPDATE } if (inputTransaction.requiresUpdateSuggestions()) { final int inputStyle; @@ -1897,6 +1892,7 @@ private void workaroundForHuaweiStatusBarIssue() { } } + @SuppressLint("SwitchIntDef") @Override public void onTrimMemory(int level) { super.onTrimMemory(level); diff --git a/app/src/main/java/helium314/keyboard/latin/PunctuationSuggestions.java b/app/src/main/java/helium314/keyboard/latin/PunctuationSuggestions.java index 241af9a5e..6f6e75413 100644 --- a/app/src/main/java/helium314/keyboard/latin/PunctuationSuggestions.java +++ b/app/src/main/java/helium314/keyboard/latin/PunctuationSuggestions.java @@ -6,11 +6,11 @@ package helium314.keyboard.latin; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import helium314.keyboard.keyboard.internal.KeySpecParser; import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode; -import helium314.keyboard.latin.common.Constants; import helium314.keyboard.latin.common.StringUtils; import java.util.ArrayList; @@ -18,7 +18,7 @@ /** * The extended {@link SuggestedWords} class to represent punctuation suggestions. - * + *

* Each punctuation specification string is the key specification that can be parsed by * {@link KeySpecParser}. */ @@ -44,7 +44,7 @@ private PunctuationSuggestions(final ArrayList punctuationsLi public static PunctuationSuggestions newPunctuationSuggestions( @Nullable final String[] punctuationSpecs) { if (punctuationSpecs == null || punctuationSpecs.length == 0) { - return new PunctuationSuggestions(new ArrayList(0)); + return new PunctuationSuggestions(new ArrayList<>(0)); } final ArrayList punctuationList = new ArrayList<>(punctuationSpecs.length); @@ -99,7 +99,7 @@ public boolean isPunctuationSuggestions() { } @Override - public String toString() { + @NonNull public String toString() { return "PunctuationSuggestions: " + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray()); } diff --git a/app/src/main/java/helium314/keyboard/latin/WordComposer.java b/app/src/main/java/helium314/keyboard/latin/WordComposer.java index 343dc6d8e..d67f50d7e 100644 --- a/app/src/main/java/helium314/keyboard/latin/WordComposer.java +++ b/app/src/main/java/helium314/keyboard/latin/WordComposer.java @@ -15,7 +15,6 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode; import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo; import helium314.keyboard.latin.common.ComposedData; -import helium314.keyboard.latin.common.Constants; import helium314.keyboard.latin.common.CoordinateUtils; import helium314.keyboard.latin.common.InputPointers; import helium314.keyboard.latin.common.StringUtils; @@ -167,7 +166,7 @@ public Event processEvent(@NonNull final Event event) { /** * Apply a processed input event. - * + *

* All input events should be supported, including software/hardware events, characters as well * as deletions, multiple inputs and gestures. * @@ -320,7 +319,7 @@ public String getTypedWord() { /** * Whether this composer is composing or about to compose a word in which only the first letter * is a capital. - * + *

* If we do have a composing word, we just return whether the word has indeed only its first * character capitalized. If we don't, then we return a value based on the capitalized mode, * which tell us what is likely to happen for the next composing word. @@ -370,7 +369,7 @@ public boolean hasDigits() { /** * Saves the caps mode at the start of composing. - * + *

* WordComposer needs to know about the caps mode for several reasons. The first is, we need * to know after the fact what the reason was, to register the correct form into the user * history dictionary: if the word was automatically capitalized, we should insert it in @@ -385,7 +384,7 @@ public void setCapitalizedModeAtStartComposingTime(final int mode) { /** * Before fetching suggestions, we don't necessarily know about the capitalized mode yet. - * + *

* If we don't have a composing word yet, we take a note of this mode so that we can then * supply this information to the suggestion process. If we have a composing word, then * the previous mode has priority over this. diff --git a/app/src/main/java/helium314/keyboard/latin/common/LocaleUtils.kt b/app/src/main/java/helium314/keyboard/latin/common/LocaleUtils.kt index 766308182..fa2d81c78 100644 --- a/app/src/main/java/helium314/keyboard/latin/common/LocaleUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/common/LocaleUtils.kt @@ -58,7 +58,7 @@ object LocaleUtils { // The compared locales are fully identical. This is the best match level. private const val LOCALE_FULL_MATCH = 30 - const val LOCALE_GOOD_MATCH = LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; + const val LOCALE_GOOD_MATCH = LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER /** * Return how well a tested locale matches a reference locale. diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogicHandler.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogicHandler.java index f75f01854..99b77fa3e 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogicHandler.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogicHandler.java @@ -82,12 +82,8 @@ public void destroy() { // Called on the Non-UI handler thread by the Handler code. @Override public boolean handleMessage(final Message msg) { - switch (msg.what) { - case MSG_GET_SUGGESTED_WORDS: - mLatinIME.getSuggestedWords(msg.arg1 /* inputStyle */, - msg.arg2 /* sequenceNumber */, (OnGetSuggestedWordsCallback) msg.obj); - break; - } + if (msg.what == MSG_GET_SUGGESTED_WORDS) + mLatinIME.getSuggestedWords(msg.arg1, msg.arg2, (OnGetSuggestedWordsCallback) msg.obj); return true; } @@ -122,12 +118,7 @@ private void updateBatchInput(final InputPointers batchPointers, return; } mInputLogic.mWordComposer.setBatchInputPointers(batchPointers); - final OnGetSuggestedWordsCallback callback = new OnGetSuggestedWordsCallback() { - @Override - public void onGetSuggestedWords(final SuggestedWords suggestedWords) { - showGestureSuggestionsWithPreviewVisuals(suggestedWords, isTailBatchInput); - } - }; + final OnGetSuggestedWordsCallback callback = suggestedWords -> showGestureSuggestionsWithPreviewVisuals(suggestedWords, isTailBatchInput); getSuggestedWords(isTailBatchInput ? SuggestedWords.INPUT_STYLE_TAIL_BATCH : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber, callback); } @@ -159,7 +150,7 @@ void showGestureSuggestionsWithPreviewVisuals(final SuggestedWords suggestedWord /** * Update a batch input. - * + *

* This fetches suggestions and updates the suggestion strip and the floating text preview. * * @param batchPointers the updated batch pointers. @@ -168,12 +159,12 @@ void showGestureSuggestionsWithPreviewVisuals(final SuggestedWords suggestedWord // Called on the UI thread by InputLogic. public void onUpdateBatchInput(final InputPointers batchPointers, final int sequenceNumber) { - updateBatchInput(batchPointers, sequenceNumber, false /* isTailBatchInput */); + updateBatchInput(batchPointers, sequenceNumber, false); } /** * Cancel a batch input. - * + *

* Note that as opposed to updateTailBatchInput, we do the UI side of this immediately on the * same thread, rather than get this to call a method in LatinIME. This is because * canceling a batch input does not necessitate the long operation of pulling suggestions. @@ -187,7 +178,7 @@ public void onCancelBatchInput() { /** * Trigger an update for a tail batch input. - * + *

* A tail batch input is the last update for a gesture, the one that is triggered after the * user lifts their finger. This method schedules fetching suggestions on the non-UI thread, * then when the suggestions are computed it comes back on the UI thread to update the @@ -199,7 +190,7 @@ public void onCancelBatchInput() { // Called on the UI thread by InputLogic. public void updateTailBatchInput(final InputPointers batchPointers, final int sequenceNumber) { - updateBatchInput(batchPointers, sequenceNumber, true /* isTailBatchInput */); + updateBatchInput(batchPointers, sequenceNumber, true); } public void getSuggestedWords(final int inputStyle, final int sequenceNumber, diff --git a/app/src/main/java/helium314/keyboard/latin/makedict/WordProperty.java b/app/src/main/java/helium314/keyboard/latin/makedict/WordProperty.java index 5910d74c0..2fcdfa617 100644 --- a/app/src/main/java/helium314/keyboard/latin/makedict/WordProperty.java +++ b/app/src/main/java/helium314/keyboard/latin/makedict/WordProperty.java @@ -6,6 +6,7 @@ package helium314.keyboard.latin.makedict; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.inputmethod.latin.BinaryDictionary; @@ -20,7 +21,7 @@ /** * Utility class for a word with a probability. - * + *

* This is chiefly used to iterate a dictionary. */ public final class WordProperty implements Comparable { @@ -150,7 +151,7 @@ private static int computeHashCode(WordProperty word) { /** * Three-way comparison. - * + *

* A Word x is greater than a word y if x has a higher frequency. If they have the same * frequency, they are sorted in lexicographic order. */ @@ -163,15 +164,14 @@ public int compareTo(final WordProperty w) { /** * Equality test. - * + *

* Words are equal if they have the same frequency, the same spellings, and the same * attributes. */ @Override public boolean equals(Object o) { if (o == this) return true; - if (!(o instanceof WordProperty)) return false; - WordProperty w = (WordProperty)o; + if (!(o instanceof WordProperty w)) return false; return mProbabilityInfo.equals(w.mProbabilityInfo) && mWord.equals(w.mWord) && mShortcutTargets.equals(w.mShortcutTargets) && equals(mNgrams, w.mNgrams) && mIsNotAWord == w.mIsNotAWord && mIsPossiblyOffensive == w.mIsPossiblyOffensive @@ -199,7 +199,7 @@ public boolean isValid() { } @Override - public String toString() { + @NonNull public String toString() { return CombinedFormatUtils.formatWordProperty(this); } } diff --git a/app/src/main/java/helium314/keyboard/latin/permissions/PermissionsActivity.java b/app/src/main/java/helium314/keyboard/latin/permissions/PermissionsActivity.java index a3c20ee10..1d190e9a5 100644 --- a/app/src/main/java/helium314/keyboard/latin/permissions/PermissionsActivity.java +++ b/app/src/main/java/helium314/keyboard/latin/permissions/PermissionsActivity.java @@ -57,7 +57,7 @@ protected void onCreate(Bundle savedInstanceState) { } @Override - protected void onSaveInstanceState(Bundle outState) { + protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(EXTRA_PERMISSION_REQUEST_CODE, mPendingRequestCode); } @@ -68,8 +68,8 @@ protected void onResume() { // Only do request when there is no pending request to avoid duplicated requests. if (mPendingRequestCode == INVALID_REQUEST_CODE) { final Bundle extras = getIntent().getExtras(); - final String[] permissionsToRequest = - extras.getStringArray(EXTRA_PERMISSION_REQUESTED_PERMISSIONS); + if (extras == null) return; + final String[] permissionsToRequest = extras.getStringArray(EXTRA_PERMISSION_REQUESTED_PERMISSIONS); mPendingRequestCode = extras.getInt(EXTRA_PERMISSION_REQUEST_CODE); // Assuming that all supplied permissions are not granted yet, so that we don't need to // check them again. diff --git a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt index 740af6f4c..937441df1 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt @@ -73,6 +73,7 @@ import java.util.zip.ZipOutputStream * - Improve keyboard * - Debug settings */ +@Suppress("KotlinConstantConditions") // build type might be a constant, but depends on... build type! class AdvancedSettingsFragment : SubScreenFragment() { private val libfile by lazy { File(requireContext().filesDir.absolutePath + File.separator + JniUtils.JNI_LIB_IMPORT_FILE_NAME) } private val backupFilePatterns by lazy { listOf( @@ -436,8 +437,8 @@ class AdvancedSettingsFragment : SubScreenFragment() { } checkVersionUpgrade(requireContext()) Settings.getInstance().startListener() - val additionalSubtypes = Settings.readPrefAdditionalSubtypes(sharedPreferences, resources); - updateAdditionalSubtypes(AdditionalSubtypeUtils.createAdditionalSubtypesArray(additionalSubtypes)); + val additionalSubtypes = Settings.readPrefAdditionalSubtypes(sharedPreferences, resources) + updateAdditionalSubtypes(AdditionalSubtypeUtils.createAdditionalSubtypesArray(additionalSubtypes)) reloadEnabledSubtypes(requireContext()) val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION) activity?.sendBroadcast(newDictBroadcast) diff --git a/app/src/main/java/helium314/keyboard/latin/setup/SetupStartIndicatorView.java b/app/src/main/java/helium314/keyboard/latin/setup/SetupStartIndicatorView.java index 004fbdd02..bab9415d8 100644 --- a/app/src/main/java/helium314/keyboard/latin/setup/SetupStartIndicatorView.java +++ b/app/src/main/java/helium314/keyboard/latin/setup/SetupStartIndicatorView.java @@ -21,7 +21,6 @@ import androidx.annotation.NonNull; import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.AppCompatTextView; -import androidx.core.view.ViewCompat; public final class SetupStartIndicatorView extends LinearLayout { public SetupStartIndicatorView(final Context context, final AttributeSet attrs) { @@ -72,13 +71,13 @@ public IndicatorView(final Context context, final AttributeSet attrs) { @Override protected void onDraw(@NonNull final Canvas canvas) { super.onDraw(canvas); - final int layoutDirection = ViewCompat.getLayoutDirection(this); + final int layoutDirection = getLayoutDirection(); final int width = getWidth(); final int height = getHeight(); final float halfHeight = height / 2.0f; final Path path = mIndicatorPath; path.rewind(); - if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) { + if (layoutDirection == View.LAYOUT_DIRECTION_RTL) { // Left arrow path.moveTo(width, 0.0f); path.lineTo(0.0f, halfHeight); diff --git a/app/src/main/java/helium314/keyboard/latin/setup/SetupStepIndicatorView.java b/app/src/main/java/helium314/keyboard/latin/setup/SetupStepIndicatorView.java index bee8069f8..320c0fe48 100644 --- a/app/src/main/java/helium314/keyboard/latin/setup/SetupStepIndicatorView.java +++ b/app/src/main/java/helium314/keyboard/latin/setup/SetupStepIndicatorView.java @@ -13,9 +13,9 @@ import android.util.AttributeSet; import android.view.View; -import helium314.keyboard.latin.R; +import androidx.annotation.NonNull; -import androidx.core.view.ViewCompat; +import helium314.keyboard.latin.R; public final class SetupStepIndicatorView extends View { private final Path mIndicatorPath = new Path(); @@ -29,17 +29,17 @@ public SetupStepIndicatorView(final Context context, final AttributeSet attrs) { } public void setIndicatorPosition(final int stepPos, final int totalStepNum) { - final int layoutDirection = ViewCompat.getLayoutDirection(this); + final int layoutDirection = getLayoutDirection(); // The indicator position is the center of the partition that is equally divided into // the total step number. final float partionWidth = 1.0f / totalStepNum; final float pos = stepPos * partionWidth + partionWidth / 2.0f; - mXRatio = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos; + mXRatio = (layoutDirection == View.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos; invalidate(); } @Override - protected void onDraw(final Canvas canvas) { + protected void onDraw(@NonNull final Canvas canvas) { super.onDraw(canvas); final int xPos = (int)(getWidth() * mXRatio); final int height = getHeight(); diff --git a/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java b/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java index 93030def7..8ef29dd40 100644 --- a/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java +++ b/app/src/main/java/helium314/keyboard/latin/suggestions/MoreSuggestions.java @@ -7,10 +7,12 @@ package helium314.keyboard.latin.suggestions; import android.content.Context; -import android.content.res.Resources; import android.graphics.Paint; import android.graphics.drawable.Drawable; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; + import helium314.keyboard.keyboard.Key; import helium314.keyboard.keyboard.Keyboard; import helium314.keyboard.keyboard.internal.KeyboardBuilder; @@ -45,12 +47,11 @@ public MoreSuggestionsParam() { public int layout(final SuggestedWords suggestedWords, final int fromIndex, final int maxWidth, final int minWidth, final int maxRow, final Paint paint, - final Resources res) { + final Context context) { clearKeys(); - mDivider = res.getDrawable(R.drawable.more_suggestions_divider); - mDividerWidth = mDivider.getIntrinsicWidth(); - final float padding = res.getDimension( - R.dimen.config_more_suggestions_key_horizontal_padding); + mDivider = ContextCompat.getDrawable(context, R.drawable.more_suggestions_divider); + mDividerWidth = mDivider == null ? 0 : mDivider.getIntrinsicWidth(); + final float padding = context.getResources().getDimension(R.dimen.config_more_suggestions_key_horizontal_padding); int row = 0; int index = fromIndex; @@ -186,7 +187,7 @@ public Builder layout(final SuggestedWords suggestedWords, final int fromIndex, mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2; mPaneView.updateKeyboardGeometry(mParams.mDefaultAbsoluteRowHeight); final int count = mParams.layout(suggestedWords, fromIndex, maxWidth, minWidth, maxRow, - mPaneView.newLabelPaint(null /* key */), mResources); + mPaneView.newLabelPaint(null /* key */), getMContext()); mFromIndex = fromIndex; mToIndex = fromIndex + count; mSuggestedWords = suggestedWords; @@ -194,7 +195,7 @@ public Builder layout(final SuggestedWords suggestedWords, final int fromIndex, } @Override - public MoreSuggestions build() { + @NonNull public MoreSuggestions build() { final MoreSuggestionsParam params = mParams; for (int index = mFromIndex; index < mToIndex; index++) { final int x = params.getX(index); diff --git a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java index a8b4ffc2b..aea803001 100644 --- a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java +++ b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java @@ -67,8 +67,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.widget.PopupMenu; -import androidx.core.view.ViewCompat; public final class SuggestionStripView extends RelativeLayout implements OnClickListener, OnLongClickListener { @@ -76,7 +76,6 @@ public interface Listener { void pickSuggestionManually(SuggestedWordInfo word); void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat); void removeSuggestion(final String word); - CharSequence getSelection(); } public static boolean DEBUG_SUGGESTIONS; @@ -124,8 +123,8 @@ public StripVisibilityGroup(final View suggestionStripView, } public void setLayoutDirection(final int layoutDirection) { - ViewCompat.setLayoutDirection(mSuggestionStripView, layoutDirection); - ViewCompat.setLayoutDirection(mSuggestionsStrip, layoutDirection); + mSuggestionStripView.setLayoutDirection(layoutDirection); + mSuggestionsStrip.setLayoutDirection(layoutDirection); } public void showSuggestionsStrip() { @@ -141,6 +140,7 @@ public SuggestionStripView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.suggestionStripViewStyle); } + @SuppressLint("InflateParams") // does not seem suitable here public SuggestionStripView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); final Colors colors = Settings.getInstance().getCurrent().mColors; @@ -183,6 +183,7 @@ public SuggestionStripView(final Context context, final AttributeSet attrs, fina R.dimen.config_more_suggestions_modal_tolerance); mMoreSuggestionsSlidingDetector = new GestureDetector(context, mMoreSuggestionsSlidingListener); + @SuppressLint("CustomViewStyleable") final TypedArray keyboardAttr = context.obtainStyledAttributes(attrs, R.styleable.Keyboard, defStyle, R.style.SuggestionStripView); mIncognitoIcon = keyboardAttr.getDrawable(R.styleable.Keyboard_iconIncognitoKey); mToolbarArrowIcon = keyboardAttr.getDrawable(R.styleable.Keyboard_iconToolbarKey); @@ -262,9 +263,9 @@ private void updateKeys() { public void setRtl(final boolean isRtlLanguage) { final int layoutDirection; if (!Settings.getInstance().getCurrent().mVarToolbarDirection) - layoutDirection = ViewCompat.LAYOUT_DIRECTION_LOCALE; + layoutDirection = View.LAYOUT_DIRECTION_LOCALE; else{ - layoutDirection = isRtlLanguage ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR; + layoutDirection = isRtlLanguage ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR; mRtl = isRtlLanguage ? -1 : 1; } mStripVisibilityGroup.setLayoutDirection(layoutDirection); @@ -528,7 +529,8 @@ boolean showMoreSuggestions() { private final GestureDetector.OnGestureListener mMoreSuggestionsSlidingListener = new GestureDetector.SimpleOnGestureListener() { @Override - public boolean onScroll(MotionEvent down, MotionEvent me, float deltaX, float deltaY) { + public boolean onScroll(@Nullable MotionEvent down, @NonNull MotionEvent me, float deltaX, float deltaY) { + if (down == null) return false; final float dy = me.getY() - down.getY(); if (mToolbarContainer.getVisibility() != VISIBLE && deltaY > 0 && dy < 0) { return showMoreSuggestions(); @@ -637,7 +639,7 @@ public void onClick(final View view) { AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(KeyCode.NOT_SPECIFIED, this); final Object tag = view.getTag(); if (tag instanceof ToolbarKey) { - final Integer code = getCodeForToolbarKey((ToolbarKey) tag); + final int code = getCodeForToolbarKey((ToolbarKey) tag); if (code != KeyCode.UNSPECIFIED) { Log.d(TAG, "click toolbar key "+tag); mListener.onCodeInput(code, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, false); diff --git a/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt index 05283a1f0..3fa3d2e64 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt @@ -18,7 +18,6 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams import helium314.keyboard.latin.R import helium314.keyboard.latin.common.FileUtils -import helium314.keyboard.latin.settings.Settings import java.io.File import java.io.IOException import java.math.BigInteger @@ -80,13 +79,13 @@ private fun checkLayout(layoutContent: String, context: Context): Boolean? { params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT) addLocaleKeyTextsToParams(context, params, POPUP_KEYS_NORMAL) try { - val keys = RawKeyboardParser.parseJsonString(layoutContent).map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } + val keys = RawKeyboardParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } } if (!checkKeys(keys)) return null return true } catch (e: Exception) { Log.w(TAG, "error parsing custom json layout", e) } try { - val keys = RawKeyboardParser.parseSimpleString(layoutContent).map { it.map { it.toKeyParams(params) } } + val keys = RawKeyboardParser.parseSimpleString(layoutContent).map { row -> row.map { it.toKeyParams(params) } } if (!checkKeys(keys)) return null return false @@ -94,7 +93,7 @@ private fun checkLayout(layoutContent: String, context: Context): Boolean? { if (layoutContent.startsWith("[")) { // layout can't be loaded, assume it's json -> load json layout again because the error message shown to the user is from the most recent error try { - RawKeyboardParser.parseJsonString(layoutContent).map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } + RawKeyboardParser.parseJsonString(layoutContent).map { row -> row.mapNotNull { it.compute(params)?.toKeyParams(params) } } } catch (e: Exception) { Log.w(TAG, "error parsing custom json layout", e) } } return null @@ -109,19 +108,19 @@ private fun checkKeys(keys: List>): Boolean { Log.w(TAG, "too many rows") return false } - if (keys.any { it.size > 20 }) { + if (keys.any { row -> row.size > 20 }) { Log.w(TAG, "too many keys in one row") return false } - if (keys.any { it.any { ((it.mLabel?.length ?: 0) > 6) } }) { + if (keys.any { row -> row.any { ((it.mLabel?.length ?: 0) > 6) } }) { Log.w(TAG, "too long text on key") return false } - if (keys.any { it.any { (it.mPopupKeys?.size ?: 0) > 20 } }) { + if (keys.any { row -> row.any { (it.mPopupKeys?.size ?: 0) > 20 } }) { Log.w(TAG, "too many popup keys on a key") return false } - if (keys.any { it.any { it.mPopupKeys?.any { (it.mLabel?.length ?: 0) > 10 } == true } }) { + if (keys.any { row -> row.any { it.mPopupKeys?.any { popupKey -> (popupKey.mLabel?.length ?: 0) > 10 } == true } }) { Log.w(TAG, "too long text on popup key") return false } diff --git a/app/src/main/java/helium314/keyboard/latin/utils/RecapitalizeStatus.java b/app/src/main/java/helium314/keyboard/latin/utils/RecapitalizeStatus.java index 0b0f129f9..1616017fd 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/RecapitalizeStatus.java +++ b/app/src/main/java/helium314/keyboard/latin/utils/RecapitalizeStatus.java @@ -29,7 +29,7 @@ public class RecapitalizeStatus { CAPS_MODE_ALL_UPPER }; - private static final int getStringMode(final String string, final int[] sortedSeparators) { + private static int getStringMode(final String string, final int[] sortedSeparators) { if (StringUtils.isIdenticalAfterUpcase(string)) { return CAPS_MODE_ALL_UPPER; } else if (StringUtils.isIdenticalAfterDowncase(string)) { @@ -42,14 +42,14 @@ private static final int getStringMode(final String string, final int[] sortedSe } public static String modeToString(final int recapitalizeMode) { - switch (recapitalizeMode) { - case NOT_A_RECAPITALIZE_MODE: return "undefined"; - case CAPS_MODE_ORIGINAL_MIXED_CASE: return "mixedCase"; - case CAPS_MODE_ALL_LOWER: return "allLower"; - case CAPS_MODE_FIRST_WORD_UPPER: return "firstWordUpper"; - case CAPS_MODE_ALL_UPPER: return "allUpper"; - default: return "unknown<" + recapitalizeMode + ">"; - } + return switch (recapitalizeMode) { + case NOT_A_RECAPITALIZE_MODE -> "undefined"; + case CAPS_MODE_ORIGINAL_MIXED_CASE -> "mixedCase"; + case CAPS_MODE_ALL_LOWER -> "allLower"; + case CAPS_MODE_FIRST_WORD_UPPER -> "firstWordUpper"; + case CAPS_MODE_ALL_UPPER -> "allUpper"; + default -> "unknown<" + recapitalizeMode + ">"; + }; } /** @@ -145,21 +145,10 @@ public void rotate() { } ++count; switch (ROTATION_STYLE[mRotationStyleCurrentIndex]) { - case CAPS_MODE_ORIGINAL_MIXED_CASE: - mStringAfter = mStringBefore; - break; - case CAPS_MODE_ALL_LOWER: - mStringAfter = mStringBefore.toLowerCase(mLocale); - break; - case CAPS_MODE_FIRST_WORD_UPPER: - mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSortedSeparators, - mLocale); - break; - case CAPS_MODE_ALL_UPPER: - mStringAfter = mStringBefore.toUpperCase(mLocale); - break; - default: - mStringAfter = mStringBefore; + case CAPS_MODE_ALL_LOWER -> mStringAfter = mStringBefore.toLowerCase(mLocale); + case CAPS_MODE_FIRST_WORD_UPPER -> mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSortedSeparators, mLocale); + case CAPS_MODE_ALL_UPPER -> mStringAfter = mStringBefore.toUpperCase(mLocale); + default -> mStringAfter = mStringBefore; } } while (mStringAfter.equals(oldResult) && count < ROTATION_STYLE.length + 1); mCursorEndAfter = mCursorStartAfter + mStringAfter.length(); diff --git a/app/src/test/java/helium314/keyboard/KeySpecParserTest.kt b/app/src/test/java/helium314/keyboard/KeySpecParserTest.kt index 1c0fc1ba1..5212984e7 100644 --- a/app/src/test/java/helium314/keyboard/KeySpecParserTest.kt +++ b/app/src/test/java/helium314/keyboard/KeySpecParserTest.kt @@ -2,7 +2,6 @@ package helium314.keyboard import helium314.keyboard.keyboard.internal.KeySpecParser import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode -import helium314.keyboard.latin.common.Constants import org.junit.Assert.assertEquals import org.junit.Test diff --git a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt index 68d36b977..f5d398085 100644 --- a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt +++ b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt @@ -867,7 +867,7 @@ private val ic = object : InputConnection { // update selection selectionStart -= beforeLength selectionEnd -= beforeLength - return true; + return true } override fun sendKeyEvent(p0: KeyEvent): Boolean { if (p0.action != KeyEvent.ACTION_DOWN) return true // only change the text on key down, like RichInputConnection does @@ -908,7 +908,7 @@ private val ic = object : InputConnection { override fun clearMetaKeyStates(p0: Int): Boolean = TODO("Not yet implemented") override fun reportFullscreenMode(p0: Boolean): Boolean = TODO("Not yet implemented") override fun performPrivateCommand(p0: String?, p1: Bundle?): Boolean = TODO("Not yet implemented") - override fun getHandler(): Handler? = TODO("Not yet implemented") + override fun getHandler(): Handler = TODO("Not yet implemented") override fun closeConnection() = TODO("Not yet implemented") override fun commitContent(p0: InputContentInfo, p1: Int, p2: Bundle?): Boolean = TODO("Not yet implemented") } diff --git a/app/src/test/java/helium314/keyboard/latin/SuggestTest.kt b/app/src/test/java/helium314/keyboard/latin/SuggestTest.kt index 7e92f48f7..fe07e5c5f 100644 --- a/app/src/test/java/helium314/keyboard/latin/SuggestTest.kt +++ b/app/src/test/java/helium314/keyboard/latin/SuggestTest.kt @@ -25,6 +25,7 @@ import org.robolectric.annotation.Implements import org.robolectric.shadows.ShadowLog import java.util.* +@Suppress("NonAsciiCharacters") @RunWith(RobolectricTestRunner::class) @Config(shadows = [ ShadowLocaleManagerCompat::class, diff --git a/build.gradle b/build.gradle index 0f10f1bfc..83e31cf34 100755 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.9.23' + ext.kotlin_version = '1.9.24' repositories { mavenCentral() google() @@ -17,7 +17,7 @@ buildscript { } plugins { - id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.23' apply false + id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version" } allprojects { From 82705e5d5e265a3ec9249b69e9964547545f13cf Mon Sep 17 00:00:00 2001 From: Helium314 Date: Wed, 15 May 2024 22:54:11 +0200 Subject: [PATCH 17/58] remove unused fields in DictionaryHeader --- .../inputmethod/latin/BinaryDictionary.java | 6 +----- .../latin/makedict/DictionaryHeader.kt | 10 ++-------- .../keyboard/latin/makedict/FormatSpec.java | 20 ++++--------------- 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/android/inputmethod/latin/BinaryDictionary.java b/app/src/main/java/com/android/inputmethod/latin/BinaryDictionary.java index 7638c426c..b5d339774 100644 --- a/app/src/main/java/com/android/inputmethod/latin/BinaryDictionary.java +++ b/app/src/main/java/com/android/inputmethod/latin/BinaryDictionary.java @@ -21,7 +21,6 @@ import helium314.keyboard.latin.common.InputPointers; import helium314.keyboard.latin.common.StringUtils; import helium314.keyboard.latin.makedict.DictionaryHeader; -import helium314.keyboard.latin.makedict.FormatSpec; import helium314.keyboard.latin.makedict.FormatSpec.DictionaryOptions; import helium314.keyboard.latin.makedict.UnsupportedFormatException; import helium314.keyboard.latin.makedict.WordProperty; @@ -245,10 +244,7 @@ public DictionaryHeader getHeader() throws UnsupportedFormatException { outAttributeValues.get(i)); attributes.put(attributeKey, attributeValue); } - final boolean hasHistoricalInfo = DictionaryHeader.ATTRIBUTE_VALUE_TRUE.equals( - attributes.get(DictionaryHeader.HAS_HISTORICAL_INFO_KEY)); - return new DictionaryHeader(outHeaderSize[0], new DictionaryOptions(attributes), - new FormatSpec.FormatOptions(outFormatVersion[0], hasHistoricalInfo)); + return new DictionaryHeader(new DictionaryOptions(attributes)); } @Override diff --git a/app/src/main/java/helium314/keyboard/latin/makedict/DictionaryHeader.kt b/app/src/main/java/helium314/keyboard/latin/makedict/DictionaryHeader.kt index a57600fdf..9ca80b975 100644 --- a/app/src/main/java/helium314/keyboard/latin/makedict/DictionaryHeader.kt +++ b/app/src/main/java/helium314/keyboard/latin/makedict/DictionaryHeader.kt @@ -6,10 +6,8 @@ package helium314.keyboard.latin.makedict -import helium314.keyboard.latin.common.LocaleUtils import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.makedict.FormatSpec.DictionaryOptions -import helium314.keyboard.latin.makedict.FormatSpec.FormatOptions import java.text.DateFormat import java.util.Date import java.util.Locale @@ -18,12 +16,8 @@ import java.util.Locale * Class representing dictionary header. */ class DictionaryHeader( - headerSize: Int, - @JvmField - val mDictionaryOptions: DictionaryOptions, - val mFormatOptions: FormatOptions + @JvmField val mDictionaryOptions: DictionaryOptions, ) { - val mBodyOffset = if (mFormatOptions.mVersion < FormatSpec.VERSION4) headerSize else 0 val mLocaleString = mDictionaryOptions.mAttributes[DICTIONARY_LOCALE_KEY] ?: throw UnsupportedFormatException("Cannot create a FileHeader without a locale") @JvmField @@ -34,7 +28,7 @@ class DictionaryHeader( @JvmField val mIdString = mDictionaryOptions.mAttributes[DICTIONARY_ID_KEY] ?: throw UnsupportedFormatException("Cannot create a FileHeader without an ID") - val mDate = mDictionaryOptions.mAttributes[DICTIONARY_DATE_KEY]?.toIntOrNull() + private val mDate = mDictionaryOptions.mAttributes[DICTIONARY_DATE_KEY]?.toIntOrNull() val description: String? // Helper method to get the description diff --git a/app/src/main/java/helium314/keyboard/latin/makedict/FormatSpec.java b/app/src/main/java/helium314/keyboard/latin/makedict/FormatSpec.java index 25351e6ee..91f391464 100644 --- a/app/src/main/java/helium314/keyboard/latin/makedict/FormatSpec.java +++ b/app/src/main/java/helium314/keyboard/latin/makedict/FormatSpec.java @@ -6,6 +6,8 @@ package helium314.keyboard.latin.makedict; +import androidx.annotation.NonNull; + import helium314.keyboard.latin.define.DecoderSpecificConstants; import java.util.Date; @@ -236,19 +238,6 @@ public final class FormatSpec { static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20; static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF; - /** - * Options about file format. - */ - public static final class FormatOptions { - public final int mVersion; - public final boolean mHasTimestamp; - - public FormatOptions(final int version, final boolean hasTimestamp) { - mVersion = version; - mHasTimestamp = hasTimestamp; - } - } - /** * Options global to the dictionary. */ @@ -258,7 +247,7 @@ public DictionaryOptions(final HashMap attributes) { mAttributes = attributes; } @Override - public String toString() { // Convenience method + @NonNull public String toString() { // Convenience method return toString(0, false); } public String toString(final int indentCount, final boolean plumbing) { @@ -277,8 +266,7 @@ public String toString(final int indentCount, final boolean plumbing) { s.append(" = "); if ("date".equals(optionKey) && !plumbing) { // Date needs a number of milliseconds, but the dictionary contains seconds - s.append(new Date( - 1000 * Long.parseLong(mAttributes.get(optionKey))).toString()); + s.append(new Date(1000 * Long.parseLong(mAttributes.get(optionKey)))); } else { s.append(mAttributes.get(optionKey)); } From 1a91ce5dd025bdc5ca0ab50725ea6dfaf65a14a2 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Fri, 17 May 2024 17:22:51 +0200 Subject: [PATCH 18/58] allow commenting lines in json layouts by starting the line with // --- .../keyboard/internal/keyboard_parser/RawKeyboardParser.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt index f108a5666..7c47dae93 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt @@ -65,7 +65,7 @@ object RawKeyboardParser { * codes of multi_text_key not used, only the label * (currently) popups is always read to [number, main, relevant] layoutPopupKeys, no choice of which to use or which hint is provided */ - fun parseJsonString(layoutText: String): List> = florisJsonConfig.decodeFromString(layoutText) + fun parseJsonString(layoutText: String): List> = florisJsonConfig.decodeFromString(layoutText.stripCommentLines()) /** Parse simple layouts, defined only as rows of (normal) keys with popup keys. */ fun parseSimpleString(layoutText: String): List> { @@ -166,6 +166,10 @@ object RawKeyboardParser { } } + // allow commenting lines by starting them with "//" + private fun String.stripCommentLines(): String = + split("\n").filterNot { it.startsWith("//") }.joinToString("\n") + /* * Copyright (C) 2021 Patrick Goldinger * modified From 71727de5a0ad25c4252302404aa332637ad0962e Mon Sep 17 00:00:00 2001 From: Helium314 Date: Fri, 17 May 2024 17:24:34 +0200 Subject: [PATCH 19/58] add basic support for modifier keys, fixes #479 --- README.md | 5 +- .../keyboard/event/DeadKeyCombiner.kt | 2 +- .../java/helium314/keyboard/event/Event.kt | 69 +++++++++---------- .../keyboard/event/HangulCombiner.kt | 2 +- .../keyboard/event/HangulEventDecoder.kt | 2 +- .../event/HardwareKeyboardEventDecoder.kt | 29 ++++---- .../java/helium314/keyboard/keyboard/Key.java | 2 + .../keyboard/KeyboardActionListener.java | 3 + .../keyboard/KeyboardActionListenerImpl.kt | 23 ++++++- .../keyboard/keyboard/PointerTracker.java | 5 ++ .../keyboard_parser/KeyboardParser.kt | 1 - .../keyboard_parser/floris/KeyCode.kt | 49 ++++++++++++- .../helium314/keyboard/latin/LatinIME.java | 15 ++-- .../keyboard/latin/inputlogic/InputLogic.java | 16 ++++- .../keyboard/latin/InputLogicTest.kt | 2 +- layouts.md | 5 +- 16 files changed, 158 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 80f4cc23a..669a0a879 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,10 @@ See make-dict-list tool [README](tools/make-dict-list/README.md). __Planned features and improvements:__ * Customizable functional key layout * Will likely result in having the same functional key layout for alphabet and symbols layouts -* Support for _alt_, _ctrl_, _meta_ and _fn_ (#479) +* Improve support for modifier keys (_alt_, _ctrl_, _meta_ and _fn_), some ideas: + * keep modifier keys on with long press + * keep modifier keys on until the next key press + * use sliding input * Less complicated addition of new keyboard languages (e.g. #519) * Additional and customizable key swipe functionality * Some functionality will not be possible when using glide typing diff --git a/app/src/main/java/helium314/keyboard/event/DeadKeyCombiner.kt b/app/src/main/java/helium314/keyboard/event/DeadKeyCombiner.kt index 2de633668..a00b8941f 100644 --- a/app/src/main/java/helium314/keyboard/event/DeadKeyCombiner.kt +++ b/app/src/main/java/helium314/keyboard/event/DeadKeyCombiner.kt @@ -261,7 +261,7 @@ class DeadKeyCombiner : Combiner { var lastEvent: Event? = null do { val codePoint = Character.codePointBefore(text, index) - lastEvent = Event.createHardwareKeypressEvent(codePoint, originalEvent.mKeyCode, lastEvent, false) + lastEvent = Event.createHardwareKeypressEvent(codePoint, originalEvent.mKeyCode, 0, lastEvent, false) index -= Character.charCount(codePoint) } while (index > 0) // can't be null because diff --git a/app/src/main/java/helium314/keyboard/event/Event.kt b/app/src/main/java/helium314/keyboard/event/Event.kt index e8af099fc..d36ab49c9 100644 --- a/app/src/main/java/helium314/keyboard/event/Event.kt +++ b/app/src/main/java/helium314/keyboard/event/Event.kt @@ -39,6 +39,9 @@ class Event private constructor( // this to be equal to mCodePoint for convenience. If this is not a key, this must contain // NOT_A_KEY_CODE. val mKeyCode: Int, + // State of meta keys (currently ctrl, alt, fn, meta) + // same value as https://developer.android.com/reference/android/view/KeyEvent#getMetaState() + val mMetaState: Int, // Coordinates of the touch event, if relevant. If useful, we may want to replace this with // a MotionEvent or something in the future. This is only relevant when the keypress is from // a software keyboard obviously, unless there are touch-sensitive hardware keyboards in the @@ -57,7 +60,7 @@ class Event private constructor( // Returns whether this is a function key like backspace, ctrl, settings... as opposed to keys // that result in input like letters or space. val isFunctionalKeyEvent: Boolean - get() = NOT_A_CODE_POINT == mCodePoint // This logic may need to be refined in the future + get() = NOT_A_CODE_POINT == mCodePoint || mMetaState != 0 // This logic may need to be refined in the future // Returns whether this event is for a dead character. @see {@link #FLAG_DEAD} val isDead: Boolean get() = 0 != FLAG_DEAD and mFlags @@ -131,24 +134,22 @@ class Event private constructor( private const val FLAG_COMBINING = 0x8 @JvmStatic - fun createSoftwareKeypressEvent(codePoint: Int, keyCode: Int, - x: Int, y: Int, isKeyRepeat: Boolean): Event { - return Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, x, y, - null /* suggestedWordInfo */, if (isKeyRepeat) FLAG_REPEAT else FLAG_NONE, null) + fun createSoftwareKeypressEvent(codePoint: Int, keyCode: Int, metaState: Int, x: Int, y: Int, isKeyRepeat: Boolean): Event { + return Event(EVENT_TYPE_INPUT_KEYPRESS, null, codePoint, keyCode, metaState, x, y, + null, if (isKeyRepeat) FLAG_REPEAT else FLAG_NONE, null) } - fun createHardwareKeypressEvent(codePoint: Int, keyCode: Int, - next: Event?, isKeyRepeat: Boolean): Event { - return Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, + fun createHardwareKeypressEvent(codePoint: Int, keyCode: Int, metaState: Int, next: Event?, isKeyRepeat: Boolean): Event { + return Event(EVENT_TYPE_INPUT_KEYPRESS, null, codePoint, keyCode, metaState, Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE, - null /* suggestedWordInfo */, if (isKeyRepeat) FLAG_REPEAT else FLAG_NONE, next) + null, if (isKeyRepeat) FLAG_REPEAT else FLAG_NONE, next) } // This creates an input event for a dead character. @see {@link #FLAG_DEAD} - fun createDeadEvent(codePoint: Int, keyCode: Int, next: Event?): Event { // TODO: add an argument or something if we ever create a software layout with dead keys. - return Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, + fun createDeadEvent(codePoint: Int, keyCode: Int, metaState: Int, next: Event?): Event { // TODO: add an argument or something if we ever create a software layout with dead keys. + return Event(EVENT_TYPE_INPUT_KEYPRESS, null, codePoint, keyCode, metaState, Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE, - null /* suggestedWordInfo */, FLAG_DEAD, next) + null, FLAG_DEAD, next) } /** @@ -160,9 +161,8 @@ class Event private constructor( */ @JvmStatic fun createEventForCodePointFromUnknownSource(codePoint: Int): Event { // TODO: should we have a different type of event for this? After all, it's not a key press. - return Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, NOT_A_KEY_CODE, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, - null /* suggestedWordInfo */, FLAG_NONE, null /* next */) + return Event(EVENT_TYPE_INPUT_KEYPRESS, null, codePoint, NOT_A_KEY_CODE, 0, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, null, FLAG_NONE, null) } /** @@ -176,8 +176,8 @@ class Event private constructor( @JvmStatic fun createEventForCodePointFromAlreadyTypedText(codePoint: Int, x: Int, y: Int): Event { // TODO: should we have a different type of event for this? After all, it's not a key press. - return Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, NOT_A_KEY_CODE, - x, y, null /* suggestedWordInfo */, FLAG_NONE, null /* next */) + return Event(EVENT_TYPE_INPUT_KEYPRESS, null, codePoint, NOT_A_KEY_CODE, 0, + x, y, null, FLAG_NONE, null) } /** @@ -187,9 +187,9 @@ class Event private constructor( @JvmStatic fun createSuggestionPickedEvent(suggestedWordInfo: SuggestedWordInfo): Event { return Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, - NOT_A_CODE_POINT, NOT_A_KEY_CODE, + NOT_A_CODE_POINT, NOT_A_KEY_CODE, 0, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, - suggestedWordInfo, FLAG_NONE, null /* next */) + suggestedWordInfo, FLAG_NONE, null) } /** @@ -203,9 +203,8 @@ class Event private constructor( */ @JvmStatic fun createSoftwareTextEvent(text: CharSequence?, keyCode: Int, next: Event?): Event { - return Event(EVENT_TYPE_SOFTWARE_GENERATED_STRING, text, NOT_A_CODE_POINT, keyCode, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, - null /* suggestedWordInfo */, FLAG_NONE, next) + return Event(EVENT_TYPE_SOFTWARE_GENERATED_STRING, text, NOT_A_CODE_POINT, keyCode, 0, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,null, FLAG_NONE, next) } @JvmStatic @@ -217,13 +216,10 @@ class Event private constructor( * @return an event for this suggestion pick. */ @JvmStatic - fun createPunctuationSuggestionPickedEvent( - suggestedWordInfo: SuggestedWordInfo): Event { + fun createPunctuationSuggestionPickedEvent(suggestedWordInfo: SuggestedWordInfo): Event { val primaryCode = suggestedWordInfo.mWord[0].code - return Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, primaryCode, - NOT_A_KEY_CODE, Constants.SUGGESTION_STRIP_COORDINATE, - Constants.SUGGESTION_STRIP_COORDINATE, suggestedWordInfo, FLAG_NONE, - null /* next */) + return Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, primaryCode, NOT_A_KEY_CODE, 0, + Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, suggestedWordInfo, FLAG_NONE,null) } /** @@ -234,7 +230,7 @@ class Event private constructor( */ @JvmStatic fun createCursorMovedEvent(moveAmount: Int): Event { - return Event(EVENT_TYPE_CURSOR_MOVE, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE, + return Event(EVENT_TYPE_CURSOR_MOVE, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE, 0, moveAmount, Constants.NOT_A_COORDINATE, null, FLAG_NONE, null) } @@ -244,21 +240,18 @@ class Event private constructor( * @return an identical event marked as consumed. */ fun createConsumedEvent(source: Event): Event { // A consumed event should not input any text at all, so we pass the empty string as text. - return Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode, - source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_CONSUMED, - source.mNextEvent) + return Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode, source.mMetaState, + source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_CONSUMED, source.mNextEvent) } fun createCombiningEvent(source: Event): Event { - return Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode, - source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_COMBINING, - source.mNextEvent) + return Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode, source.mMetaState, + source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_COMBINING, source.mNextEvent) } fun createNotHandledEvent(): Event { - return Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, - null /* suggestedWordInfo */, FLAG_NONE, null) + return Event(EVENT_TYPE_NOT_HANDLED, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE, 0, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, null, FLAG_NONE, null) } } diff --git a/app/src/main/java/helium314/keyboard/event/HangulCombiner.kt b/app/src/main/java/helium314/keyboard/event/HangulCombiner.kt index 411c46995..42fa01616 100644 --- a/app/src/main/java/helium314/keyboard/event/HangulCombiner.kt +++ b/app/src/main/java/helium314/keyboard/event/HangulCombiner.kt @@ -25,7 +25,7 @@ class HangulCombiner : Combiner { return when { history.size == 1 && composingWord.isEmpty() || history.isEmpty() && composingWord.length == 1 -> { reset() - Event.createHardwareKeypressEvent(0x20, Constants.CODE_SPACE, event, event.isKeyRepeat) + Event.createHardwareKeypressEvent(0x20, Constants.CODE_SPACE, 0, event, event.isKeyRepeat) } history.isNotEmpty() -> { history.removeAt(history.lastIndex) diff --git a/app/src/main/java/helium314/keyboard/event/HangulEventDecoder.kt b/app/src/main/java/helium314/keyboard/event/HangulEventDecoder.kt index 6b5ee8d15..73635aec5 100644 --- a/app/src/main/java/helium314/keyboard/event/HangulEventDecoder.kt +++ b/app/src/main/java/helium314/keyboard/event/HangulEventDecoder.kt @@ -13,7 +13,7 @@ object HangulEventDecoder { fun decodeHardwareKeyEvent(subtype: RichInputMethodSubtype, event: KeyEvent, defaultEvent: () -> Event): Event { val layout = LAYOUTS[subtype.keyboardLayoutSetName] ?: return defaultEvent() val codePoint = layout[event.keyCode]?.let { if (event.isShiftPressed) it.second else it.first } ?: return defaultEvent() - val hardwareEvent = Event.createHardwareKeypressEvent(codePoint, event.keyCode, null, event.repeatCount != 0) + val hardwareEvent = Event.createHardwareKeypressEvent(codePoint, event.keyCode, event.metaState, null, event.repeatCount != 0) return decodeSoftwareKeyEvent(hardwareEvent) } diff --git a/app/src/main/java/helium314/keyboard/event/HardwareKeyboardEventDecoder.kt b/app/src/main/java/helium314/keyboard/event/HardwareKeyboardEventDecoder.kt index 31c64f22a..2742ee193 100644 --- a/app/src/main/java/helium314/keyboard/event/HardwareKeyboardEventDecoder.kt +++ b/app/src/main/java/helium314/keyboard/event/HardwareKeyboardEventDecoder.kt @@ -29,30 +29,25 @@ class HardwareKeyboardEventDecoder(val mDeviceId: Int) : HardwareEventDecoder { // do not necessarily map to a unicode character. This represents a physical key, like // the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock. val keyCode = keyEvent.keyCode + val metaState = keyEvent.metaState val isKeyRepeat = 0 != keyEvent.repeatCount - if (KeyEvent.KEYCODE_DEL == keyCode) { - return Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, KeyCode.DELETE, null /* next */, isKeyRepeat) - } - if (keyEvent.isPrintingKey || KeyEvent.KEYCODE_SPACE == keyCode || KeyEvent.KEYCODE_ENTER == keyCode) { + return if (KeyEvent.KEYCODE_DEL == keyCode) { + Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, KeyCode.DELETE, metaState, null, isKeyRepeat) + } else if (keyEvent.isPrintingKey || KeyEvent.KEYCODE_SPACE == keyCode || KeyEvent.KEYCODE_ENTER == keyCode) { if (0 != codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT) { // A dead key. - return Event.createDeadEvent( - codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT_MASK, keyCode, null /* next */) - } - return if (KeyEvent.KEYCODE_ENTER == keyCode) { + Event.createDeadEvent(codePointAndFlags and KeyCharacterMap.COMBINING_ACCENT_MASK, keyCode, metaState, null) + } else if (KeyEvent.KEYCODE_ENTER == keyCode) { // The Enter key. If the Shift key is not being pressed, this should send a // CODE_ENTER to trigger the action if any, or a carriage return otherwise. If the // Shift key is being pressed, this should send a CODE_SHIFT_ENTER and let // Latin IME decide what to do with it. if (keyEvent.isShiftPressed) { - Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, - KeyCode.SHIFT_ENTER, null /* next */, isKeyRepeat) - } else Event.createHardwareKeypressEvent(Constants.CODE_ENTER, keyCode, - null /* next */, isKeyRepeat) - } else Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, null /* next */, isKeyRepeat) + Event.createHardwareKeypressEvent(Event.NOT_A_CODE_POINT, // todo: maybe remove, see also related comment in input logic + KeyCode.SHIFT_ENTER, 0, null, isKeyRepeat) + } else Event.createHardwareKeypressEvent(Constants.CODE_ENTER, keyCode, metaState, null, isKeyRepeat) + } else Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, metaState, null, isKeyRepeat) // If not Enter, then this is just a regular keypress event for a normal character // that can be committed right away, taking into account the current state. - } - return Event.createNotHandledEvent() + } else Event.createNotHandledEvent() } - -} \ No newline at end of file +} diff --git a/app/src/main/java/helium314/keyboard/keyboard/Key.java b/app/src/main/java/helium314/keyboard/keyboard/Key.java index 7368b27bc..7f765ff4f 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/Key.java +++ b/app/src/main/java/helium314/keyboard/keyboard/Key.java @@ -514,6 +514,8 @@ public final boolean isShift() { public final boolean isModifier() { return mCode == KeyCode.SHIFT || mCode == KeyCode.SYMBOL_ALPHA || mCode == KeyCode.ALPHA || mCode == KeyCode.SYMBOL; + // todo: if this is used, sliding input starts on those keys, but it's not yet implemented +// || mCode == KeyCode.CTRL || mCode == KeyCode.ALT || mCode == KeyCode.FN || mCode == KeyCode.META; } public final boolean isRepeatable() { diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java index 0e3c6c5cb..4320f7ba0 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListener.java @@ -100,6 +100,7 @@ public interface KeyboardActionListener { void onMoveDeletePointer(int steps); void onUpWithDeletePointerActive(); + void resetMetaState(); KeyboardActionListener EMPTY_LISTENER = new Adapter(); @@ -144,5 +145,7 @@ public boolean onVerticalSpaceSwipe(int steps) { public void onMoveDeletePointer(int steps) {} @Override public void onUpWithDeletePointerActive() {} + @Override + public void resetMetaState() {} } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt index 30c047dbe..36ad42ebe 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardActionListenerImpl.kt @@ -1,5 +1,6 @@ package helium314.keyboard.keyboard +import android.view.KeyEvent import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode import helium314.keyboard.latin.LatinIME import helium314.keyboard.latin.RichInputMethodManager @@ -13,18 +14,34 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp private val keyboardSwitcher = KeyboardSwitcher.getInstance() private val settings = Settings.getInstance() + private var metaState = 0 // is this enough, or are there threading issues with the different PointerTrackers? + + // todo: maybe keep meta state presses to KeyboardActionListenerImpl, and avoid calls to press/release key + private fun adjustMetaState(code: Int, remove: Boolean) { + val metaCode = when (code) { + KeyCode.CTRL -> KeyEvent.META_CTRL_ON + KeyCode.ALT -> KeyEvent.META_ALT_ON + KeyCode.FN -> KeyEvent.META_FUNCTION_ON + KeyCode.META -> KeyEvent.META_META_ON + else -> return + } + metaState = if (remove) metaState and metaCode.inv() + else metaState or metaCode + } override fun onPressKey(primaryCode: Int, repeatCount: Int, isSinglePointer: Boolean) { + adjustMetaState(primaryCode, false) keyboardSwitcher.onPressKey(primaryCode, isSinglePointer, latinIME.currentAutoCapsState, latinIME.currentRecapitalizeState) latinIME.hapticAndAudioFeedback(primaryCode, repeatCount) } override fun onReleaseKey(primaryCode: Int, withSliding: Boolean) { + adjustMetaState(primaryCode, true) keyboardSwitcher.onReleaseKey(primaryCode, withSliding, latinIME.currentAutoCapsState, latinIME.currentRecapitalizeState) } override fun onCodeInput(primaryCode: Int, x: Int, y: Int, isKeyRepeat: Boolean) = - latinIME.onCodeInput(primaryCode, x, y, isKeyRepeat) + latinIME.onCodeInput(primaryCode, metaState, x, y, isKeyRepeat) override fun onTextInput(text: String?) = latinIME.onTextInput(text) @@ -74,6 +91,10 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp onCodeInput(KeyCode.DELETE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false) } + override fun resetMetaState() { + metaState = 0 + } + private fun onLanguageSlide(steps: Int): Boolean { if (abs(steps) < 4) return false val subtypes = RichInputMethodManager.getInstance().getMyEnabledInputMethodSubtypeList(false) diff --git a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java index 4baed1054..332825a00 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java @@ -737,6 +737,7 @@ private void onGestureMoveEvent(final int x, final int y, final long eventTime, } if (!sInGesture && key != null && Character.isLetter(key.getCode()) && mBatchInputArbiter.mayStartBatchInput(this)) { + sListener.resetMetaState(); // avoid metaState getting stuck, doesn't work with gesture typing anyway sInGesture = true; } if (sInGesture) { @@ -1117,6 +1118,10 @@ public void onLongPressed() { if (popupKeysPanel == null) { return; } + if (code == KeyCode.CTRL || code == KeyCode.ALT || code == KeyCode.FN || code == KeyCode.META) { + // avoid metaState getting stuck + sListener.onReleaseKey(code, false); + } final int translatedX = popupKeysPanel.translateX(mLastX); final int translatedY = popupKeysPanel.translateY(mLastY); popupKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, SystemClock.uptimeMillis()); diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index 5b9d15809..fabae1290 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -298,7 +298,6 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co // this is ugly... if (label.length > 8 && label.startsWith("!string/")) { val id = context.resources.getIdentifier(label.substringAfter("!string/"), "string", context.packageName) - Log.i("test", "id of $label: $id") if (id != 0) copy(newLabel = getInLocale(id)) else this } else this diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt index d11b206c4..96f5173e6 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt @@ -5,6 +5,8 @@ */ package helium314.keyboard.keyboard.internal.keyboard_parser.floris +import android.view.KeyEvent + // taken from FlorisBoard and modified object KeyCode { object Spec { @@ -134,6 +136,8 @@ object KeyCode { const val CLIPBOARD_COPY_ALL = -10009 const val PAGE_UP = -10010 const val PAGE_DOWN = -10011 + const val META = -10012 + const val META_LOCK = -10013 // to be consistent with the CTRL/ALT(/FN LOCK codes, not sure whether this will be used /** to make sure a FlorisBoard code works when reading a JSON layout */ fun Int.checkAndConvertCode(): Int = if (this > 0) this else when (this) { @@ -142,7 +146,7 @@ object KeyCode { VOICE_INPUT, LANGUAGE_SWITCH, SETTINGS, DELETE, ALPHA, SYMBOL, EMOJI, CLIPBOARD, UNDO, REDO, ARROW_DOWN, ARROW_UP, ARROW_RIGHT, ARROW_LEFT, CLIPBOARD_COPY, CLIPBOARD_SELECT_ALL, CLIPBOARD_SELECT_WORD, TOGGLE_INCOGNITO_MODE, TOGGLE_AUTOCORRECT, MOVE_START_OF_LINE, MOVE_END_OF_LINE, - SHIFT, CAPS_LOCK, MULTIPLE_CODE_POINTS, UNSPECIFIED, + SHIFT, CAPS_LOCK, MULTIPLE_CODE_POINTS, UNSPECIFIED, CTRL, ALT, FN, META, // heliboard only SYMBOL_ALPHA, START_ONE_HANDED_MODE, STOP_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SHIFT_ENTER, @@ -156,4 +160,47 @@ object KeyCode { else -> throw IllegalStateException("key code $this not yet supported") } + + // todo: add more keys, see near https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0 + // maybe not toChar for conversion of some special keys? + /** convert a codePoint to a KeyEvent.KEYCODE_, fallback to KeyEvent.KEYCODE_UNKNOWN */ + fun Int.toKeyEventCode(): Int = when (this.toChar().uppercaseChar()) { + '0' -> KeyEvent.KEYCODE_0 + '1' -> KeyEvent.KEYCODE_1 + '2' -> KeyEvent.KEYCODE_2 + '3' -> KeyEvent.KEYCODE_3 + '4' -> KeyEvent.KEYCODE_4 + '5' -> KeyEvent.KEYCODE_5 + '6' -> KeyEvent.KEYCODE_6 + '7' -> KeyEvent.KEYCODE_7 + '8' -> KeyEvent.KEYCODE_8 + '9' -> KeyEvent.KEYCODE_9 + 'A' -> KeyEvent.KEYCODE_A + 'B' -> KeyEvent.KEYCODE_B + 'C' -> KeyEvent.KEYCODE_C + 'D' -> KeyEvent.KEYCODE_D + 'E' -> KeyEvent.KEYCODE_E + 'F' -> KeyEvent.KEYCODE_F + 'G' -> KeyEvent.KEYCODE_G + 'H' -> KeyEvent.KEYCODE_H + 'I' -> KeyEvent.KEYCODE_I + 'J' -> KeyEvent.KEYCODE_J + 'K' -> KeyEvent.KEYCODE_K + 'L' -> KeyEvent.KEYCODE_L + 'M' -> KeyEvent.KEYCODE_M + 'N' -> KeyEvent.KEYCODE_N + 'O' -> KeyEvent.KEYCODE_O + 'P' -> KeyEvent.KEYCODE_P + 'Q' -> KeyEvent.KEYCODE_Q + 'R' -> KeyEvent.KEYCODE_R + 'S' -> KeyEvent.KEYCODE_S + 'T' -> KeyEvent.KEYCODE_T + 'U' -> KeyEvent.KEYCODE_U + 'V' -> KeyEvent.KEYCODE_V + 'W' -> KeyEvent.KEYCODE_W + 'X' -> KeyEvent.KEYCODE_X + 'Y' -> KeyEvent.KEYCODE_Y + 'Z' -> KeyEvent.KEYCODE_Z + else -> KeyEvent.KEYCODE_UNKNOWN + } } diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java index 0e9bf6e26..84a41d6ef 100644 --- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java +++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java @@ -1075,6 +1075,7 @@ private void cleanupInternalStateForFinishInput() { mHandler.cancelUpdateSuggestionStrip(); // Should do the following in onFinishInputInternal but until JB MR2 it's not called :( mInputLogic.finishInput(); + mKeyboardActionListener.resetMetaState(); } protected void deallocateMemory() { @@ -1453,9 +1454,13 @@ public void switchInputMethodAndSubtype(final InputMethodInfo imi, final InputMe } } - // Implementation of {@link KeyboardActionListener}. + // Implementation of {@link SuggestionStripView.Listener}. @Override public void onCodeInput(final int codePoint, final int x, final int y, final boolean isKeyRepeat) { + onCodeInput(codePoint, 0, x, y, isKeyRepeat); + } + + public void onCodeInput(final int codePoint, final int metaState, final int x, final int y, final boolean isKeyRepeat) { if (codePoint < 0) { switch (codePoint) { case KeyCode.TOGGLE_AUTOCORRECT -> {mSettings.toggleAutoCorrect(); return; } @@ -1471,7 +1476,7 @@ public void onCodeInput(final int codePoint, final int x, final int y, final boo // this transformation, it should be done already before calling onEvent. final int keyX = mainKeyboardView.getKeyX(x); final int keyY = mainKeyboardView.getKeyY(y); - final Event event = createSoftwareKeypressEvent(codePoint, keyX, keyY, isKeyRepeat); + final Event event = createSoftwareKeypressEvent(codePoint, metaState, keyX, keyY, isKeyRepeat); onEvent(event); } @@ -1493,8 +1498,8 @@ public void onEvent(@NonNull final Event event) { // squashed into the same variable, and this method should be removed. // public for testing, as we don't want to copy the same logic into test code @NonNull - public static Event createSoftwareKeypressEvent(final int keyCodeOrCodePoint, final int keyX, - final int keyY, final boolean isKeyRepeat) { + public static Event createSoftwareKeypressEvent(final int keyCodeOrCodePoint, final int metaState, + final int keyX, final int keyY, final boolean isKeyRepeat) { final int keyCode; final int codePoint; if (keyCodeOrCodePoint <= 0) { @@ -1504,7 +1509,7 @@ public static Event createSoftwareKeypressEvent(final int keyCodeOrCodePoint, fi keyCode = Event.NOT_A_KEY_CODE; codePoint = keyCodeOrCodePoint; } - return Event.createSoftwareKeypressEvent(codePoint, keyCode, keyX, keyY, isKeyRepeat); + return Event.createSoftwareKeypressEvent(codePoint, keyCode, metaState, keyX, keyY, isKeyRepeat); } public void onTextInput(final String rawText) { diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java index b1152a3c4..4efdd4e45 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java @@ -704,8 +704,9 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp handleClipboardPaste(); break; case KeyCode.SHIFT_ENTER: + // todo: try using sendDownUpKeyEventWithMetaState() and remove the key code maybe final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER, - event.getMKeyCode(), event.getMX(), event.getMY(), event.isKeyRepeat()); + event.getMKeyCode(), 0, event.getMX(), event.getMY(), event.isKeyRepeat()); handleNonSpecialCharacterEvent(tmpEvent, inputTransaction, handler); // Shift + Enter is treated as a functional key but it results in adding a new // line, so that does affect the contents of the editor. @@ -733,7 +734,7 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp if (mConnection.hasSelection()) { mConnection.copyText(true); // fake delete keypress to remove the text - final Event backspaceEvent = LatinIME.createSoftwareKeypressEvent(KeyCode.DELETE, + final Event backspaceEvent = LatinIME.createSoftwareKeypressEvent(KeyCode.DELETE, 0, event.getMX(), event.getMY(), event.isKeyRepeat()); handleBackspaceEvent(backspaceEvent, inputTransaction, currentKeyboardScript); inputTransaction.setDidAffectContents(); @@ -783,9 +784,18 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp case KeyCode.START_ONE_HANDED_MODE: case KeyCode.STOP_ONE_HANDED_MODE: case KeyCode.SWITCH_ONE_HANDED_MODE: + case KeyCode.CTRL: + case KeyCode.ALT: + case KeyCode.FN: + case KeyCode.META: break; default: - throw new RuntimeException("Unknown key code : " + event.getMKeyCode()); + if (event.getMMetaState() != 0) { + // need to convert codepoint to KeyEvent.KEYCODE_ + int keyEventCode = KeyCode.INSTANCE.toKeyEventCode(event.getMCodePoint()); + sendDownUpKeyEventWithMetaState(keyEventCode, event.getMMetaState()); + } else + throw new RuntimeException("Unknown key code : " + event.getMKeyCode()); } } diff --git a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt index f5d398085..b30d5a1ae 100644 --- a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt +++ b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt @@ -591,7 +591,7 @@ class InputLogicTest { private fun functionalKeyPress(keyCode: Int) { require(keyCode < 0) { "not a functional key code: $keyCode" } - latinIME.onEvent(Event.createSoftwareKeypressEvent(Event.NOT_A_CODE_POINT, keyCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false)) + latinIME.onEvent(Event.createSoftwareKeypressEvent(Event.NOT_A_CODE_POINT, keyCode, 0, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false)) handleMessages() checkConnectionConsistency() } diff --git a/layouts.md b/layouts.md index e7191082d..fed2c015e 100644 --- a/layouts.md +++ b/layouts.md @@ -22,10 +22,10 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm * Allows more flexibility than the simple format, e.g. changing keys depending on input type, shift state or layout direction * You can use character layouts from [FlorisBoard](https://github.com/florisboard/florisboard/blob/master/CONTRIBUTING.md#adding-the-layout) * Support is not 100% there yet, notably `kana_selector` and `char_width_selector` do not work. +* Lines _starting_ with `//` are ignored. * There is no need for specifying a `code`, it will be determined from the label automatically * You can still specify it, but it's only necessary if you want key label and code to be different (please avoid contributing layout with unnecessary codes to HeliBoard) * Note that not all _special codes_ (negative numbers) from FlorisBoard are supported -* More details on the formal will be provided. For now you can check other layouts, often you just need to copy lines and change the labels. * Key classes: specified with `$`, usually you can omit them in HeliBoard * `text_key`: normal key, default * `auto_text_key`: used in FlorisBoard for a key that changes text case when shift is enabled, HeliBoard does that anyway unless disabled with a _labelFlag_ @@ -48,6 +48,9 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm * There are some more values, but they do nothing * `code`: code point that is entered when the key is pressed, determined from the label by default, not available for `multi_text_key` * There are special negative values available, e.g. the ones used by functional keys, see [KeyCode.kt](/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt). There are several not yet supported key codes in there, you can see in the function `checkAndConvertCode` which ones are working. + * Special notes for the modifier keys `CTRL`, `ALT`, `FN`, `META` + * Currently there is no special lock-treatment, so you need to hold the key and press another key at the same time (like on a hardware keyboard) + * this means you should avoid putting popups on modifier keys (or press the other key quickly) * `codePoints`: when multiple code points should be entered, only available for `multi_text_key` * `label`: text to display on the key, determined from code if empty * There are some special values, see the [label section](#labels) From 4a6ef143091bfd69906bbf4b10e40faba9acc30a Mon Sep 17 00:00:00 2001 From: Helium314 Date: Fri, 17 May 2024 18:36:41 +0200 Subject: [PATCH 20/58] after upgrade, disable new toolbar keys if the toolbar is customized --- .../java/helium314/keyboard/latin/utils/ToolbarUtils.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt index 3ec59fcb7..f3d78a804 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt @@ -128,18 +128,19 @@ val defaultClipboardToolbarPref by lazy { /** add missing keys, typically because a new key has been added */ fun upgradeToolbarPrefs(prefs: SharedPreferences) { - upgradeToolbarPref(prefs, Settings.PREF_TOOLBAR_KEYS, defaultToolbarPref, "true") - upgradeToolbarPref(prefs, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, defaultClipboardToolbarPref, "false") + upgradeToolbarPref(prefs, Settings.PREF_TOOLBAR_KEYS, defaultToolbarPref) + upgradeToolbarPref(prefs, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, defaultClipboardToolbarPref) } -private fun upgradeToolbarPref(prefs: SharedPreferences, pref: String, default: String, defaultEnabled: String) { +private fun upgradeToolbarPref(prefs: SharedPreferences, pref: String, default: String) { + if (!prefs.contains(pref)) return val list = prefs.getString(pref, default)!!.split(";").toMutableList() val splitDefault = defaultToolbarPref.split(";") if (list.size == splitDefault.size) return splitDefault.forEach { entry -> val keyWithComma = entry.substringBefore(",") + "," if (list.none { it.startsWith(keyWithComma) }) - list.add("${keyWithComma}$defaultEnabled") + list.add("${keyWithComma}false") } // likely not needed, but better prepare for possibility of key removal list.removeAll { From ec72b49ead8d319368e009c1064998df41547fa1 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Fri, 17 May 2024 19:04:39 +0200 Subject: [PATCH 21/58] move contribution guidelines into default file CONTRIBUTING.md --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- .github/ISSUE_TEMPLATE/other.md | 2 ++ .github/ISSUE_TEMPLATE/spell_checker.md | 2 ++ .github/PULL_REQUEST_TEMPLATE.md | 4 ++- CONTRIBUTING.md | 39 ++++++++++++++++++++ README.md | 44 +++-------------------- 7 files changed, 53 insertions(+), 42 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 907816de7..ab2de7e01 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,7 +10,7 @@ tl;dr: * a single issue per topic * reduce screenshot size - + **Describe the bug** diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e3fbb2eb1..8a6c702ca 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -10,7 +10,7 @@ tl;dr: * a single issue per topic * reduce screenshot size - + **Is your feature request related to a problem? Please describe.** diff --git a/.github/ISSUE_TEMPLATE/other.md b/.github/ISSUE_TEMPLATE/other.md index 0b93d0d59..3956154a8 100644 --- a/.github/ISSUE_TEMPLATE/other.md +++ b/.github/ISSUE_TEMPLATE/other.md @@ -8,3 +8,5 @@ tl;dr: * search for duplicates, also in closed issues * a single issue per topic * reduce screenshot size + + diff --git a/.github/ISSUE_TEMPLATE/spell_checker.md b/.github/ISSUE_TEMPLATE/spell_checker.md index 39706e3d2..629a69749 100644 --- a/.github/ISSUE_TEMPLATE/spell_checker.md +++ b/.github/ISSUE_TEMPLATE/spell_checker.md @@ -12,3 +12,5 @@ tl;dr: Make sure you actually enabled HeliBoard spell checker. Usually it can be found in System Settings -> System -> Languages -> Advanced -> Spell Checker, but this may depend on Android version. Note that the menu when tapping a word underlined in red is coming form the Android system and is not under control of HeliBoard. + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 770660849..8756a0530 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,4 @@ -See the contributing section in the readme for more detailed guideline: https://github.com/Helium314/HeliBoard?tab=readme-ov-file#guidelines +See the contributing readme for more detailed guideline: https://github.com/Helium314/HeliBoard/blob/main/CONTRIBUTING.md tl;dr (you should still read the full list though): * make sure it's wanted * a single thing only @@ -12,3 +12,5 @@ Further * When the PR contains "fixes" , the related issue will be linked and automatically closed if the PR is merged (also works for other words like "fix", "resolve", "resolves", "closes", ...) * If you add a keyboard layout, make sure you have read https://github.com/Helium314/HeliBoard/blob/main/layouts.md#adding-new-layouts--languages * Please avoid force-pushing when doing changes. This way it's not possible which parts have changed since the previous state. + +(please remove the template text before submitting the PR) \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..71ebd725f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,39 @@ +# Getting Started + +HeliBoard project is based on Gradle and Android Gradle Plugin. To get started, you can install [Android Studio](https://developer.android.com/studio), and import project 'from Version Control / Git / Github' by providing this git repository [URL](https://github.com/Helium314/HeliBoard) (or git SSH [URL](git@github.com:Helium314/heliboard.git)). +Of course you can also use any other compatible IDE, or work with text editor and command line. + +Once everything is up correctly, you're ready to go! + +# Guidelines + +HeliBoard is a complex application, when contributing, you must take a step back and make sure your contribution: +- **Is actually wanted**. Best check related open issues before you start working on a PR. Issues with "PR" and "contributor needed" labels are accepted, but still it would be good if you announced that you are working on it. + If there is no issue related to your intended contribution, it's a good idea to open a new one to avoid disappointment of the contribution not being accepted. For small changes or fixing obvious bugs this step is not necessary. +- **Is only about a single thing**. Mixing unrelated contributions into a single PR is hard to review and can get messy. +- **Is finished or a draft**. When you keep changing the PR without reviewer's feedback, any attempt to review it is doomed and a waste of time. Better mark it as a draft in this case. +- **Has a proper description**. What your contribution does is usually less obvious to reviewers than for yourself. A good description helps _a lot_ for understanding what is going on, and for separating wanted from unintended changes in behavior. +- **Uses already in-place mechanism and take advantage of them**. In other terms, does not reinvent the wheel or uses shortcuts that could alter the consistency of the existing code. The contribution should only add as little complexity as necessary, the code is overly complicated already 😶. +- **Has a low footprint**. Some parts of the code are executed very frequently, and the keyboard should stay responsive even on older devices. +- **Does not bring any non-free code or proprietary binary blobs**. This also applies to code/binaries with unknown licenses. Make sure you do not introduce any closed-source library from Google. + If your contribution contains code that is not your own, provide a link to the source. +- **Complies with the user privacy principle HeliBoard follows**. + +(and please leave dependency upgrades to the maintainers, unless it's an actual security issue) +In addition to previous elements, HeliBoard must stick to [F-Droid inclusion guidelines](https://f-droid.org/docs/Inclusion_Policy/). + +# Adding Layouts + +See [layouts.md](layouts.md#adding-new-layouts--languages) for how to add new layouts to the app. + +# Update Emojis + +See make-emoji-keys tool [README](tools/make-emoji-keys/README.md). + +# Update List of Existing Dictionaries + +See make-dict-list tool [README](tools/make-dict-list/README.md). + +# Translations +Translations can be added using [Weblate](https://translate.codeberg.org/projects/heliboard/). You will need an account to update translations and add languages. Add the language you want to translate to in Languages -> Manage translated languages in the top menu bar. +Updating translations in a PR will not be accepted, as it may cause conflicts with Weblate translations. diff --git a/README.md b/README.md index 669a0a879..bedcddbfd 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ Does not use internet permission, and thus is 100% offline. * [Hidden Functionality](#hidden-functionality) - [Contributing](#contributing-) * [Reporting Issues](#reporting-issues) - * [Translation](#translation) + * [Translations](#translations) * [Dictionary Creation](#dictionary-creation) - * [Code Contribution](#code-contribution) + * [Code Contribution](CONTRIBUTING.md) - [To-do](#to-do) - [License](#license) - [Credits](#credits) @@ -38,7 +38,7 @@ Does not use internet permission, and thus is 100% offline.

  • Glide typing (only with closed source library ☹️)
    • library not included in the app, as there is no compatible open source library available
    • -
    • can be extracted from GApps packages ("swypelibs"), or downloaded here
    • +
    • can be extracted from GApps packages ("swypelibs"), or downloaded here (click on the file and then "raw" or the tiny download button)
  • Clipboard history
  • One-handed mode
  • @@ -110,7 +110,7 @@ Note that issues that that ignore part of the issue template will likely get tre If you're interested, you can read the following useful text about effective bug reporting (a bit longer read): https://www.chiark.greenend.org.uk/~sgtatham/bugs.html -## Translation +## Translations Translations can be added using [Weblate](https://translate.codeberg.org/projects/heliboard/). You will need an account to update translations and add languages. Add the language you want to translate to in Languages -> Manage translated languages in the top menu bar. Updating translations in a PR will not be accepted, as it may cause conflicts with Weblate translations. @@ -119,41 +119,7 @@ There will not be any further dictionaries bundled in this app. However, you can To create or update a dictionary for your language, you can use [this tool](https://github.com/remi0s/aosp-dictionary-tools). You will need a wordlist, as described [here](https://codeberg.org/Helium314/aosp-dictionaries/src/branch/main/wordlists/sample.combined) and in the repository readme. ## Code Contribution - -### Getting Started - -HeliBoard project is based on Gradle and Android Gradle Plugin. To get started, you can install [Android Studio](https://developer.android.com/studio), and import project 'from Version Control / Git / Github' by providing this git repository [URL](https://github.com/Helium314/HeliBoard) (or git SSH [URL](git@github.com:Helium314/heliboard.git)). -Of course you can also use any other compatible IDE, or work with text editor and command line. - -Once everything is up correctly, you're ready to go! - -### Guidelines - -HeliBoard is a complex application, when contributing, you must take a step back and make sure your contribution: -- **Is actually wanted**. Best check related open issues before you start working on a PR. Issues with "PR" and "contributor needed" labels are accepted, but still it would be good if you announced that you are working on it. -If there is no issue related to your intended contribution, it's a good idea to open a new one to avoid disappointment of the contribution not being accepted. For small changes or fixing obvious bugs this step is not necessary. -- **Is only about a single thing**. Mixing unrelated contributions into a single PR is hard to review and can get messy. -- **Is finished or a draft**. When you keep changing the PR without reviewer's feedback, any attempt to review it is doomed and a waste of time. Better mark it as a draft in this case. -- **Has a proper description**. What your contribution does is usually less obvious to reviewers than for yourself. A good description helps _a lot_ for understanding what is going on, and for separating wanted from unintended changes in behavior. -- **Uses already in-place mechanism and take advantage of them**. In other terms, does not reinvent the wheel or uses shortcuts that could alter the consistency of the existing code. -- **Has a low footprint**. Some parts of the code are executed very frequently, and the keyboard should stay responsive even on older devices. -- **Does not bring any non-free code or proprietary binary blobs**. This also applies to code/binaries with unknown licenses. Make sure you do not introduce any closed-source library from Google. -If your contribution contains code that is not your own, provide a link to the source. -- **Complies with the user privacy principle HeliBoard follows**. - -In addition to previous elements, HeliBoard must stick to [F-Droid inclusion guidelines](https://f-droid.org/docs/Inclusion_Policy/). - -### Adding Layouts - -See [layouts.md](layouts.md#adding-new-layouts--languages) for how to add new layouts to the app. - -### Update Emojis - -See make-emoji-keys tool [README](tools/make-emoji-keys/README.md). - -### Update List of Existing Dictionaries - -See make-dict-list tool [README](tools/make-dict-list/README.md). +See [Contribution Guidelines](CONTRIBUTING.md) # To-do __Planned features and improvements:__ From 8ea693d92e1c2db0808fd4d8089b531da3b9326d Mon Sep 17 00:00:00 2001 From: Helium314 Date: Fri, 17 May 2024 19:30:52 +0200 Subject: [PATCH 22/58] add a failing test for stuff that should be fixed --- .../keyboard/latin/InputLogicTest.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt index b30d5a1ae..b7189946c 100644 --- a/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt +++ b/app/src/test/java/helium314/keyboard/latin/InputLogicTest.kt @@ -545,6 +545,26 @@ class InputLogicTest { assertEquals("hello ", text) } + @Test fun `autospace in json editor`() { + reset() + chainInput("{\"label\":\"") + assertEquals("{\"label\": \"", text) + input('c') + assertEquals("{\"label\": \"c", text) + } + + // todo: the test fails because assert wants it as it's in app + // but actually the "failing text" is the wanted behavior -> how to get it in app? + @Test fun `autospace in json editor 2`() { + reset() + setInputType(InputType.TYPE_CLASS_TEXT and InputType.TYPE_TEXT_FLAG_MULTI_LINE) + setText("[\n[\n{ \"label\": \"a\" },\n") + chainInput("{\"label\":\"") + assertEquals("[\n[\n{ \"label\": \"a\" },\n{\"label\":\"", text) + input('c') + assertEquals("[\n[\n{ \"label\": \"a\" },\n{\"label\":\" c", text) + } + // ------- helper functions --------- // should be called before every test, so the same state is guaranteed From e54d8161262f15d10d8b7b46408d9008bfd8523e Mon Sep 17 00:00:00 2001 From: tenextractor <139619642+tenextractor@users.noreply.github.com> Date: Fri, 17 May 2024 23:27:23 +0530 Subject: [PATCH 23/58] Add Mansi layout (#791) --- .../main/assets/language_key_texts/mns.txt | 20 ++++++++ app/src/main/assets/layouts/mansi_north.txt | 47 +++++++++++++++++++ .../keyboard_parser/LocaleKeyboardInfos.kt | 1 + .../keyboard/latin/common/LocaleUtils.kt | 2 +- .../keyboard/latin/utils/ScriptUtils.kt | 2 +- app/src/main/res/values-ru/strings.xml | 2 + app/src/main/res/values/donottranslate.xml | 3 ++ app/src/main/res/values/strings.xml | 3 ++ app/src/main/res/xml/method.xml | 9 ++++ 9 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 app/src/main/assets/language_key_texts/mns.txt create mode 100644 app/src/main/assets/layouts/mansi_north.txt diff --git a/app/src/main/assets/language_key_texts/mns.txt b/app/src/main/assets/language_key_texts/mns.txt new file mode 100644 index 000000000..04674e713 --- /dev/null +++ b/app/src/main/assets/language_key_texts/mns.txt @@ -0,0 +1,20 @@ +[popup_keys] +ё ё̄ +у ӯ ӱ +к қ +е е̄ +н ӈ +г ғ +х ҳ +ы ы̄ +а а̄ ӓ +о о̄ ӧ +ж җ +э э̄ +я я̄ +ч ҷ +и ӣ +ю ю̄ + +[labels] +alphabet: АБВ \ No newline at end of file diff --git a/app/src/main/assets/layouts/mansi_north.txt b/app/src/main/assets/layouts/mansi_north.txt new file mode 100644 index 000000000..efdbd965e --- /dev/null +++ b/app/src/main/assets/layouts/mansi_north.txt @@ -0,0 +1,47 @@ +ё +ы̄ +ӯ +а̄ +е̄ +ӈ +о̄ +я̄ +ю̄ +ӣ +э̄ +ё̄ + +й +ц +у +к +е +н +г +ш +щ +з +х +ъ + +ф +ы +в +а +п +р +о +л +д +ж +э + +я +ч +с +м +и +т +ь +б +ю \ No newline at end of file diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/LocaleKeyboardInfos.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/LocaleKeyboardInfos.kt index 04a406a67..0cbd84712 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/LocaleKeyboardInfos.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/LocaleKeyboardInfos.kt @@ -52,6 +52,7 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) { "hy", "ar", "be", "fa", "hi", "lo", "mr", "ne", "th", "ur" -> Key.LABEL_FLAGS_FONT_NORMAL "km", "ml", "si", "ta", "te" -> Key.LABEL_FLAGS_FONT_NORMAL or Key.LABEL_FLAGS_AUTO_X_SCALE "kn" -> Key.LABEL_FLAGS_FONT_NORMAL or Key.LABEL_FLAGS_AUTO_X_SCALE or Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO + "mns" -> Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO else -> 0 } diff --git a/app/src/main/java/helium314/keyboard/latin/common/LocaleUtils.kt b/app/src/main/java/helium314/keyboard/latin/common/LocaleUtils.kt index fa2d81c78..e33efc73e 100644 --- a/app/src/main/java/helium314/keyboard/latin/common/LocaleUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/common/LocaleUtils.kt @@ -190,7 +190,7 @@ object LocaleUtils { fun getLocaleDisplayNameInLocale(locale: Locale, resources: Resources, displayLocale: Locale): String { val languageTag = locale.toLanguageTag() if (languageTag == SubtypeLocaleUtils.NO_LANGUAGE) return resources.getString(R.string.subtype_no_language) - if (locale.script() != locale.language.constructLocale().script() || locale.language == "xdq") { + if (locale.script() != locale.language.constructLocale().script() || locale.language == "mns" || locale.language == "xdq") { val resId = resources.getIdentifier( "subtype_${languageTag.replace("-", "_")}", "string", diff --git a/app/src/main/java/helium314/keyboard/latin/utils/ScriptUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/ScriptUtils.kt index 5d55fab90..6daec94bc 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/ScriptUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/ScriptUtils.kt @@ -165,7 +165,7 @@ object ScriptUtils { "ar", "ur", "fa" -> SCRIPT_ARABIC "hy" -> SCRIPT_ARMENIAN "bn" -> SCRIPT_BENGALI - "sr", "mk", "ru", "uk", "mn", "be", "kk", "ky", "bg", "xdq", "cv", "mhr" -> SCRIPT_CYRILLIC + "sr", "mk", "ru", "uk", "mn", "be", "kk", "ky", "bg", "xdq", "cv", "mhr", "mns" -> SCRIPT_CYRILLIC "ka" -> SCRIPT_GEORGIAN "el" -> SCRIPT_GREEK "iw" -> SCRIPT_HEBREW diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index f0f05dcd7..d660f567f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -53,12 +53,14 @@ Английский (США) Испанский (США) хинглиш + Мансийский кайтагский сербский (латиница) Английский (Великобритания) (%s) Английский (США) (%s) Испанский (США) (%s) Хинглиш (%s) + Мансийский (%s) Кайтагский (%s) Сербский (%s) %s (Традиционная) diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 351361166..184afc61c 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -14,6 +14,7 @@ en-GB es-US hi-Latn + mns xdq sr-Latn @@ -21,10 +22,12 @@ hi-Latn + mns xdq sr-Latn Hinglish + Ма̄ньси Хайдаҡьан Srpski diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4c36c256c..420e60636 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -279,6 +279,8 @@ Spanish (US) Hinglish + + Mansi Kaitag + Mansi (%s) Kaitag (%s) diff --git a/app/src/main/res/xml/method.xml b/app/src/main/res/xml/method.xml index c3c1c96c5..2a044259e 100644 --- a/app/src/main/res/xml/method.xml +++ b/app/src/main/res/xml/method.xml @@ -737,6 +737,15 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian,EmojiCapable" android:isAsciiCapable="false" /> + Date: Fri, 17 May 2024 22:05:36 +0200 Subject: [PATCH 24/58] remove some old attrs stuff --- .../keyboard/keyboard/KeyboardView.java | 33 +--------------- .../keyboard/keyboard/MainKeyboardView.java | 38 +------------------ .../GestureFloatingTextDrawingPreview.java | 6 --- .../internal/GestureStrokeDrawingParams.java | 9 +---- .../GestureStrokeRecognitionParams.java | 11 ------ .../internal/GestureTrailDrawingParams.java | 7 +--- .../SlidingKeyInputDrawingPreview.java | 8 +--- app/src/main/res/values/attrs.xml | 11 ------ 8 files changed, 6 insertions(+), 117 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java index 6eba7a46b..38ecfb9b8 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java @@ -39,41 +39,12 @@ import helium314.keyboard.latin.settings.Settings; import helium314.keyboard.latin.suggestions.MoreSuggestions; import helium314.keyboard.latin.suggestions.PopupSuggestionsView; -import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.TypefaceUtils; import java.util.HashSet; -/** - * A view that renders a virtual {@link Keyboard}. - * - * @attr ref R.styleable#KeyboardView_keyBackground - * @attr ref R.styleable#KeyboardView_functionalKeyBackground - * @attr ref R.styleable#KeyboardView_spacebarBackground - * @attr ref R.styleable#KeyboardView_spacebarIconWidthRatio - * @attr ref R.styleable#Keyboard_Key_keyLabelFlags - * @attr ref R.styleable#KeyboardView_keyHintLetterPadding - * @attr ref R.styleable#KeyboardView_keyPopupHintLetter - * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding - * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintPadding - * @attr ref R.styleable#KeyboardView_keyTextShadowRadius - * @attr ref R.styleable#KeyboardView_verticalCorrection - * @attr ref R.styleable#Keyboard_Key_keyTypeface - * @attr ref R.styleable#Keyboard_Key_keyLetterSize - * @attr ref R.styleable#Keyboard_Key_keyLabelSize - * @attr ref R.styleable#Keyboard_Key_keyLargeLetterRatio - * @attr ref R.styleable#Keyboard_Key_keyLargeLabelRatio - * @attr ref R.styleable#Keyboard_Key_keyHintLetterRatio - * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintRatio - * @attr ref R.styleable#Keyboard_Key_keyHintLabelRatio - * @attr ref R.styleable#Keyboard_Key_keyLabelOffCenterRatio - * @attr ref R.styleable#Keyboard_Key_keyHintLabelOffCenterRatio - * @attr ref R.styleable#Keyboard_Key_keyPreviewTextRatio - * @attr ref R.styleable#Keyboard_Key_keyTextColorDisabled - * @attr ref R.styleable#Keyboard_Key_keyTextShadowColor - * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintInactivatedColor - * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintActivatedColor - */ +/** A view that renders a virtual {@link Keyboard}. */ +// todo: this ThemeStyle-dependent stuff really should not be in here! public class KeyboardView extends View { // XML attributes private final KeyVisualAttributes mKeyVisualAttributes; diff --git a/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java index f18905b6b..6ebcfb778 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/MainKeyboardView.java @@ -63,43 +63,7 @@ import java.util.Locale; import java.util.WeakHashMap; -/** - * A view that is responsible for detecting key presses and touch movements. - * - * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio - * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowRadius - * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor - * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator - * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator - * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator - * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance - * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime - * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance - * @attr ref R.styleable#MainKeyboardView_keySelectionByDraggingFinger - * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout - * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval - * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout - * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout - * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout - * @attr ref R.styleable#MainKeyboardView_keyPreviewLayout - * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset - * @attr ref R.styleable#MainKeyboardView_popupKeysKeyboardLayout - * @attr ref R.styleable#MainKeyboardView_popupKeysKeyboardForActionLayout - * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha - * @attr ref R.styleable#MainKeyboardView_showPopupKeysKeyboardAtTouchPoint - * @attr ref R.styleable#MainKeyboardView_gestureFloatingPreviewTextLingerTimeout - * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping - * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold - * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration - * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom - * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo - * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom - * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo - * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance - * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime - * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold - * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration - */ +/** A view that is responsible for detecting key presses and touch movements. */ public final class MainKeyboardView extends KeyboardView implements DrawingProxy, PopupKeysPanel.Controller { private static final String TAG = MainKeyboardView.class.getSimpleName(); diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/GestureFloatingTextDrawingPreview.java b/app/src/main/java/helium314/keyboard/keyboard/internal/GestureFloatingTextDrawingPreview.java index 40a565844..b2f5d69a7 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/GestureFloatingTextDrawingPreview.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/GestureFloatingTextDrawingPreview.java @@ -27,12 +27,6 @@ /** * The class for single gesture preview text. The class for multiple gesture preview text will be * derived from it. - * - * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewTextSize - * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewTextOffset - * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewHorizontalPadding - * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewVerticalPadding - * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewRoundRadius */ public class GestureFloatingTextDrawingPreview extends AbstractDrawingPreview { protected static final class GesturePreviewTextParams { diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/GestureStrokeDrawingParams.java b/app/src/main/java/helium314/keyboard/keyboard/internal/GestureStrokeDrawingParams.java index 113653be3..134f22295 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/GestureStrokeDrawingParams.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/GestureStrokeDrawingParams.java @@ -10,14 +10,7 @@ import helium314.keyboard.latin.R; -/** - * This class holds parameters to control how a gesture stroke is sampled and drawn on the screen. - * - * @attr ref R.styleable#MainKeyboardView_gestureTrailMinSamplingDistance - * @attr ref R.styleable#MainKeyboardView_gestureTrailMaxInterpolationAngularThreshold - * @attr ref R.styleable#MainKeyboardView_gestureTrailMaxInterpolationDistanceThreshold - * @attr ref R.styleable#MainKeyboardView_gestureTrailMaxInterpolationSegments - */ +/** This class holds parameters to control how a gesture stroke is sampled and drawn on the screen. */ public final class GestureStrokeDrawingParams { public final double mMinSamplingDistance; // in pixel public final double mMaxInterpolationAngularThreshold; // in radian diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/GestureStrokeRecognitionParams.java b/app/src/main/java/helium314/keyboard/keyboard/internal/GestureStrokeRecognitionParams.java index 4e4101d1f..51352af1f 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/GestureStrokeRecognitionParams.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/GestureStrokeRecognitionParams.java @@ -14,17 +14,6 @@ /** * This class holds parameters to control how a gesture stroke is sampled and recognized. * This class also has parameters to distinguish gesture input events from fast typing events. - * - * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping - * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold - * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration - * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom - * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo - * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom - * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo - * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance - * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime - * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold */ public final class GestureStrokeRecognitionParams { // Static threshold for gesture after fast typing diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/GestureTrailDrawingParams.java b/app/src/main/java/helium314/keyboard/keyboard/internal/GestureTrailDrawingParams.java index 00b89699e..5f86f3644 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/GestureTrailDrawingParams.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/GestureTrailDrawingParams.java @@ -14,15 +14,10 @@ /** * This class holds parameters to control how a gesture trail is drawn and animated on the screen. - * + *

    * On the other hand, {@link GestureStrokeDrawingParams} class controls how each gesture stroke is * sampled and interpolated. This class controls how those gesture strokes are displayed as a * gesture trail and animated on the screen. - * - * @attr ref R.styleable#MainKeyboardView_gestureTrailFadeoutStartDelay - * @attr ref R.styleable#MainKeyboardView_gestureTrailFadeoutDuration - * @attr ref R.styleable#MainKeyboardView_gestureTrailUpdateInterval - * @attr ref R.styleable#MainKeyboardView_gestureTrailWidth */ final class GestureTrailDrawingParams { private static final int FADEOUT_START_DELAY_FOR_DEBUG = 2000; // millisecond diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/SlidingKeyInputDrawingPreview.java b/app/src/main/java/helium314/keyboard/keyboard/internal/SlidingKeyInputDrawingPreview.java index 0dc78a8fa..23381fa5a 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/SlidingKeyInputDrawingPreview.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/SlidingKeyInputDrawingPreview.java @@ -19,13 +19,7 @@ import helium314.keyboard.latin.common.CoordinateUtils; import helium314.keyboard.latin.settings.Settings; -/** - * Draw rubber band preview graphics during sliding key input. - * - * @attr ref R.styleable#MainKeyboardView_slidingKeyInputPreviewWidth - * @attr ref R.styleable#MainKeyboardView_slidingKeyInputPreviewBodyRatio - * @attr ref R.styleable#MainKeyboardView_slidingKeyInputPreviewShadowRatio - */ +/** Draw rubber band preview graphics during sliding key input. */ public final class SlidingKeyInputDrawingPreview extends AbstractDrawingPreview { private final float mPreviewBodyRadius; diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 463b87b1d..d8ef5903e 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -287,17 +287,6 @@ - - - - - - - - - - @@ -967,7 +967,7 @@ android:imeSubtypeLocale="ta_SG" android:languageTag="ta-SG" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,EmojiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,NoShiftKey,EmojiCapable" android:isAsciiCapable="false" /> Date: Sat, 18 May 2024 14:52:12 +0200 Subject: [PATCH 28/58] remove label processing from KeyboardParser --- .../internal/keyboard_parser/KeyboardParser.kt | 18 +++++------------- .../keyboard_parser/floris/TextKeyData.kt | 4 +++- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index 015d33200..30a5f42f3 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -103,14 +103,13 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co val functionalKeysFromBottom = functionalKeysBottom.getOrNull(i - bottomIndexOffset) ?: emptyList() functionalKeys.add(getFunctionalKeysBySide(functionalKeysFromTop, functionalKeysFromBottom)) - row.mapNotNull { key -> + row.map { key -> val extraFlags = if (key.label.length > 2 && key.label.codePointCount(0, key.label.length) > 2 && !isEmoji(key.label)) Key.LABEL_FLAGS_AUTO_X_SCALE else 0 - val keyData = key.processFunctionalKeys() ?: return@mapNotNull null // all keys could actually be functional keys... if (DebugFlags.DEBUG_ENABLED) - Log.d(TAG, "adding key ${keyData.label}, ${keyData.code}") - keyData.toKeyParams(params, defaultLabelFlags or extraFlags) + Log.d(TAG, "adding key ${key.label}, ${key.code}") + key.toKeyParams(params, defaultLabelFlags or extraFlags) } } return setReasonableWidths(baseKeyParams, functionalKeys) @@ -256,18 +255,11 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co // functional keys from top rows are the outermost, if there are some in the same row functionalKeysFromTopLeft.addAll(functionalKeysFromBottomLeft) functionalKeysFromBottomRight.addAll(functionalKeysFromTopRight) - val functionalKeysLeft = functionalKeysFromTopLeft.mapNotNull { it.processFunctionalKeys()?.toKeyParams(params) } - val functionalKeysRight = functionalKeysFromBottomRight.mapNotNull { it.processFunctionalKeys()?.toKeyParams(params) } + val functionalKeysLeft = functionalKeysFromTopLeft.map { it.toKeyParams(params) } + val functionalKeysRight = functionalKeysFromBottomRight.map { it.toKeyParams(params) } return functionalKeysLeft to functionalKeysRight } - // todo: try moving defaultLabelFlags and layoutInfo into KeyboardParams - private fun KeyData.processFunctionalKeys(): KeyData? = when (label) { - // todo: why defaultLabelFlags exactly here? is this for armenian or bengali period labels? try removing also check in holo theme - KeyLabel.PERIOD -> copy(newLabelFlags = labelFlags or defaultLabelFlags) - else -> this - } - private fun addNumberRowOrPopupKeys(baseKeys: MutableList>) { if (!params.mId.mNumberRowEnabled && params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) { // replace first symbols row with number row, but use the labels as popupKeys diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index 931ed3931..5c5e66e0c 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -459,7 +459,9 @@ sealed interface KeyData : AbstractKeyData { private fun getAdditionalLabelFlags(params: KeyboardParams): Int { return when (label) { KeyLabel.ALPHA, KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL -> Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR - KeyLabel.PERIOD, KeyLabel.COMMA -> Key.LABEL_FLAGS_HAS_POPUP_HINT // todo: period also has defaultLabelFlags -> when is this relevant? + KeyLabel.COMMA -> Key.LABEL_FLAGS_HAS_POPUP_HINT + // essentially this only changes the appearance of the armenian period key in holo theme + KeyLabel.PERIOD -> Key.LABEL_FLAGS_HAS_POPUP_HINT and if (params.mId.isAlphabetKeyboard) params.mLocaleKeyboardInfos.labelFlags else 0 KeyLabel.ACTION -> { Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_AUTO_X_SCALE or Key.LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR or From 2cb2a5da21ef7f7f7ceec42fe5b39349fb7bab44 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 18 May 2024 15:01:30 +0200 Subject: [PATCH 29/58] sort HeliBoard subtypes by name in InputMethodPicker --- .../java/helium314/keyboard/latin/utils/InputMethodPicker.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/helium314/keyboard/latin/utils/InputMethodPicker.kt b/app/src/main/java/helium314/keyboard/latin/utils/InputMethodPicker.kt index 70e222a72..33ac3d5d5 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/InputMethodPicker.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/InputMethodPicker.kt @@ -25,7 +25,8 @@ fun createInputMethodPickerDialog(latinIme: LatinIME, richImm: RichInputMethodMa val enabledSubtypes = mutableListOf>() var currentSubtypeIndex = 0 enabledImis.forEach { imi -> - val subtypes = richImm.getEnabledInputMethodSubtypeList(imi, true) + val subtypes = if (imi != thisImi) richImm.getEnabledInputMethodSubtypeList(imi, true) + else richImm.getEnabledInputMethodSubtypeList(imi, true).sortedBy { it.displayName(latinIme).toString() } if (subtypes.isEmpty()) { enabledSubtypes.add(imi to null) } else { From 1f0f7f6b7c8dc6b9d10da9410da42a30a929c55f Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 18 May 2024 15:59:47 +0200 Subject: [PATCH 30/58] simplify setting touchPositionCorrectionData --- .../keyboard/internal/KeyboardBuilder.kt | 6 ++--- .../keyboard/internal/KeyboardParams.java | 26 +++---------------- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt index 5171199d8..73f6c01b4 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt @@ -53,6 +53,8 @@ open class KeyboardBuilder(protected val mContext: Context, try { setupParams() keysInRows = KeyboardParser(mParams, mContext).parseLayout() + if (keysInRows.size != 4) // that was effectively the default for OpenBoard + mParams.mTouchPositionCorrection.load(mContext.resources.getStringArray(R.array.touch_position_correction_data_default)) determineAbsoluteValues() } catch (e: Exception) { Log.e(TAG, "error parsing layout $id ${id.mElementId}", e) @@ -63,9 +65,6 @@ open class KeyboardBuilder(protected val mContext: Context, } private fun setupParams() { - val sv = Settings.getInstance().current - val layoutName = mParams.mId.mSubtype.keyboardLayoutSetName - // previously was false for nordic and serbian_qwertz, true for all others // todo: add setting? maybe users want it in a custom layout mParams.mAllowRedundantPopupKeys = mParams.mId.mElementId != KeyboardId.ELEMENT_SYMBOLS @@ -73,6 +72,7 @@ open class KeyboardBuilder(protected val mContext: Context, mParams.mProximityCharsCorrectionEnabled = mParams.mId.mElementId == KeyboardId.ELEMENT_ALPHABET || (mParams.mId.isAlphabetKeyboard && !mParams.mId.mSubtype.hasExtraValue(Constants.Subtype.ExtraValue.NO_SHIFT_PROXIMITY_CORRECTION)) + val sv = Settings.getInstance().current addLocaleKeyTextsToParams(mContext, mParams, sv.mShowMorePopupKeys) mParams.mPopupKeyTypes.addAll(sv.mPopupKeyTypes) // add label source only if popup key type enabled diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java index e3c9fd911..9d39c79d6 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java @@ -20,7 +20,6 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode; import helium314.keyboard.latin.R; import helium314.keyboard.latin.settings.Settings; -import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.ResourceUtils; import java.util.ArrayList; @@ -98,8 +97,7 @@ public class KeyboardParams { public boolean mProximityCharsCorrectionEnabled; @NonNull - public final TouchPositionCorrection mTouchPositionCorrection = - new TouchPositionCorrection(); + public final TouchPositionCorrection mTouchPositionCorrection = new TouchPositionCorrection(); // Comparator to sort {@link Key}s from top-left to bottom-right order. private static final Comparator ROW_COLUMN_COMPARATOR = (lhs, rhs) -> { @@ -269,29 +267,13 @@ public void readAttributes(final Context context, @Nullable final AttributeSet a mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0); mIconsSet.loadIcons(keyboardAttr); - // todo: this clashes with the other way of doing it... now both moved here, in same order - // need to check OpenBoard how it was done there - // also, popup keys should have an empty array + // touchPositionResId currently is 0 for popups, and touch_position_correction_data_holo for others final int touchPositionResId = keyboardAttr.getResourceId(R.styleable.Keyboard_touchPositionCorrectionData, 0); if (touchPositionResId != 0) { - final String[] data = context.getResources().getStringArray(touchPositionResId); + final int actualId = mId.isAlphabetKeyboard() ? touchPositionResId : R.array.touch_position_correction_data_default; + final String[] data = context.getResources().getStringArray(actualId); mTouchPositionCorrection.load(data); } - // so this is the new way: - final int touchPositionResIdNew; - if (mId.isAlphabetKeyboard()) { - touchPositionResIdNew = switch (mId.mSubtype.getKeyboardLayoutSetName()) { - case "armenian_phonetic", "khmer", "lao", "malayalam", "pcqwerty", "thai" -> R.array.touch_position_correction_data_default; - default -> R.array.touch_position_correction_data_holo; - }; - } else touchPositionResIdNew = R.array.touch_position_correction_data_holo; - if (touchPositionResIdNew != touchPositionResId) { - Log.i("KeyboardParams", "overriding touchPositionCorrection "+touchPositionResId+" with "+touchPositionResIdNew); - if (touchPositionResIdNew != 0) { - final String[] data = context.getResources().getStringArray(touchPositionResIdNew); - mTouchPositionCorrection.load(data); - } - } } finally { keyAttr.recycle(); keyboardAttr.recycle(); From eada9bc51ca6ad880322781ba13360248a15c750 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 18 May 2024 18:37:13 +0200 Subject: [PATCH 31/58] don't store colors immediately when changing only store when user presses ok avoids bugs, most prominently that the color remains when closing the app while in color picker --- .../latin/settings/ColorsSettingsFragment.kt | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/latin/settings/ColorsSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/ColorsSettingsFragment.kt index 2d1db75c4..af7c88adb 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/ColorsSettingsFragment.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/ColorsSettingsFragment.kt @@ -141,7 +141,9 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi picker.showHex(true) picker.showPreview(true) picker.color = initialColor - picker.addColorObserver { observer -> + // without the observer, the color previews in the background don't update + // but storing the pref and resetting on cancel is really bad style, so this is disabled for now +/* picker.addColorObserver { observer -> prefs.edit { putInt(prefPrefix + colorPref, observer.color) } if (!csb.colorSwitch.isChecked) { prefs.edit { putBoolean(prefPrefix + colorPref + Settings.PREF_AUTO_USER_COLOR_SUFFIX, false) } @@ -153,21 +155,23 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi return@addColorObserver } updateColorPreviews() - } + }*/ val builder = AlertDialog.Builder(requireContext()) builder .setTitle(colorPrefName) .setView(picker) - .setNegativeButton(android.R.string.cancel) { _, _ -> - // If the slider is disabled, we simply want to close the dialog when no color is selected. - if (csb.colorSwitch.isChecked) - picker.color = initialColor - } + .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(android.R.string.ok) { _, _ -> - // When the slider is disabled, we want to define the default color as a custom color + prefs.edit { putInt(prefPrefix + colorPref, picker.color) } if (!csb.colorSwitch.isChecked) { - csb.colorSwitch.toggle() - picker.color = initialColor + prefs.edit { putBoolean(prefPrefix + colorPref + Settings.PREF_AUTO_USER_COLOR_SUFFIX, false) } + csb.colorSwitch.setOnCheckedChangeListener(null) + csb.colorSwitch.isChecked = true + csb.colorSummary.text = "" + csb.colorSwitch.setOnCheckedChangeListener(switchListener) + updateColorPreviews() + } else { + updateColorPreviews() } reloadKeyboard(hidden) } @@ -175,10 +179,8 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi if (csb.colorSwitch.isChecked) { // Reset the color and the color picker to their initial state builder.setNeutralButton(R.string.button_default) { _, _ -> + prefs.edit { remove(prefPrefix + colorPref + Settings.PREF_AUTO_USER_COLOR_SUFFIX) } csb.colorSwitch.isChecked = false - val resetColor = Settings.readUserColor(prefs, requireContext(), colorPref, isNight) - picker.color = resetColor - csb.colorSwitch.toggle() } } val dialog = builder.create() From 77f03b07565b3a621099f80ee51ff28726e380c2 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 18 May 2024 20:27:22 +0200 Subject: [PATCH 32/58] allow customizing all colors, fixes #529 --- README.md | 1 - .../keyboard/keyboard/KeyboardTheme.kt | 12 +- .../keyboard/keyboard/KeyboardView.java | 5 +- .../helium314/keyboard/latin/common/Colors.kt | 65 ++++++----- .../latin/settings/ColorsSettingsFragment.kt | 108 +++++++++++++++--- .../keyboard/latin/settings/Settings.java | 5 +- app/src/main/res/layout/color_settings.xml | 23 ++-- app/src/main/res/values/strings.xml | 4 + 8 files changed, 158 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index bedcddbfd..84907492c 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,6 @@ __Planned features and improvements:__ * Additional and customizable key swipe functionality * Some functionality will not be possible when using glide typing * Ability to enter all emojis independent of Android version (optional, #297) -* (limited) support for customizing _all_ internally used colors * Add and enable emoji dictionaries by default (if available for language) * Clearer / more intuitive arrangement of settings * Maybe hide some less used settings by default (similar to color customization) diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardTheme.kt b/app/src/main/java/helium314/keyboard/keyboard/KeyboardTheme.kt index 6594c6f65..00fb9cdc8 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardTheme.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardTheme.kt @@ -12,9 +12,11 @@ import android.os.Build import android.os.Build.VERSION_CODES import androidx.core.content.ContextCompat import helium314.keyboard.latin.R +import helium314.keyboard.latin.common.AllColors import helium314.keyboard.latin.common.Colors import helium314.keyboard.latin.common.DefaultColors import helium314.keyboard.latin.common.DynamicColors +import helium314.keyboard.latin.common.readAllColorsMap import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.ResourceUtils @@ -101,10 +103,11 @@ private constructor(val themeId: Int, @JvmField val mStyleId: Int) { @JvmStatic fun getThemeColors(themeColors: String, themeStyle: String, context: Context, prefs: SharedPreferences): Colors { val hasBorders = prefs.getBoolean(Settings.PREF_THEME_KEY_BORDERS, false) - val useNightImage = Settings.readDayNightPref(prefs, context.resources) && ResourceUtils.isNight(context.resources) - val backgroundImage = Settings.readUserBackgroundImage(context, useNightImage) + val useNightMode = Settings.readDayNightPref(prefs, context.resources) && ResourceUtils.isNight(context.resources) + val backgroundImage = Settings.readUserBackgroundImage(context, useNightMode) return when (themeColors) { - THEME_USER -> DefaultColors( + THEME_USER -> if (prefs.getInt(Settings.PREF_SHOW_MORE_COLORS, 0) == 2) AllColors(readAllColorsMap(prefs, false), themeStyle, hasBorders, backgroundImage) + else DefaultColors( themeStyle, hasBorders, Settings.readUserColor(prefs, context, Settings.PREF_COLOR_ACCENT_SUFFIX, false), @@ -119,7 +122,8 @@ private constructor(val themeId: Int, @JvmField val mStyleId: Int) { Settings.readUserColor(prefs, context, Settings.PREF_COLOR_GESTURE_SUFFIX, false), keyboardBackground = backgroundImage ) - THEME_USER_NIGHT -> DefaultColors( + THEME_USER_NIGHT -> if (prefs.getInt(Settings.PREF_SHOW_MORE_COLORS, 0) == 2) AllColors(readAllColorsMap(prefs, true), themeStyle, hasBorders, backgroundImage) + else DefaultColors( themeStyle, hasBorders, Settings.readUserColor(prefs, context, Settings.PREF_COLOR_ACCENT_SUFFIX, true), diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java index 38ecfb9b8..35f8e6670 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardView.java @@ -27,7 +27,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import helium314.keyboard.keyboard.emoji.EmojiPageKeyboardView; import helium314.keyboard.keyboard.internal.KeyDrawParams; import helium314.keyboard.keyboard.internal.KeyVisualAttributes; import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode; @@ -107,8 +106,8 @@ public KeyboardView(final Context context, final AttributeSet attrs, final int d final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView); - if (this instanceof EmojiPageKeyboardView || this instanceof PopupSuggestionsView) - mKeyBackground = mColors.selectAndColorDrawable(keyboardViewAttr, ColorType.BACKGROUND); + if (this instanceof PopupSuggestionsView) + mKeyBackground = mColors.selectAndColorDrawable(keyboardViewAttr, ColorType.MORE_SUGGESTIONS_WORD_BACKGROUND); else if (this instanceof PopupKeysKeyboardView) mKeyBackground = mColors.selectAndColorDrawable(keyboardViewAttr, ColorType.POPUP_KEYS_BACKGROUND); else diff --git a/app/src/main/java/helium314/keyboard/latin/common/Colors.kt b/app/src/main/java/helium314/keyboard/latin/common/Colors.kt index 60d53304d..4ad873e4f 100644 --- a/app/src/main/java/helium314/keyboard/latin/common/Colors.kt +++ b/app/src/main/java/helium314/keyboard/latin/common/Colors.kt @@ -29,6 +29,7 @@ import helium314.keyboard.keyboard.KeyboardTheme.Companion.STYLE_HOLO import helium314.keyboard.keyboard.KeyboardTheme.Companion.STYLE_MATERIAL import helium314.keyboard.latin.common.ColorType.* import helium314.keyboard.latin.R +import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.adjustLuminosityAndKeepAlpha import helium314.keyboard.latin.utils.brighten import helium314.keyboard.latin.utils.brightenOrDarken @@ -63,7 +64,7 @@ interface Colors { /** returns a colored drawable selected from [attr], which must contain using R.styleable.KeyboardView_* */ fun selectAndColorDrawable(attr: TypedArray, color: ColorType): Drawable { val drawable = when (color) { - KEY_BACKGROUND, BACKGROUND, ACTION_KEY_POPUP_KEYS_BACKGROUND, POPUP_KEYS_BACKGROUND -> + KEY_BACKGROUND, MORE_SUGGESTIONS_WORD_BACKGROUND, ACTION_KEY_POPUP_KEYS_BACKGROUND, POPUP_KEYS_BACKGROUND -> attr.getDrawable(R.styleable.KeyboardView_keyBackground) FUNCTIONAL_KEY_BACKGROUND -> attr.getDrawable(R.styleable.KeyboardView_functionalKeyBackground) SPACE_BAR_BACKGROUND -> { @@ -281,7 +282,7 @@ class DynamicColors(context: Context, override val themeStyle: String, override SPACE_BAR_TEXT -> spaceBarText FUNCTIONAL_KEY_BACKGROUND -> functionalKey SPACE_BAR_BACKGROUND -> spaceBar - BACKGROUND, MAIN_BACKGROUND -> background + MORE_SUGGESTIONS_WORD_BACKGROUND, MAIN_BACKGROUND -> background KEY_BACKGROUND -> keyBackground ACTION_KEY_POPUP_KEYS_BACKGROUND -> if (themeStyle == STYLE_HOLO) adjustedBackground else accent STRIP_BACKGROUND -> if (!hasKeyBorders && themeStyle == STYLE_MATERIAL) adjustedBackground else background @@ -292,7 +293,7 @@ class DynamicColors(context: Context, override val themeStyle: String, override override fun setColor(drawable: Drawable, color: ColorType) { val colorStateList = when (color) { - BACKGROUND -> backgroundStateList + MORE_SUGGESTIONS_WORD_BACKGROUND -> backgroundStateList KEY_BACKGROUND -> keyStateList FUNCTIONAL_KEY_BACKGROUND -> functionalKeyStateList ACTION_KEY_BACKGROUND -> actionKeyStateList @@ -334,8 +335,8 @@ class DynamicColors(context: Context, override val themeStyle: String, override view.setBackgroundColor(Color.WHITE) // set white to make the color filters work when (color) { KEY_PREVIEW -> view.background.colorFilter = adjustedBackgroundFilter - FUNCTIONAL_KEY_BACKGROUND, KEY_BACKGROUND, BACKGROUND, SPACE_BAR_BACKGROUND, STRIP_BACKGROUND -> setColor(view.background, color) - ONE_HANDED_MODE_BUTTON -> setColor(view.background, if (keyboardBackground == null) BACKGROUND else STRIP_BACKGROUND) + FUNCTIONAL_KEY_BACKGROUND, KEY_BACKGROUND, MORE_SUGGESTIONS_WORD_BACKGROUND, SPACE_BAR_BACKGROUND, STRIP_BACKGROUND -> setColor(view.background, color) + ONE_HANDED_MODE_BUTTON -> setColor(view.background, if (keyboardBackground == null) MAIN_BACKGROUND else STRIP_BACKGROUND) MORE_SUGGESTIONS_BACKGROUND -> view.background.colorFilter = backgroundFilter POPUP_KEYS_BACKGROUND -> if (themeStyle != STYLE_HOLO) @@ -473,7 +474,7 @@ class DefaultColors ( SPACE_BAR_TEXT -> spaceBarText FUNCTIONAL_KEY_BACKGROUND -> functionalKey SPACE_BAR_BACKGROUND -> spaceBar - BACKGROUND, MAIN_BACKGROUND -> background + MORE_SUGGESTIONS_WORD_BACKGROUND, MAIN_BACKGROUND -> background KEY_BACKGROUND -> keyBackground ACTION_KEY_POPUP_KEYS_BACKGROUND -> if (themeStyle == STYLE_HOLO) adjustedBackground else accent STRIP_BACKGROUND -> if (!hasKeyBorders && themeStyle == STYLE_MATERIAL) adjustedBackground else background @@ -485,7 +486,7 @@ class DefaultColors ( override fun setColor(drawable: Drawable, color: ColorType) { val colorStateList = when (color) { - BACKGROUND -> backgroundStateList + MORE_SUGGESTIONS_WORD_BACKGROUND -> backgroundStateList KEY_BACKGROUND -> keyStateList FUNCTIONAL_KEY_BACKGROUND -> functionalKeyStateList ACTION_KEY_BACKGROUND -> actionKeyStateList @@ -518,8 +519,8 @@ class DefaultColors ( view.setBackgroundColor(Color.WHITE) // set white to make the color filters work when (color) { KEY_PREVIEW, POPUP_KEYS_BACKGROUND -> view.background.colorFilter = adjustedBackgroundFilter - FUNCTIONAL_KEY_BACKGROUND, KEY_BACKGROUND, BACKGROUND, SPACE_BAR_BACKGROUND, STRIP_BACKGROUND -> setColor(view.background, color) - ONE_HANDED_MODE_BUTTON -> setColor(view.background, if (keyboardBackground == null) BACKGROUND else STRIP_BACKGROUND) + FUNCTIONAL_KEY_BACKGROUND, KEY_BACKGROUND, MORE_SUGGESTIONS_WORD_BACKGROUND, SPACE_BAR_BACKGROUND, STRIP_BACKGROUND -> setColor(view.background, color) + ONE_HANDED_MODE_BUTTON -> setColor(view.background, if (keyboardBackground == null) MAIN_BACKGROUND else STRIP_BACKGROUND) MORE_SUGGESTIONS_BACKGROUND -> view.background.colorFilter = backgroundFilter MAIN_BACKGROUND -> { if (keyboardBackground != null) { @@ -546,17 +547,12 @@ class DefaultColors ( } } -// todo: allow users to use this class -// the colorMap should be stored in settings -// settings read and write untested -// color settings should add another menu option for "all colors" -// just show all ColorTypes with current value read from the map (default to black, same as in get) -// no string name, as it is not stable -class AllColors(private val colorMap: EnumMap, override val themeStyle: String, override val hasKeyBorders: Boolean) : Colors { - private var keyboardBackground: Drawable? = null +class AllColors(private val colorMap: EnumMap, override val themeStyle: String, override val hasKeyBorders: Boolean, backgroundImage: Drawable?) : Colors { + private var keyboardBackground: Drawable? = backgroundImage private val stateListMap = EnumMap(ColorType::class.java) private var backgroundSetupDone = false - override fun get(color: ColorType): Int = colorMap[color] ?: Color.BLACK + private val colorFilters = hashMapOf() + override fun get(color: ColorType): Int = colorMap[color] ?: Color.GRAY override fun setColor(drawable: Drawable, color: ColorType) { val colorStateList = stateListMap.getOrPut(color) { stateList(brightenOrDarken(get(color), true), get(color)) } @@ -565,14 +561,18 @@ class AllColors(private val colorMap: EnumMap, override val them } override fun setColor(view: ImageView, color: ColorType) { - setColor(view.drawable, color) + if (color == TOOL_BAR_KEY) { + setColor(view.drawable, color) + return + } + view.colorFilter = getColorFilter(color) } override fun setBackground(view: View, color: ColorType) { if (view.background == null) view.setBackgroundColor(Color.WHITE) // set white to make the color filters work when (color) { - ONE_HANDED_MODE_BUTTON -> setColor(view.background, BACKGROUND) // button has no separate background color + ONE_HANDED_MODE_BUTTON -> setColor(view.background, MAIN_BACKGROUND) // button has no separate background color MAIN_BACKGROUND -> { if (keyboardBackground != null) { if (!backgroundSetupDone) { @@ -587,25 +587,32 @@ class AllColors(private val colorMap: EnumMap, override val them else -> setColor(view.background, color) } } + + private fun getColorFilter(color: ColorType) = colorFilters.getOrPut(color) { colorFilter(get(color)) } } -fun readAllColorsMap(prefs: SharedPreferences): EnumMap { - val s = prefs.getString("all_colors", "") ?: "" - val c = EnumMap(ColorType::class.java) - s.split(";").forEach { +fun readAllColorsMap(prefs: SharedPreferences, isNight: Boolean): EnumMap { + val prefPrefix = if (isNight) Settings.PREF_THEME_USER_COLOR_NIGHT_PREFIX else Settings.PREF_THEME_USER_COLOR_PREFIX + val colorsString = prefs.getString(prefPrefix + Settings.PREF_ALL_COLORS_SUFFIX, "") ?: "" + val colorMap = EnumMap(ColorType::class.java) + colorsString.split(";").forEach { val ct = try { ColorType.valueOf(it.substringBefore(",").uppercase()) } catch (_: Exception) { // todo: which one? return@forEach } val i = it.substringAfter(",").toIntOrNull() ?: return@forEach - c[ct] = i + colorMap[ct] = i } - return c + return colorMap } -fun writeAllColorsMap(c: EnumMap, prefs: SharedPreferences) { - prefs.edit { putString("all_colors", c.map { "${it.key},${it.value}" }.joinToString(";")) } +fun writeAllColorsMap(colorMap: EnumMap, prefs: SharedPreferences, isNight: Boolean) { + val prefPrefix = if (isNight) Settings.PREF_THEME_USER_COLOR_NIGHT_PREFIX else Settings.PREF_THEME_USER_COLOR_PREFIX + prefs.edit { putString( + prefPrefix + Settings.PREF_ALL_COLORS_SUFFIX, + colorMap.map { "${it.key},${it.value}" }.joinToString(";") + ) } } private fun colorFilter(color: Int, mode: BlendModeCompat = BlendModeCompat.MODULATE): ColorFilter { @@ -628,7 +635,6 @@ enum class ColorType { ACTION_KEY_BACKGROUND, ACTION_KEY_POPUP_KEYS_BACKGROUND, AUTOFILL_BACKGROUND_CHIP, - BACKGROUND, CLIPBOARD_PIN, EMOJI_CATEGORY, EMOJI_CATEGORY_SELECTED, @@ -643,6 +649,7 @@ enum class ColorType { KEY_PREVIEW, MORE_SUGGESTIONS_HINT, MORE_SUGGESTIONS_BACKGROUND, + MORE_SUGGESTIONS_WORD_BACKGROUND, POPUP_KEYS_BACKGROUND, NAVIGATION_BAR, SHIFT_KEY_ICON, diff --git a/app/src/main/java/helium314/keyboard/latin/settings/ColorsSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/ColorsSettingsFragment.kt index af7c88adb..c39c963ff 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/ColorsSettingsFragment.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/ColorsSettingsFragment.kt @@ -2,6 +2,7 @@ package helium314.keyboard.latin.settings import android.content.res.Configuration +import android.graphics.Color import android.os.Bundle import android.view.Menu import android.view.MenuInflater @@ -15,12 +16,15 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.edit import androidx.core.view.MenuProvider import androidx.core.view.forEach -import androidx.core.view.get +import androidx.core.view.isGone import androidx.fragment.app.Fragment import com.rarepebble.colorpicker.ColorPickerView import helium314.keyboard.keyboard.KeyboardSwitcher import helium314.keyboard.latin.R import helium314.keyboard.latin.RichInputMethodManager +import helium314.keyboard.latin.common.ColorType +import helium314.keyboard.latin.common.readAllColorsMap +import helium314.keyboard.latin.common.writeAllColorsMap import helium314.keyboard.latin.databinding.ColorSettingBinding import helium314.keyboard.latin.databinding.ColorSettingsBinding import helium314.keyboard.latin.utils.DeviceProtectedUtils @@ -32,9 +36,14 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi private val binding by viewBinding(ColorSettingsBinding::bind) open val isNight = false open val titleResId = R.string.select_user_colors - private var moreColors: Boolean - get() = prefs.getBoolean(Settings.PREF_SHOW_ALL_COLORS, false) - set(value) { prefs.edit().putBoolean(Settings.PREF_SHOW_ALL_COLORS, value).apply() } + + // 0 for default + // 1 for more colors + // 2 for all colors + private var moreColors: Int + get() = prefs.getInt(Settings.PREF_SHOW_MORE_COLORS, 0) + set(value) { prefs.edit().putInt(Settings.PREF_SHOW_MORE_COLORS, value).apply() } + private val prefs by lazy { DeviceProtectedUtils.getSharedPreferences(requireContext()) } private val colorPrefsAndNames by lazy { @@ -83,24 +92,28 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi } override fun onCreateMenu(menu: Menu, inflater: MenuInflater) { - if (menu.size() == 1) menu[0].setTitle(getMenuTitle()) - else menu.add(Menu.NONE, 1, Menu.NONE, getMenuTitle()) + menu.add(Menu.NONE, 0, Menu.NONE, R.string.main_colors) + menu.add(Menu.NONE, 1, Menu.NONE, R.string.more_colors) + menu.add(Menu.NONE, 2, Menu.NONE, R.string.all_colors) } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { // necessary, even though we only have a single menu item // because the back arrow on top absurdly is implemented as a menu item - if (menuItem.itemId == 1) { - moreColors = !moreColors - menuItem.setTitle(getMenuTitle()) + if (menuItem.itemId in 0..2) { + if (moreColors == menuItem.itemId) return true + if (moreColors == 2 || menuItem.itemId == 2) { + RichInputMethodManager.getInstance().inputMethodManager.hideSoftInputFromWindow(binding.dummyText.windowToken, 0) + reloadKeyboard(false) + } + moreColors = menuItem.itemId + binding.info.isGone = menuItem.itemId != 2 updateColorPrefs() return true } return false } - private fun getMenuTitle() = if (moreColors) R.string.main_colors else R.string.all_colors - override fun onViewStateRestored(savedInstanceState: Bundle?) { super.onViewStateRestored(savedInstanceState) // updateColorPrefs must be called after super.onViewStateRestored because for some reason Android @@ -110,10 +123,61 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi private fun updateColorPrefs() { binding.colorSettingsContainer.removeAllViews() + if (moreColors == 2) showAllColors() + else showMainColors() + } + + private fun showAllColors() { + val colors = readAllColorsMap(prefs, isNight) + ColorType.entries.forEach { type -> + val color = colors[type] ?: Color.GRAY + val csb = ColorSettingBinding.inflate(layoutInflater, binding.colorSettingsContainer, true) + csb.root.tag = type + csb.colorSwitch.isGone = true + csb.colorPreview.setColorFilter(color) + csb.colorText.text = type.name + + val clickListener = View.OnClickListener { + val hidden = RichInputMethodManager.getInstance().inputMethodManager.hideSoftInputFromWindow(binding.dummyText.windowToken, 0) + val picker = ColorPickerView(requireContext()) + picker.showAlpha(type != ColorType.MAIN_BACKGROUND) // background behind background looks broken and sometimes is dark, sometimes light + picker.showHex(true) + picker.showPreview(true) + picker.color = color + val builder = AlertDialog.Builder(requireContext()) + builder + .setTitle(type.name) + .setView(picker) + .setCancelable(false) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok) { _, _ -> + val colorMap = readAllColorsMap(prefs, isNight) // better re-read it + colorMap[type] = picker.color + writeAllColorsMap(colorMap, prefs, isNight) + updateAllColorPreviews() + reloadKeyboard(hidden) + } + val dialog = builder.create() + dialog.show() + // Reduce the size of the dialog in portrait mode + val wrapContent = WindowManager.LayoutParams.WRAP_CONTENT + val widthPortrait = (resources.displayMetrics.widthPixels * 0.80f).toInt() + val orientation = (resources.configuration.orientation) + if (orientation == Configuration.ORIENTATION_LANDSCAPE) + dialog.window?.setLayout(wrapContent, wrapContent) + else + dialog.window?.setLayout(widthPortrait, wrapContent) + } + csb.colorTextContainer.setOnClickListener(clickListener) + csb.colorPreview.setOnClickListener(clickListener) + } + } + + private fun showMainColors() { val prefPrefix = if (isNight) Settings.PREF_THEME_USER_COLOR_NIGHT_PREFIX else Settings.PREF_THEME_USER_COLOR_PREFIX colorPrefsAndNames.forEachIndexed { index, (colorPref, colorPrefName) -> val autoColor = prefs.getBoolean(prefPrefix + colorPref + Settings.PREF_AUTO_USER_COLOR_SUFFIX, true) - if (!moreColors && colorPref in colorPrefsToHideInitially && autoColor) + if (moreColors == 0 && colorPref in colorPrefsToHideInitially && autoColor) return@forEachIndexed val csb = ColorSettingBinding.inflate(layoutInflater, binding.colorSettingsContainer, true) csb.root.tag = index @@ -129,7 +193,7 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi if (b) csb.colorSummary.text = "" else csb.colorSummary.setText(R.string.auto_user_color) reloadKeyboard(hidden) - updateColorPreviews() + updateMainColorPreviews() } csb.colorSwitch.setOnCheckedChangeListener(switchListener) @@ -160,6 +224,7 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi builder .setTitle(colorPrefName) .setView(picker) + .setCancelable(false) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(android.R.string.ok) { _, _ -> prefs.edit { putInt(prefPrefix + colorPref, picker.color) } @@ -169,9 +234,9 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi csb.colorSwitch.isChecked = true csb.colorSummary.text = "" csb.colorSwitch.setOnCheckedChangeListener(switchListener) - updateColorPreviews() + updateMainColorPreviews() } else { - updateColorPreviews() + updateMainColorPreviews() } reloadKeyboard(hidden) } @@ -199,14 +264,23 @@ open class ColorsSettingsFragment : Fragment(R.layout.color_settings), MenuProvi } } - private fun updateColorPreviews() { + private fun updateMainColorPreviews() { binding.colorSettingsContainer.forEach { view -> - val index = view.tag as Int + val index = view.tag as? Int ?: return@forEach val color = Settings.readUserColor(prefs, requireContext(), colorPrefsAndNames[index].first, isNight) view.findViewById(R.id.color_preview)?.setColorFilter(color) } } + private fun updateAllColorPreviews() { + val colorMap = readAllColorsMap(prefs, isNight) + binding.colorSettingsContainer.forEach { view -> + val type = view.tag as? ColorType ?: return@forEach + val color = colorMap[type] ?: Color.GRAY + view.findViewById(R.id.color_preview)?.setColorFilter(color) + } + } + private fun reloadKeyboard(show: Boolean) { ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute { KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext()) diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index 4ba7601a6..a091a069c 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -79,6 +79,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_COLOR_HINT_TEXT_SUFFIX = "hint_text"; public static final String PREF_COLOR_BACKGROUND_SUFFIX = "background"; public static final String PREF_AUTO_USER_COLOR_SUFFIX = "_auto"; + public static final String PREF_ALL_COLORS_SUFFIX = "all_colors"; public static final String PREF_AUTO_CAP = "auto_cap"; public static final String PREF_VIBRATE_ON = "vibrate_on"; @@ -158,7 +159,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_PINNED_CLIPS = "pinned_clips"; public static final String PREF_VERSION_CODE = "version_code"; - public static final String PREF_SHOW_ALL_COLORS = "show_all_colors"; + public static final String PREF_SHOW_MORE_COLORS = "show_more_colors"; public static final String PREF_LIBRARY_CHECKSUM = "lib_checksum"; private static final float UNDEFINED_PREFERENCE_VALUE_FLOAT = -1.0f; @@ -182,7 +183,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang add(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID); add(PREF_EMOJI_RECENT_KEYS); add(PREF_DONT_SHOW_MISSING_DICTIONARY_DIALOG); - add(PREF_SHOW_ALL_COLORS); + add(PREF_SHOW_MORE_COLORS); add(PREF_SELECTED_SUBTYPE); }}; diff --git a/app/src/main/res/layout/color_settings.xml b/app/src/main/res/layout/color_settings.xml index 11e1facd0..b6c4533b4 100644 --- a/app/src/main/res/layout/color_settings.xml +++ b/app/src/main/res/layout/color_settings.xml @@ -5,22 +5,27 @@ + - - + android:layout_height="wrap_content" + android:orientation="vertical" /> Choose color automatically Show main colors only + + Show more colors Show all colors + + This setting exposes all colors that are used internally. The list of colors may change at any time. There is no default color, and the names will not be translated. Click for preview From f81e24d550c5d71d15cb3af88cb57bbc3a8352a0 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 19 May 2024 15:26:57 +0200 Subject: [PATCH 33/58] allow enabling popups on keypress on tablets still disabled by default, because the popups are too small (size should not be in dp, but in % of screen width or something like that) fixes #799 --- README.md | 2 +- .../latin/settings/PreferencesSettingsFragment.java | 3 --- .../helium314/keyboard/latin/settings/Settings.java | 10 +--------- .../main/res/values-sw430dp/config-per-form-factor.xml | 1 - .../main/res/values-sw600dp/config-per-form-factor.xml | 1 - .../main/res/values-sw768dp/config-per-form-factor.xml | 1 - app/src/main/res/values/config-per-form-factor.xml | 1 - 7 files changed, 2 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 84907492c..1e4fd0f6c 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ __Planned features and improvements:__ __What will _not_ be added:__ * Material 3 (not worth adding 1.5 MB to app size) * Dictionaries for more languages (you can still download them) -* Anything that requires additional permissions +* Anything that requires additional permissions, unless there is a very good reason # License diff --git a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java index 36668d367..4f98a2959 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java @@ -49,9 +49,6 @@ public void onCreate(final Bundle icicle) { removePreference(Settings.PREF_VIBRATE_ON); removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS); } - if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { - removePreference(Settings.PREF_POPUP_ON); - } setupKeypressVibrationDurationSettings(); setupKeypressSoundVolumeSettings(); diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index a091a069c..60812d79e 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -303,16 +303,8 @@ public static boolean readGestureInputEnabled(final SharedPreferences prefs) { return JniUtils.sHaveGestureLib && prefs.getBoolean(PREF_GESTURE_INPUT, true); } - public static boolean readFromBuildConfigIfToShowKeyPreviewPopupOption(final Resources res) { - return res.getBoolean(R.bool.config_enable_show_key_preview_popup_option); - } - public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs, final Resources res) { - final boolean defaultKeyPreviewPopup = res.getBoolean( - R.bool.config_default_key_preview_popup); - if (!readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { - return defaultKeyPreviewPopup; - } + final boolean defaultKeyPreviewPopup = res.getBoolean(R.bool.config_default_key_preview_popup); return prefs.getBoolean(PREF_POPUP_ON, defaultKeyPreviewPopup); } diff --git a/app/src/main/res/values-sw430dp/config-per-form-factor.xml b/app/src/main/res/values-sw430dp/config-per-form-factor.xml index d87ffcefb..a4e7de72f 100644 --- a/app/src/main/res/values-sw430dp/config-per-form-factor.xml +++ b/app/src/main/res/values-sw430dp/config-per-form-factor.xml @@ -7,7 +7,6 @@ - true true false diff --git a/app/src/main/res/values-sw600dp/config-per-form-factor.xml b/app/src/main/res/values-sw600dp/config-per-form-factor.xml index 21942e781..e43101de3 100644 --- a/app/src/main/res/values-sw600dp/config-per-form-factor.xml +++ b/app/src/main/res/values-sw600dp/config-per-form-factor.xml @@ -7,7 +7,6 @@ - false false false diff --git a/app/src/main/res/values-sw768dp/config-per-form-factor.xml b/app/src/main/res/values-sw768dp/config-per-form-factor.xml index 49f7e17c4..d1dbd6160 100644 --- a/app/src/main/res/values-sw768dp/config-per-form-factor.xml +++ b/app/src/main/res/values-sw768dp/config-per-form-factor.xml @@ -7,7 +7,6 @@ - false false false diff --git a/app/src/main/res/values/config-per-form-factor.xml b/app/src/main/res/values/config-per-form-factor.xml index b27018d24..3633ccde6 100644 --- a/app/src/main/res/values/config-per-form-factor.xml +++ b/app/src/main/res/values/config-per-form-factor.xml @@ -7,7 +7,6 @@ - true true false From bb3fe9d9f9034d7f6e3fd66f0c5faee5b16178f1 Mon Sep 17 00:00:00 2001 From: codokie <151087174+codokie@users.noreply.github.com> Date: Sun, 19 May 2024 17:42:20 +0300 Subject: [PATCH 34/58] Add caps lock indicator (#692) --- .../keyboard/internal/KeyboardIconsSet.kt | 2 ++ .../keyboard_parser/floris/TextKeyData.kt | 4 ++-- .../drawable/sym_keyboard_shift_lock_holo.xml | 17 +++++++++++++++++ .../drawable/sym_keyboard_shift_lock_lxx.xml | 14 ++++++++++++++ .../sym_keyboard_shift_lock_rounded.xml | 14 ++++++++++++++ .../res/drawable/sym_keyboard_shift_lxx.xml | 8 ++++---- .../res/drawable/sym_keyboard_shift_rounded.xml | 2 +- ...d_holo.xml => sym_keyboard_shifted_holo.xml} | 0 app/src/main/res/values/attrs.xml | 1 + app/src/main/res/values/keyboard-icons-holo.xml | 3 ++- .../res/values/keyboard-icons-lxx-light.xml | 1 + .../main/res/values/keyboard-icons-rounded.xml | 1 + 12 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 app/src/main/res/drawable/sym_keyboard_shift_lock_holo.xml create mode 100644 app/src/main/res/drawable/sym_keyboard_shift_lock_lxx.xml create mode 100644 app/src/main/res/drawable/sym_keyboard_shift_lock_rounded.xml rename app/src/main/res/drawable/{sym_keyboard_shift_locked_holo.xml => sym_keyboard_shifted_holo.xml} (100%) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt index f143bdcbb..a8a7a2508 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt @@ -32,6 +32,7 @@ class KeyboardIconsSet { const val NAME_UNDEFINED = "undefined" const val NAME_SHIFT_KEY = "shift_key" const val NAME_SHIFT_KEY_SHIFTED = "shift_key_shifted" + const val NAME_SHIFT_KEY_LOCKED = "shift_key_locked" const val NAME_DELETE_KEY = "delete_key" const val NAME_SETTINGS_KEY = "settings_key" const val NAME_SPACE_KEY = "space_key" @@ -78,6 +79,7 @@ class KeyboardIconsSet { NAME_INCOGNITO_KEY to R.styleable.Keyboard_iconIncognitoKey, NAME_SPACE_KEY_FOR_NUMBER_LAYOUT to R.styleable.Keyboard_iconSpaceKeyForNumberLayout, NAME_SHIFT_KEY_SHIFTED to R.styleable.Keyboard_iconShiftKeyShifted, + NAME_SHIFT_KEY_LOCKED to R.styleable.Keyboard_iconShiftKeyLocked, NAME_SHORTCUT_KEY_DISABLED to R.styleable.Keyboard_iconShortcutKeyDisabled, NAME_LANGUAGE_SWITCH_KEY to R.styleable.Keyboard_iconLanguageSwitchKey, NAME_ZWNJ_KEY to R.styleable.Keyboard_iconZwnjKey, diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index 5c5e66e0c..fe8fa73d2 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -87,8 +87,8 @@ sealed interface KeyData : AbstractKeyData { private fun getShiftLabel(params: KeyboardParams) = when (params.mId.mElementId) { KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> params.mLocaleKeyboardInfos.labelSymbol KeyboardId.ELEMENT_SYMBOLS -> params.mLocaleKeyboardInfos.getShiftSymbolLabel(Settings.getInstance().isTablet) - KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED, KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, - KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED, KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED -> "!icon/${KeyboardIconsSet.NAME_SHIFT_KEY_SHIFTED}" + KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED, KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED -> "!icon/${KeyboardIconsSet.NAME_SHIFT_KEY_SHIFTED}" + KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED, KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED -> "!icon/${KeyboardIconsSet.NAME_SHIFT_KEY_LOCKED}" else -> "!icon/${KeyboardIconsSet.NAME_SHIFT_KEY}" } diff --git a/app/src/main/res/drawable/sym_keyboard_shift_lock_holo.xml b/app/src/main/res/drawable/sym_keyboard_shift_lock_holo.xml new file mode 100644 index 000000000..84d4e8439 --- /dev/null +++ b/app/src/main/res/drawable/sym_keyboard_shift_lock_holo.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/sym_keyboard_shift_lock_lxx.xml b/app/src/main/res/drawable/sym_keyboard_shift_lock_lxx.xml new file mode 100644 index 000000000..3f2068542 --- /dev/null +++ b/app/src/main/res/drawable/sym_keyboard_shift_lock_lxx.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/sym_keyboard_shift_lock_rounded.xml b/app/src/main/res/drawable/sym_keyboard_shift_lock_rounded.xml new file mode 100644 index 000000000..c07064217 --- /dev/null +++ b/app/src/main/res/drawable/sym_keyboard_shift_lock_rounded.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/sym_keyboard_shift_lxx.xml b/app/src/main/res/drawable/sym_keyboard_shift_lxx.xml index 9d900fc4e..242b21f92 100644 --- a/app/src/main/res/drawable/sym_keyboard_shift_lxx.xml +++ b/app/src/main/res/drawable/sym_keyboard_shift_lxx.xml @@ -1,12 +1,12 @@ + android:viewportHeight="960" + android:viewportWidth="960" > + android:pathData="m480,332 l240,240 -56,56 -184,-184 -184,184 -56,-56z"/> diff --git a/app/src/main/res/drawable/sym_keyboard_shift_rounded.xml b/app/src/main/res/drawable/sym_keyboard_shift_rounded.xml index f05fc08f9..f1461c775 100644 --- a/app/src/main/res/drawable/sym_keyboard_shift_rounded.xml +++ b/app/src/main/res/drawable/sym_keyboard_shift_rounded.xml @@ -10,5 +10,5 @@ android:viewportHeight="960"> + android:pathData="m508.62,360.62 l182.77,182.77a40.47,40.47 90,0 1,0 57.23,39.61 39.61,1.26 0,1 -56,-1.23L480,444 324.62,599.38a40.47,40.47 0,0 1,-57.23 0,39.61 39.61,91.26 0,1 1.23,-56l182.77,-182.77a40.47,40.47 0,0 1,57.23 0z"/> \ No newline at end of file diff --git a/app/src/main/res/drawable/sym_keyboard_shift_locked_holo.xml b/app/src/main/res/drawable/sym_keyboard_shifted_holo.xml similarity index 100% rename from app/src/main/res/drawable/sym_keyboard_shift_locked_holo.xml rename to app/src/main/res/drawable/sym_keyboard_shifted_holo.xml diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index d8ef5903e..35dd5d32e 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -247,6 +247,7 @@ + diff --git a/app/src/main/res/values/keyboard-icons-holo.xml b/app/src/main/res/values/keyboard-icons-holo.xml index e6dd92501..1eae6d2d7 100644 --- a/app/src/main/res/values/keyboard-icons-holo.xml +++ b/app/src/main/res/values/keyboard-icons-holo.xml @@ -18,7 +18,8 @@ @drawable/sym_keyboard_voice_holo @drawable/sym_keyboard_incognito_holo @drawable/sym_keyboard_space_holo - @drawable/sym_keyboard_shift_locked_holo + @drawable/sym_keyboard_shifted_holo + @drawable/sym_keyboard_shift_lock_holo @drawable/sym_keyboard_voice_off_holo @drawable/sym_keyboard_language_switch @drawable/ic_autocorrect diff --git a/app/src/main/res/values/keyboard-icons-lxx-light.xml b/app/src/main/res/values/keyboard-icons-lxx-light.xml index a2868674f..f673403fe 100644 --- a/app/src/main/res/values/keyboard-icons-lxx-light.xml +++ b/app/src/main/res/values/keyboard-icons-lxx-light.xml @@ -10,6 +10,7 @@ @drawable/sym_keyboard_shift_lxx @drawable/sym_keyboard_shift_lxx + @drawable/sym_keyboard_shift_lock_lxx @drawable/sym_keyboard_delete_lxx @drawable/sym_keyboard_tab_lxx @drawable/sym_keyboard_settings_lxx diff --git a/app/src/main/res/values/keyboard-icons-rounded.xml b/app/src/main/res/values/keyboard-icons-rounded.xml index ad66ba327..105d73f4e 100644 --- a/app/src/main/res/values/keyboard-icons-rounded.xml +++ b/app/src/main/res/values/keyboard-icons-rounded.xml @@ -9,6 +9,7 @@ @drawable/sym_keyboard_shift_rounded @drawable/sym_keyboard_shift_rounded + @drawable/sym_keyboard_shift_lock_rounded @drawable/sym_keyboard_delete_rounded @drawable/sym_keyboard_tab_rounded @drawable/sym_keyboard_settings_rounded From 1a1c8890f63dbf181cc69e01ca9aec830045a592 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 19 May 2024 22:27:06 +0200 Subject: [PATCH 35/58] use existing arrow icons instead of duplicating the vector data --- .../res/drawable/sym_keyboard_shift_lxx.xml | 16 ++++------------ .../drawable/sym_keyboard_shift_rounded.xml | 18 ++++-------------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/app/src/main/res/drawable/sym_keyboard_shift_lxx.xml b/app/src/main/res/drawable/sym_keyboard_shift_lxx.xml index 242b21f92..5c4b54897 100644 --- a/app/src/main/res/drawable/sym_keyboard_shift_lxx.xml +++ b/app/src/main/res/drawable/sym_keyboard_shift_lxx.xml @@ -1,12 +1,4 @@ - - - - + + + diff --git a/app/src/main/res/drawable/sym_keyboard_shift_rounded.xml b/app/src/main/res/drawable/sym_keyboard_shift_rounded.xml index f1461c775..d3df2e6f0 100644 --- a/app/src/main/res/drawable/sym_keyboard_shift_rounded.xml +++ b/app/src/main/res/drawable/sym_keyboard_shift_rounded.xml @@ -1,14 +1,4 @@ - - - - - \ No newline at end of file + + + From c566c93bff56d3fa8ba5edf2f3d38ad0ffcb7f9a Mon Sep 17 00:00:00 2001 From: Helium314 Date: Mon, 20 May 2024 22:26:26 +0200 Subject: [PATCH 36/58] import translations, upgrade version --- app/build.gradle | 4 +- .../main/assets/dictionaries_in_dict_repo.csv | 5 +- app/src/main/res/values-ar/strings.xml | 14 +- app/src/main/res/values-be/strings.xml | 10 +- app/src/main/res/values-bn/strings.xml | 5 + app/src/main/res/values-cs/strings.xml | 200 ++++++++++++- app/src/main/res/values-de/strings.xml | 5 + app/src/main/res/values-es-rUS/strings.xml | 183 +++++++++++- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-eu/strings.xml | 272 +++++++++++++++--- app/src/main/res/values-fil/strings.xml | 62 ++++ app/src/main/res/values-fr/strings.xml | 44 ++- app/src/main/res/values-gl/strings.xml | 9 + app/src/main/res/values-pl/strings.xml | 52 +++- app/src/main/res/values-pt-rBR/strings.xml | 45 +-- app/src/main/res/values-ru/strings.xml | 8 +- app/src/main/res/values-sr/strings.xml | 27 +- app/src/main/res/values-sv/strings.xml | 90 +++++- app/src/main/res/values-tr/strings.xml | 19 +- app/src/main/res/values-uk/strings.xml | 6 +- app/src/main/res/values/strings.xml | 58 ++-- .../metadata/android/ar/changelogs/1001.txt | 20 ++ .../metadata/android/ar/changelogs/1003.txt | 7 + .../metadata/android/ar/changelogs/1004.txt | 7 + .../metadata/android/ar/full_description.txt | 29 ++ .../metadata/android/ar/short_description.txt | 1 + fastlane/metadata/android/ar/title.txt | 1 + .../android/cs-CZ/changelogs/1001.txt | 20 ++ .../android/cs-CZ/changelogs/1003.txt | 9 + .../android/cs-CZ/changelogs/1004.txt | 7 + .../android/cs-CZ/short_description.txt | 1 + fastlane/metadata/android/cs-CZ/title.txt | 1 + .../android/de-DE/changelogs/1001.txt | 20 ++ .../android/de-DE/changelogs/1003.txt | 9 + .../android/de-DE/changelogs/1004.txt | 7 + .../android/de-DE/full_description.txt | 29 ++ .../android/de-DE/short_description.txt | 1 + fastlane/metadata/android/de-DE/title.txt | 1 + .../android/en-US/changelogs/2000.txt | 10 + .../android/fr-FR/full_description.txt | 29 ++ .../android/fr-FR/short_description.txt | 1 + fastlane/metadata/android/fr-FR/title.txt | 1 + .../android/gl-ES/changelogs/1001.txt | 20 ++ .../android/gl-ES/changelogs/1003.txt | 9 + .../android/gl-ES/changelogs/1004.txt | 7 + .../android/gl-ES/full_description.txt | 29 ++ .../android/pl-PL/changelogs/1001.txt | 20 ++ .../android/pl-PL/changelogs/1003.txt | 9 + .../android/pl-PL/changelogs/1004.txt | 7 + .../android/pl-PL/full_description.txt | 29 ++ .../android/pt-BR/full_description.txt | 29 ++ .../android/pt-BR/short_description.txt | 1 + fastlane/metadata/android/pt-BR/title.txt | 1 + .../android/ru-RU/full_description.txt | 29 ++ .../android/ru-RU/short_description.txt | 1 + 55 files changed, 1378 insertions(+), 144 deletions(-) create mode 100644 app/src/main/res/values-fil/strings.xml create mode 100644 fastlane/metadata/android/ar/changelogs/1001.txt create mode 100644 fastlane/metadata/android/ar/changelogs/1003.txt create mode 100644 fastlane/metadata/android/ar/changelogs/1004.txt create mode 100644 fastlane/metadata/android/ar/full_description.txt create mode 100644 fastlane/metadata/android/ar/short_description.txt create mode 100644 fastlane/metadata/android/ar/title.txt create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/1001.txt create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/1003.txt create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/1004.txt create mode 100644 fastlane/metadata/android/cs-CZ/short_description.txt create mode 100644 fastlane/metadata/android/cs-CZ/title.txt create mode 100644 fastlane/metadata/android/de-DE/changelogs/1001.txt create mode 100644 fastlane/metadata/android/de-DE/changelogs/1003.txt create mode 100644 fastlane/metadata/android/de-DE/changelogs/1004.txt create mode 100644 fastlane/metadata/android/de-DE/full_description.txt create mode 100644 fastlane/metadata/android/de-DE/short_description.txt create mode 100644 fastlane/metadata/android/de-DE/title.txt create mode 100644 fastlane/metadata/android/en-US/changelogs/2000.txt create mode 100644 fastlane/metadata/android/fr-FR/full_description.txt create mode 100644 fastlane/metadata/android/fr-FR/short_description.txt create mode 100644 fastlane/metadata/android/fr-FR/title.txt create mode 100644 fastlane/metadata/android/gl-ES/changelogs/1001.txt create mode 100644 fastlane/metadata/android/gl-ES/changelogs/1003.txt create mode 100644 fastlane/metadata/android/gl-ES/changelogs/1004.txt create mode 100644 fastlane/metadata/android/gl-ES/full_description.txt create mode 100644 fastlane/metadata/android/pl-PL/changelogs/1001.txt create mode 100644 fastlane/metadata/android/pl-PL/changelogs/1003.txt create mode 100644 fastlane/metadata/android/pl-PL/changelogs/1004.txt create mode 100644 fastlane/metadata/android/pl-PL/full_description.txt create mode 100644 fastlane/metadata/android/pt-BR/full_description.txt create mode 100644 fastlane/metadata/android/pt-BR/short_description.txt create mode 100644 fastlane/metadata/android/pt-BR/title.txt create mode 100644 fastlane/metadata/android/ru-RU/full_description.txt create mode 100644 fastlane/metadata/android/ru-RU/short_description.txt diff --git a/app/build.gradle b/app/build.gradle index 1df406d0d..2830cacbb 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "helium314.keyboard" minSdkVersion 21 targetSdkVersion 34 - versionCode 1004 - versionName '1.3' + versionCode 2000 + versionName '2.0-alpha1' ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } diff --git a/app/src/main/assets/dictionaries_in_dict_repo.csv b/app/src/main/assets/dictionaries_in_dict_repo.csv index 34de7d2f8..fe9800762 100644 --- a/app/src/main/assets/dictionaries_in_dict_repo.csv +++ b/app/src/main/assets/dictionaries_in_dict_repo.csv @@ -59,12 +59,13 @@ main,te, main,tok, main,tcy, main,tr, +emoji,uk, main,uk, main,ur, main,af,exp main,ar,exp -main,bn,exp main,bn_BD,exp +main,bn,exp main,bg,exp main,cs,exp main,en_GB,exp @@ -74,10 +75,12 @@ symbols,fr,exp main,fr,exp main,de_AT,exp main,de,exp +main,he,exp main,id,exp main,it,exp main,kab,exp addon,ml_ZZ,exp +main,pms,exp main,ru,exp main,sk,exp main,es,exp diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 70157b9b5..e0dcb8620 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -336,8 +336,9 @@ هذه الكلمة موجودة بالفعل في قاموس المستخدم %s. الرجاء كتابة واحدة أخرى. تفضيل المحلية على الأرقام اللاتينية حدد الألوان للنص والخلفيات - "بدون قاموس، ستحصل فقط على اقتراحات للنص الذي أدخلته من قبل.<br> -\n يمكنك تنزيل القواميس %1$s، أو التحقق من إمكانية تنزيل قاموس \"%2$s\" مباشرة %3$s." + بدون قاموس، ستحصل فقط على اقتراحات للنص الذي أدخلته من قبل.<br> +\n +\n . . . . . . . .يمكنك تنزيل القواميس %1$s، أو التحقق من إمكانية تنزيل قاموس \"%2$s\" مباشرة %3$s. %s (Probhat) %s (Sebeolsik 390) اختر ترتيب المفاتيح المنبثقه @@ -345,4 +346,13 @@ اضغط لفترة طويلة على مفتاح الرموز للوحة الرقمية قصّ %s (طالب) + استخدم دائمًا الاقتراح الأوسط + عند الضغط على مسافة أو علامات الترقيم، سيتم إدخال الاقتراح الأوسط + إغلاق تاريخ الحافظة + حدد مفاتيح شريط أدوات الحافظة + %s (Extended) + مانسي + مانسي (%s) + عرض المزيد من الالوان + يعرض هذا الإعداد كافة الألوان المستخدمة داخليًا. قد تتغير قائمة الألوان في أي وقت. لا يوجد لون افتراضي، ولن تتم ترجمة الأسماء. \ No newline at end of file diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 81a6d108e..cbaf32b54 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -90,7 +90,7 @@ Праграма %s ўжо ўключана ў наладах «Мова і ўвод». Перайдзіце да наступнага кроку! Ўключыць ў наладах Пераключыцца на %s - Выберыце \"%s\" як актыўны метад ўводу тэксту. + Выберыце «%s» бягучым метадам ўводу. Змена спосабаў ўводу Ўсё гатова! Цяпер вы можаце пісаць ва ўсіх вашых любімых праграмах з дапамогай %s. @@ -152,9 +152,9 @@ Шэрая Чорная Карыстацкая - Карыстацкая (Цёмная тэма) + Карыстацкая (цёмная тэма) Настройка колераў тэмы - Настройка колераў тэмы (Цёмная тэма) + Настройка колераў тэмы (цёмная тэма) Аўтаматычны выбар колеру Націсніце для прадпрагляду Абярыце колеру для тэксту і фону @@ -194,7 +194,7 @@ Стыль тэмы Закруглены Колеры тэмы - Колеры тэмы (Цёмная тэма) + Колеры тэмы (цёмная тэма) Контуры клавіш Правядзіце пальцам ад клавішы выдалення, каб выбраць і выдаліць адразу большыя часткі тэксту Аўтаматычна ўстаўляць прабел пасля знакаў прыпынку пры ўводзе новага слова @@ -291,7 +291,7 @@ Сімвалы тэлефона Лічбы Лічбавая клавіятура - Лічбавая клавіятура(гарызантальная) + Лічбавая клавіятура (гарызантальная) Ўсталяваць фонавы малюнак Ўсталяваць малюнак для дзённага ці начнога рэжыму? Дзень diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 5d1ddfbc8..4d92a285f 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -336,4 +336,9 @@ স্পেসবারের পটভূমি স্পেসবারে উল্লম্ব অঙ্গুলিহেলন স্পেসবারে অনুভূমিক অঙ্গুলিহেলন + কাটো + নাম্বারপ্যাডের জন্য সিম্বল বোতাম কিছুক্ষণ ধরে রাখো + পরিবর্তনশীল টুলবার দিক + ডান থেকে বাম কিবোর্ড নির্বাচন করলে উল্টো দিকে যাও + %s (শিক্ষার্থী) \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index a82757710..fe60dd5d4 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -3,9 +3,9 @@ Copyright (C) 2008 The Android Open Source Project modified SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only ---> +--> "Vyhledat kontakty" - "Kontrola pravopisu používá záznamy z vašeho seznamu kontaktů." + Kontrola pravopisu používá záznamy z vašeho seznamu kontaktů "Při stisku klávesy vibrovat" "Zvuk při stisku klávesy" "Detail znaku při stisku klávesy" @@ -24,7 +24,7 @@ Použít jména ze seznamu kontaktů k návrhům a opravám "Personalizované návrhy" "Tečka dvojitým mezerníkem" - "Dvojím klepnutím na mezerník vložíte tečku následovanou mezerou." + Dvojím klepnutím na mezerník vložíte tečku následovanou mezerou "Velká písmena automaticky" "Kapitalizovat první slovo každé věty" "Vlastní slovník" @@ -48,7 +48,7 @@ "Dynamický plovoucí náhled" "Zobrazení navrhovaného slova při psaní gesty" "Frázové gesto" - "Mezery mezi gesty zadáte přejetím po klávese mezerníku." + Mezery mezi gesty zadáte přejetím po klávese mezerníku "Angličtina (Velká Británie)" "Angličtina (USA)" "Španělština (USA)" @@ -78,7 +78,7 @@ "Hlasitost stisknutí klávesy" "Prodleva dlouhého stisknutí" "Emodži pro fyzickou klávesnici" - "Paletu emodži zobrazíte stisknutím fyzické klávesy Alt." + Paletu emodži zobrazíte stisknutím fyzické klávesy Alt "Výchozí" "Vítá vás %s" "s psaním gesty" @@ -87,12 +87,12 @@ "Nastavení aplikace %s" "Zapnutí aplikace %s" "Zaškrtněte aplikaci %s v nastavení Jazyky a zadávání, povolíte tak její spuštění." - "Aplikace %s je již v nastavení Jazyky a zadávání zapnuta, tento krok je tedy již proveden. Pokračujte dalším." + Aplikace %s je již v nastavení Jazyky a zadávání zapnuta, tento krok je tedy již proveden. Pokračujte dalším! "Aktivovat v nastavení" "Přepnutí na aplikaci %s" "Poté vyberte jako aktivní metodu zadávání textu možnost %s." "Přepnout metody zadávání" - "Gratulujeme, vše je připraveno." + Gratulujeme, vše je připraveno! "Nyní můžete ve všech svých oblíbených aplikacích psát pomocí aplikace %s." "Hotovo" "Zobrazit ikonu aplikace" @@ -133,11 +133,11 @@ Automaticky vloží mezeru po ukončení slova znaménkem Vynutit režim inkognito Vypnout učení nových slov - Číselná řada + Číselný řádek Vstup Dodatečné klávesy Mazat posunutím - Vždy aktivní číselná řada + Vždy aktivní číselný řádek Nápověda kláves Nápověda při dlouhém stisku Změnit metodu zadávání mezerníkem @@ -168,4 +168,186 @@ "Čekat" Separační vzdálenost Přepnout jazyk + Přidat slova do osobního slovníku + Použít osobní slovník zařízení k ukládání naučených slovíček + Další automatické opravy + Automatické opravy, i když to není výslovně požadováno ve vstupním poli + Jazyk + Denní + Vždy používat prostřední návrh + Po stisknutí mezery nebo interpunkčního znaménka se zadá prostřední návrh + Chyba při obnovení zálohy: %s + Chyba při zálohování: %s + Načíst knihovnu pro psaní gesty + Zajistit nativní knihovnu umožňující psaní gesty + Neznámý soubor knihovny. Jste si jisti, že jste jej získali z důvěryhodného zdroje a že je určen pro \'%s\'? + Načíst knihovnu + Odstranit knihovnu + Zobrazit další písmena s diakritikou ve vyskakovacím okně + Zobrazit varianty definované v jazycích klávesnice (výchozí) + Přidat společné varianty + Přidat všechny dostupné varianty + Detekce URL + Snažit se detekovat URL a podobné adresy jako jedno slovo + Jazyky & Rozvržení + Lokalizace číselného řádku + Číselný řádek + Upřednostňovat lokalizovaná čísla před čísly v latince + Zvolit zdroj tipů + Jazyk (prioritní) + Rozvržení + Symboly + Vyjmout + Vybrat slovo + Režim ovládání jednou rukou + Úplně vlevo + Dlouhé stisknutí klávesy se symboly pro numerickou klávesnici + Zvolit pořadí kláves vyskakovacího okna + Výběr kláves na panelu nástrojů + Zmenšit mezery mezi klávesami + Přidat vlastní rozvržení + Konfigurace klávesnice + Výběr jazyka + Přidat do %s + Barvy (noční) + Barvy + Černý + Hnědý + Čokoládový + Dynamické barvy + Indigový + Text tipů klávesy + Text návrhové lišty + Pozadí klávesy + Pozadí funkční klávesy + Pozadí mezerníku + Text mezerníku + Žádné + Kaitag + Zavřít historii schránky + Výběr kláves panelu nástrojů schránky + Měřítko dolní výplně + Kaitag (%s) + Zkopírovat stávající rozložení + Nastavit název rozvržení + Vybrat soubor v kompatibilním formátu. Informace o formátech jsou k dispozici %s. + Symboly + Symboly (arabské) + Čísla + Opravdu odstranit vlastní rozložení %s? + Chyba rozložení: %s + Klepnutím lze upravit původní rozvržení + Rozložení (kromě symbolů) obsahují vnitřní nastavení, které se může ještě změnit. Pokud se tak stane, vlastní rozvržení již nemusí fungovat správně. + Numerická klávesnice (na šířku) + Nastavení obrázku na pozadí + Nastavit obrázek pro denní nebo noční režim? + Noční + Přidat slovník ze souboru + Slovníky + Interní hlavní slovník + Do kterého jazyka má být přidán slovník „%1$s“ pro %2$s? + Toto slovo se již nachází v uživatelském slovníku %s. Zadejte prosím jiné. + Lesní + Oceánový + Růžový + Zobrazit funkce, které mohou zůstat nepovšimnuty + chráněné úložiště zařízení + Přesun kurzoru + Svislé gesto přejetí mezerníku + Ignorovat požadavek jiných aplikací na zakázání návrhů (může způsobit problémy) + Přizpůsobit symboly a rozvržení čísel + "Bez slovníku se zobrazí pouze návrhy textu, který jste zadali dříve.<br> +\n Můžete si stáhnout slovníky %1$s nebo zkontrolovat, zda lze slovník pro „%2$s“ stáhnout přímo %3$s." + zde + Vybrat slovník, který chcete přidat. Slovníky ve formátu .dict lze stáhnout %s. + %s (experimentální) + Vybraný soubor je pro %1$s, ale byl očekáván soubor %2$s. Přesto jej použít pro %2$s? + Chyba: Vybraný soubor není platný soubor slovníku + Chyba: skript není kompatibilní s touto klávesnicí + Chyba při načítání souboru slovníku + Přesto použít + Váha: + Styl + Zaoblený + Obarvit navigační panel + Tmavší + Oblačný + Písečný + Pozadí klávesnice + Text klávesy + Akcent + Zadávání gesty + Zobrazit na GitHubu + Uložit protokol + Obrácení směru při výběru podtypu klávesnice zprava doleva + Proměnlivý směr panelu nástrojů + Záloha + Obnovení + Vícejazyčné psaní + Vlevo + Vpravo + Nahoru + Dolů + Zobrazit funkční tipy + Schránka + Vymazat schránku + Hlasové zadávání + Úplně vpravo + Zobrazit tipy, pokud dlouhé stisknutí klávesy aktivuje další funkce + %s (Akkhor) + %s (Sebeolsik 390) + %s (Sebeolsik Final) + Abeceda (Bépo) + Načíst soubor + Přidat slovo + Světlý + Holo bílá + Tmavý + Modrošedý + O aplikaci + Verze + Vodorovné gesto přejetí mezerníku + Varování: Vypnutím tohoto nastavení se vymažou naučená data + Přepínat oba + Vždy zobrazovat návrhy + Spolehlivost automatických oprav + Záloha a obnovení + Uložit nebo načíst ze souboru. Upozornění: obnovení přepíše stávající data + Numerická klávesnice + Znovu nezobrazovat + Budete potřebovat knihovnu pro \'%s\'. Nekompatibilní knihovny mohou při použití psaní gesty selhat. +\n +\nVarování: načítání externího kódu může představovat bezpečnostní riziko. Používejte pouze knihovnu ze zdroje, kterému důvěřujete. + Popis skrytých funkcí + %s (Rozšířené) + ► Dlouhým stisknutím klávesy schránky (volitelná v pruhu návrhů) vložíte obsah systémové schránky. <br> <br> ► Dlouhé stisknutí kláves na panelu nástrojů pruhu návrhů je připne na pruh návrhů. <br> <br> ► Dlouhým stisknutím klávesy čárka otevřete zobrazení schránky, zobrazení emodži, režim jedné ruky, nastavení nebo přepnutí jazyka: <br> • Zobrazení emodži a přepínání jazyků zmizí, pokud máte odpovídající klávesu povoleno; <br> • U některých rozvržení to není klávesa Comma, ale klávesa na stejné pozici (např. je to \'q\' pro rozvržení Dvorak). <br> <br> ► Když je povolen režim inkognito, nebudou se učit žádná slova a nebudou přidány žádné emotikony. <br> <br> ► Stisknutím ikony inkognito otevřete panel nástrojů. <br> <br> ► Zadávání pomocí posuvné klávesy: Přejetím z Shift na jinou klávesu zadejte jednu klávesu velkých písmen: <br> • Toto funguje také pro klávesu „?123“ pro psaní jediného symbolu z klávesnice se symboly a pro související klíče. <br> <br> ► Dlouhým stisknutím návrhu v pruhu návrhů zobrazíte další návrhy a stisknutím tlačítka Smazat tento návrh odstraníte. <br> <br> ► Přejetím prstem nahoru z návrhu otevřete další návrhy a uvolněním návrh jej vyberte. <br> <br> ► Dlouhým stisknutím položky v historii schránky ji připnete (uchovávejte ji ve schránce, dokud ji neuvolníte). <br> <br> ► Slovníky můžete přidávat tak, že je otevřete v průzkumníku souborů: <br> • Toto funguje pouze s <i>content-uris</i> a nikoli s <i>file-uris</i> , což znamená, že nemusí fungovat s některými průzkumníky souborů. <br> <br> <i>Režim ladění / ladění APK</i> <br> <br> • Dlouhým stisknutím návrhu zobrazíte zdrojový slovník.<br> <br> • Při použití ladícího souboru APK můžete najděte Nastavení ladění v Pokročilých předvolbách, i když užitečnost je omezená s výjimkou ukládání slovníků do protokolu. <br> <br> • V případě pádu aplikace budete při otevření Nastavení dotázáni, zda chcete protokoly o selhání. <br> <br> • Při použití vícejazyčného psaní bude mezerník zobrazovat hodnotu spolehlivosti používanou k určení aktuálně používaného jazyka. <br> <br> • Návrhy budou mít navrchu malá čísla zobrazující nějaké interní skóre a zdrojový slovník (lze vypnout). <br> <br> ► Pro uživatele, kteří provádějí ruční zálohování s přístupem root: Počínaje Androidem 7 není soubor sdílených předvoleb ve výchozím umístění, protože aplikace používá %s. <br> To je nezbytné, aby bylo možné načíst nastavení před odemknutím zařízení, např. při startu. <br> Soubor se nachází v /data/user_de/0/package_id/shared_prefs/, i když to může záviset na zařízení a verzi Androidu. + Nelze přečíst soubor + Další symboly + Telefon + Telefonní symboly + Opravdu nahradit uživatelem přidaný slovník „%1$s“? +\n +\nAktuální slovník: +\n%2$s +\n +\nNový slovník: +\n%3$s + Nahradit slovník + Opravdu odstranit slovník „%s“ přidaný uživatelem? + Open-source licence + GNU General Public License v3.0 + Zavřít + Klepnutím na jazyk otevřete nastavení + %s (Probhat) + Výběr barev pro text a pozadí + Fialový + Uživatelsky definovaný + Uživatelsky definovaný (noční) + Upravit barvy + Upravit barvy (noční) + Vybrat barvu automaticky + Zobrazit pouze hlavní barvy + Zobrazit všechny barvy + Klikněte pro náhled + %s (Student) \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a3e7a3b79..a64b38b3b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -345,4 +345,9 @@ Richtung umkehren, wenn die Tastatur einer Rechts-nach-Links-Sprache ausgewählt ist %s (Student) %s (Probhat) + Bei Eingabe eines Leerzeichens oder eines Punktes wird der mittlere Vorschlag gewählt + Schließe Zwischenablagen-Verlauf + Immer mittleren Vorschlag verwenden + Wähle die Tasten der Symbolleiste für die Zwischenablage + %s (Extended) \ No newline at end of file diff --git a/app/src/main/res/values-es-rUS/strings.xml b/app/src/main/res/values-es-rUS/strings.xml index e6d789ed2..a016bf9fe 100644 --- a/app/src/main/res/values-es-rUS/strings.xml +++ b/app/src/main/res/values-es-rUS/strings.xml @@ -3,8 +3,7 @@ Copyright (C) 2008 The Android Open Source Project modified SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only ---> - +--> "Nombres de contactos" "El corrector ortográfico usa entradas de tu lista de contactos." "Vibrar al presionar teclas" @@ -166,4 +165,184 @@ "Buscar" "Pausa" "Esp." + Otros + Distancia de dividido + Intercambiar ambos + Advertencia: Desactivar esta opción eliminará la información aprendida + Añadir palabras al diccionario personal + Siempre mostrar sugerencias + Autocorrección incluso cuando el campo de entrada no lo solicita explícitamente + Más autocorrecciones + Confianza de autocorrección + Copia de seguridad y restauración + Guardar o cargar desde archivo. Advertencia: la restauración sobrescribirá los datos existentes + Error de copia de seguridad: %s + Error al restaurar la copia de seguridad: %s + Copia de seguridad + Teclado númerico (apaisado) + Día + Noche + Peso: + Violeta + Toca el idioma para abrir los ajustes + Más símbolos + Teléfono + Símbolos de teléfono + Números + Teclado numérico + Añadir una palabra + Ajustar colores (noche) + Gesto horizontal en la barra espaciadora + Gesto vertical en la barra espaciadora + Ninguna + Mover cursor + Cargar biblioteca de escritura gestual + Proporcionar una biblioteca nativa para permitir la escritura gestual + Intercambiar idioma + Restaurar + Escritura multilingüe + Necesitarás la biblioteca para \'%s\'. Las bibliotecas incompatibles pueden bloquearse al utilizar la escritura gestual. +\n +\nAdvertencia: cargar código externo puede ser un riesgo para la seguridad. Utiliza únicamente una biblioteca de una fuente en la que confíes. + Archivo de biblioteca desconocido. ¿Está seguro de que lo ha obtenido de una fuente de confianza y que es para \'%s\'? + Cargar librería + Eliminar librería + Mostrar más letras con diacríticos en la ventana emergente + Mostrar las variantes definidas en los idiomas del teclado (por defecto) + Añadir variantes comunes + Añadir todas las variantes disponibles + Detección de URL + Intenta detectar las URL y similares como una sola palabra + Idiomas y diseños + Localizar fila de números + Preferir los números localizados a los latinos + Seleccionar fuente de pistas + Seleccionar el orden de las teclas emergentes + Fila de números + Idioma + Lengua (prioridad) + Diseño + Símbolos + Seleccionar teclas de la barra de herramientas + Portapapeles + Definido por el usuario + Definido por el usuario (noche) + Color del texto de tecla + Texto de la pista de la tecla + Licencia Pública General GNU v3.0 + Cerrar + Descripción de los elementos ocultos + Cortar + Limpiar portapapeles + Entrada de voz + Seleccionar palabra + Modo de una mano + A la derecha + Izquierda + Derecha + Arriba + Abajo + Mostrar pistas si al pulsar prolongadamente una tecla se activan funciones adicionales + Pulsación larga de la tecla de símbolos para cambiar al teclado numérico + Reducir la distancia de las teclas + Kaitag + Alfabeto (Bépo) + Añadir diseño personalizado + Cargar archivo + No se puede leer el archivo + Seleccione un archivo en un formato compatible. La información sobre los formatos está disponible %s. + Copiar diseño existente + Establecer el nombre del diseño + ¿Borrar realmente el diseño personalizado %s? + Error de diseño: %s + Pulse para editar el diseño sin procesar + Personaliza los símbolos y la disposición de los números + Los diseños (excepto los símbolos) exponen ajustes internos que aún pueden cambiar. Si esto ocurre, es posible que el diseño personalizado deje de funcionar correctamente. + Símbolos + Símbolos (árabe) + Establecer imagen de fondo + ¿Poner la imagen en modo día o noche? + Configurar el teclado + Diccionarios + Diccionario interno principal + Añadir diccionario desde archivo + Seleccionar idioma + Añadir a %s + ¿Realmente desea reemplazar el diccionario añadido por el usuario \"%1$s\"? +\n +\nDiccionario actual: +\n%2$s +\n +\nNuevo diccionario: +\n%3$s + Reemplazar diccionario + ¿Eliminar realmente el diccionario añadido por el usuario \"%s\"? + No mostrar de nuevo + Seleccione el diccionario que desea añadir. Los diccionarios en formato .dict pueden descargarse de %s. + aquí + %s (experimental) + Error: El archivo seleccionado no es un archivo de diccionario válido + El fichero seleccionado es para %1$s, pero se esperaba %2$s. ¿Seguir utilizándolo para %2$s? + Error: script no compatible con este teclado + Seguir usando + Error al cargar el archivo del diccionario + Estilo + Esta palabra ya está presente en el diccionario del usuario %s. Por favor, escriba otra. + Redondeado + Colores + Colores (noche) + Seguir color en la barra de navegación + Claro + Claro Holo + Rosa + Arena + Ajustar colores + Escoger colores automáticamente + Mostrar sólo los colores principales + Mostrar todos los colores + Haga clic para ver la vista previa + Seleccionar colores para el texto y los fondos + Fondo del teclado + Texto de la tira de sugerencias + Fondo de la tecla + Fondo de la tecla funcional + Fondo de la barra espaciadora + Texto de la barra espaciadora + Acento + Entrada gestual + Acerca de + Versión + Ver en GitHub + Guardar registro + Licencia de código abierto + Apariencia + almacenamiento protegido del dispositivo + Mostrar características que pueden pasar desapercibidas + Dirección variable de la barra de herramientas + Invertir la dirección cuando se selecciona un subtipo de teclado de derecha a izquierda + %s (Probhat) + %s (Sebeolsik 390) + %s (Sebeolsik Final) + Océano + Oscuro + Más oscuro + Negro + Colores dinámicos + Gris azulado + Café + Chocolate + Nuboso + Bosque + Indigo + A la izquierda + %s (Student) + Utilizar el diccionario personal del dispositivo para almacenar las palabras aprendidas + Ignorar la petición de otras aplicaciones de desactivar las sugerencias (puede producir problemas) + Mostrar pistas de funciones + Escala del borde inferior + Kaitag (%s) + ¿A qué idioma debe añadirse el diccionario \"%1$s\" para %2$s? + "Sin diccionario, sólo obtendrá sugerencias para el texto que haya introducido anteriormente.<br> +\n Puede descargar los diccionarios %1$s, o comprobar si se puede descargar directamente un diccionario para \"%2$s\" %3$s." + ► Pulsando prolongadamente la tecla del portapapeles (la opcional en la tira de sugerencias) se pega el contenido del portapapeles del sistema. <br> <br> ► Al pulsar prolongadamente las teclas de la barra de sugerencias, éstas se fijan a la barra de sugerencias. <br> <br> ► Pulse prolongadamente la tecla Coma para acceder a la vista del portapapeles, la vista de emoji, el modo de una mano, Ajustes o Cambiar idioma: <br> \t• La vista Emoji y el cambio de idioma desaparecerán si tienes activada la tecla correspondiente; <br> \t• Para algunas distribuciones no es la tecla Coma, sino la tecla en la misma posición (por ejemplo, es \'q\' para la distribución Dvorak). <br> <br> ► Cuando el modo incógnito está activado, no se aprende ninguna palabra ni se añaden emojis a los favoritos. <br> <br> ► Pulse el icono Incógnito para acceder a la barra de herramientas. <br> <br> ► Tecla deslizante: Desliza desde Mayúsculas a otra tecla para escribir una sola mayúscula: <br> \t• Esto también funciona con la tecla \'?123\' para escribir un solo símbolo del teclado de símbolos, y con las teclas relacionadas. <br> <br> ► Mantenga pulsada una sugerencia en la tira de sugerencias para mostrar más sugerencias, y el botón suprimir para eliminar esta sugerencia. <br> <br> ► Desliza el dedo hacia arriba desde una sugerencia para abrir más sugerencias y suéltalo para seleccionarla. <br> <br> ► Mantén pulsada una entrada del historial del portapapeles para anclarla (se mantendrá en el portapapeles hasta que la desancles). <br> <br> ► Puedes añadir diccionarios abriéndolos en un explorador de archivos: <br> \t• Esto sólo funciona con <i>content-uris</i> y no con <i>file-uris</i>, lo que significa que puede no funcionar con algunos exploradores de archivos. <br> <br> <i>Modo depuración / depurar APK</i> <br> <br> \t• Pulsa prolongadamente una sugerencia para mostrar el diccionario de origen.<br> <br> \t• Cuando se utiliza el APK de depuración, se puede encontrar la Configuración de Depuración dentro de las Preferencias Avanzadas, aunque la utilidad es limitada excepto para volcar diccionarios en el registro. <br> <br> \t• En caso de que se bloquee una aplicación, se te preguntará si quieres los registros de bloqueo cuando abras la Configuración. <br> <br> \t• Al utilizar la escritura multilingüe, la barra espaciadora mostrará un indicador de confianza utilizado para determinar el idioma utilizado en ese momento. <br> <br> \t• Las sugerencias tendrán unos pequeños números en la parte superior mostrando alguna puntuación interna y el diccionario de fuentes (se puede desactivar). <br> <br> ► Para usuarios que realizan copias de seguridad manuales con acceso root: A partir de Android 7, el archivo de preferencias compartidas no está en la ubicación predeterminada, porque la aplicación está utilizando %s. <br> Esto es necesario para poder leer los ajustes antes de desbloquear el dispositivo, por ejemplo, en el arranque. <br> El archivo se encuentra en /data/user_de/0/package_id/shared_prefs/, aunque esto puede depender del dispositivo y de la versión de Android. \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index af0067079..157059e6a 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -231,7 +231,7 @@ Modo claro Holo modo claro Modo oscuro - Modo oscuro + Negro Color dinámico Marrón Ajustar colores diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index c7d350228..07b4d2dcd 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -3,15 +3,14 @@ Copyright (C) 2008 The Android Open Source Project modified SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only ---> - +--> "Bilatu kontaktu-izenak" "Ortografia-zuzentzaileak kontaktuak erabiltzen ditu" "Egin dar-dar sakatzean" "Soinua teklak sakatzean" - "Handitu teklak, sakatzean" + Handitu tekla sakatzean "Hobespenak" - "Idazketa lerrakorra" + Keinu-idazketa "Testu-zuzenketa" "Aurreratuak" "Gaia" @@ -19,11 +18,11 @@ "Aldatu idazketa-metodoa" "Hizkuntza aldatzeko tekla" "%s ms" - "Sistemaren balio lehenetsia" + Sistemak lehenetsitakoa "Iradoki kontaktu-izenak" - Erabili kontaktuetako izenak iradokizunak eta zuzenketak egiteko + Erabili kontaktuen izenak iradokizunak eta zuzenketak egiteko "Iradokizun pertsonalizatuak" - "Zuriune bikoitzarekin puntu" + Zuriunearen sakatze bikoitzaren tartea "Zuriune-tekla bi aldiz sakatuta, puntua eta zuriunea txertatzen dira" "Maiuskula automatikoak" "Ezarri esaldi bakoitzaren lehenengo hizkia maiuskulaz" @@ -35,14 +34,14 @@ "Blokeatu hitz iraingarriak" "Ez iradoki iraingarria izan daitekeen hitzik" "Zuzenketa automatikoa" - "Zuzendu auto. zuriuneak eta puntuazioa gaizki idatzitako hitzetan" + Zuriuneak eta puntuak automatikoki zuzentzeko gaizki idatzitako hitzak "Desaktibatu" "Zuhurra" - "Neurriz gainekoa" - "Oso neurriz gainekoa" + Ausarta + Oso ausarta "Hurrengo hitza iradokitzea" "Erabili aurreko hitza iradokizunak egiteko" - "Gaitu idazketa lerrakorra" + Gaitu keinu-idazketa "Idatzi hitzak hizki batetik bestera hatza lerratuta" "Erakutsi keinuaren bidea" "Flotatze dinamikodun aurrebista" @@ -62,33 +61,33 @@ %s (Tradizionala) %s (Trinkoa) "Ez dago hizkuntzarik (alfabetoa)" - "alfabetoa (QWERTY)" - "alfabetoa (QWERTZ)" - "alfabetoa (AZERTY)" - "alfabetoa (Dvorak)" - "alfabetoa (Colemak)" - "alfabetoa (PC)" + Alfabetoa (QWERTY) + Alfabetoa (QWERTZ) + Alfabetoa (AZERTY) + Alfabetoa (Dvorak) + Alfabetoa (Colemak) + Alfabetoa (PC) "Emojiak" "Gehitu" "Kendu" "Gorde" "Hizkuntza" "Diseinua" - "Tekla sakatzearen dardararen iraupena" + Tekla sakatzean dardararen iraupena "Tekla sakatzearen bolumena" "Luze sakatzearen atzerapena" "Teklatu fisikorako emojiak" "Teklatu fisikoko Alt tekla sakatuta emojiak agertzen dira" "Lehenetsia" Ongi etorri %s aplikaziora - "Idazketa lerrakorrarekin" + Keinu-idazketarekin "Hasi erabiltzen" "Hurrengo urratsa" %s konfiguratzen %s gaitu Hautatu \"%s\" Hizkuntza eta idazketa ataleko ezarpenetan &. Horrek gailuan exekutatzea baimenduko dio. %s gaituta duzu Hizkuntza eta idazketa ataleko ezarpenetan & beraz, urratsa eginda dago. Ekin hurrengoari! - "Gaitu Ezarpenak atalean" + Gaitu Ezarpenetan Aldatu %s aplikaziora Ondoren, hautatu \"%s\" idazketa-metodo aktibo gisa. "Aldatu idazketa-metodoak" @@ -96,7 +95,7 @@ Gogokoen dituzun aplikazioetan idatz dezakezu dagoeneko %s erabilita. "Amaituta" "Erakutsi aplikazioaren ikonoa" - "Bistaratu aplikazioaren ikonoa abiarazlean" + Bistaratu aplikazioaren ikonoa panelean "Hiztegi gehigarriak" "Hiztegien ezarpenak" "Hiztegia erabilgarri" @@ -124,36 +123,36 @@ Esperimentala Denetarikoa Mugarik ez - %sminutu. - Erakutsi beti zenbaki-errenkada - Erakutsi aholku nagusiak - Luzaroko pultsazioen aholkuak erakutsi - Arbelaren historia aktibatu + %sminutu + Erakutsi beti zenbakien errenkada + Erakutsi teklen iradokizunak + Erakutsi sakatze luzeen iradokizunak + Gaitu arbelaren historia Desgaituta badago, arbeleko teklak arbeleko edukia itsatsiko du halakorik badago - Historikoaren atxikitutako denbora - Hitz berrien ikasketa desaktibatu + Historikoa gordetzeko denbora + Desgaitu hitz berrien ikasketa Tekla gehiago - zenbaki errenkada - Espazio tekla luze sakatzeak sarrera-metodoa hautatzeko menua piztuko du - HeliBoard Ortografia Zuzentzailea - Autoespazioa puntuazioen ondoren - Ezezagun modua behartu - %s (Akkhor hizkuntza) + Zenbakien errenkada + Zuriune tekla luze sakatzeak sarrera-metodoa hautatzeko menua erakutsiko du + HeliBoard ortografia zuzentzailea + Zuriunea puntuazioen ondoren + Behartu ezkutuko modua + %s (Akkhor hizkuntza) Sarrera - Gako-ertzak + Teklen ertzak Egun/gau modu automatikoa Itxurak sistemaren ezarpenei jarraituko die - HeliBoard Ezarpenak - Giltza gehigarriak - HeliBoard Ortografia Zuzentzailearen Ezarpenak - Emoji giltza - Ezabatu irristatzea - Ezabatu teklatik hatza irristatu, aldiberean testuen zati handiagoak hautatzeko eta ezabatzeko + HeliBoard ezarpenak + Tekla gehigarriak + HeliBoard ortografia zuzentzailearen ezarpenak + Emoji tekla + Desgaitu irristatzea + Ezabatu teklatik hatza irristatu, testuaren zati handiagoak hautatzeko eta ezabatzeko Sartu automatikoki zuriunea puntuazioen ondoren hitz berri bat idaztean - Aldatu idazketa-metodoa espazio teklarekin + Aldatu idazketa-metodoa zuriune teklarekin Teklatuaren altuera eskala - Alfabeto (Colemak Mod-DH) - Alfabeto (Workman) + Alfabetoa (Colemak Mod-DH) + Alfabetoa (Workman) "Erabili sistemaren hizkuntzak" "Aukeratu idazketa-metodoa" "Desegin" @@ -161,10 +160,193 @@ "Iradokizunak hobetzeko, ikasi komunikazioetatik eta idazten duzunetik" "Joan" "Hurrengoa" - "Atzera" + Aurrekoa "Eginda" "Bidali" "Bilatu" "Pausatu" "Itxaron" + Aldatu biak + Aldatu hizkuntza + Abisua: ezarpen hau desgaituz gero, ikasitako datuak garbituko dira + Zabaldu tartea + Gehitu hitzak hiztegi pertsonalera + Erabili gailuko hiztegi pertsonala ikasitako hitzak gordetzeko + Erakutsi beti iradokizunak + Zuzenketa automatiko gehiago + Zuzendu automatikoki sarrera-eremuak esplizituki eskatzen ez badu ere + Autozuzenketaren konfiantza + Babeskopia-errorea: %s + Babeskopia + Errore bat gertatu da babeskopiaren berreskuratzean: %s + Babeskopia eta berreskuratzea + Gorde edo kargatu fitxategitik. Abisua: berreskuratzeak lehendik dauden datuak gainidatziko ditu + Ez ikusi iradokizunak desgaitzeko beste aplikazioen eskaerak (arazoak sor ditzake) + Berreskuratu + Idazketa eleanitza + Kargatu keinu-idazketaren liburutegia + Hornitu jatorrizko liburutegi bat keinu-idazketa gaitzeko + \'%s\' liburutegia beharko duzu. Bateraezinak diren liburutegiek huts egin dezakete keinu-idazketa erabiltzean. +\n +\nAbisua: kanpoko kodea kargatzea segurtasun arriskua izan daiteke. Erabili soilik konfiantzazko iturri bateko liburutegi bat. + Liburutegiko fitxategi ezezaguna. Ziur iturri fidagarri batetik lortu duzula eta \'%s\'rentzat dela? + Kargatu liburutegia + Ezabatu liburutegia + Erakutsi pop-up leihoan ikur diakritikoak dituzten letra gehiago + Erakutsi teklatuko hizkuntzetako aldaerak (lehenetsia) + Gehitu ohiko aldaerak + Gehitu eskuragarri dauden aldaera guztiak + URL hautematea + Saiatu URLak eta antzekoak hitz bakar gisa antzematen + Hizkuntzak eta diseinuak + Aurkitu zenbakien errenkada + Hautatu iradokizunen iturria + "Hiztegirik gabe, aurretik idatzitako testuko iradokizunak soilik jasoko dituzu.<br> +\n %1$s hiztegiak deskarga ditzakezu edo egiaztatu \"%2$s\" hiztegi bat zuzenean %3$s deskargatu daitekeen." + Hautatu gehitzeko hiztegi bat. .dict formatuan dauden hiztegiak %s deskargatu daitezke. + Ilunago + Ahots sarrera + Sakatu luze ikurren tekla teklatu numerikorako + Murriztu teklen arteko tarteak + Beheko betegarriaren eskala + Kaitag (%s) + ikurrak + Pisua: + Marroia + Erabiltzaileak definituta (gauekoa) + Egin klik aurrebista ikusteko + Hautatu koloreak testurako eta atzeko planoetarako + Kaitag + Itxi + Sakatu hizkuntza ezarpenak irekitzeko + Ikusi GitHuben + Kode irekiko lizentzia + GNU General Public License v3.0 + Estiloa + Gorde erregistroa + Zuriune-barrako keinu bertikala + Zuriune-barrako keinu horizontala + Bat ere ez + Mugitu kurtsorea + Nahiago lokalizatutakoak zenbaki latindarren aldean + Teklen pop-upeko ordena hautatu + Zenbakien errenkada + Hizkuntza + Hizkuntza (lehenetsia) + Diseinua + Ikurrak + Hautatu tresna barraren teklak + Arbela + Esku bakarreko modua + Guztiz ezkerrean + Guztiz eskuinean + Ezkerrean + Eskuinean + Goian + Behean + Erakutsi aholku funtzionalak + Erakutsi aholkuak tekla bat luze sakatzeak funtzionalitate gehigarriak abiarazten baditu + Argia + Moztu + Garbitu arbela + Hautatu hitza + %s (Sebeolsik 390) + %s (Sebeolsik Final) + Alfabetoa (Bépo) + Gehitu diseinu pertsonalizatua + Hautatu fitxategi bat formatu bateragarri batean. Formatuei buruzko informazioa eskuragarri dago %s. + Kargatu fitxategia + Ezin da fitxategia irakurri + Kopiatu lehendik dagoen diseinua + Ezarri diseinuaren izena + Benetan ezabatu %s diseinu pertsonalizatua? + Sakatu diseinu gordina editatzeko + Pertsonalizatu ikurrak eta zenbaki-diseinuak + Diseinuek (ikurrak izan ezik) oraindik alda daitezkeen barne ezarpenak erakusten dituzte. Hori gertatzen bada, baliteke diseinu pertsonalizatuak behar bezala ez funtzionatzea. + Ikurrak (Arabiarrak) + ikur gehiago + Telefonoa + Telefonoaren ikurrak + Zenbakiak + Zenbakien teklatua + Zenbakien teklatua (horizontala) + Ezarri atzeko planoko irudia + Irudia eguneko edo gaueko moduan ezarri nahi duzu? + Eguna + Gaua + Konfiguratu teklatua + Ordeztu hiztegia + Erabiltzaileak gehitutako \"%s\" hiztegia benetan kendu? + Ez erakutsi berriro + hemen + %s (esperimentala) + Errorea: hautatutako fitxategia ez da baliozko hiztegi-fitxategi bat + Hautatutako fitxategia %1$srentzat da, baina %2$s espero zen. Oraindik %2$s erabiltzen al duzu? + Errorea: script-a ez da bateragarria teklatu honekin + Erabili oraindik + Errore bat gertatu da hiztegi-fitxategia kargatzean + Hitz hau %s erabiltzailearen hiztegian dago jada. Mesedez, idatzi beste bat. + Gehitu hitz bat + Borobildua + Koloreak + Koloreak (gauekoak) + Koloretako nabigazio-barra + Holo Zuria + Iluna + Gris urdina + Txokolate + Hodeitsu + Basoa + Indigo + Ozeanoa + Arrosa + Hondarra + Violeta + Erabiltzaileak definituta + Doitu koloreak + Doitu koloreak (gauekoa) + Aukeratu kolorea automatikoki + Erakutsi kolore nagusiak soilik + Erakutsi kolore guztiak + Teklatuaren atzeko planoa + Teklaren testua + Teklaren aholku-testua + Iradokizunaren testua + Teklaren atzeko planoa + Funtzio-teklaren atzeko planoa + Zuriune-teklaren atzeko planoa + Zuriune-teklaren testua + Azentua + Keinu-sarrera + Honi buruz + Bertsioa + Itxura + Ezkutuko ezaugarrien deskribapena + Erakutsi oharkabean pasa daitezkeen ezaugarriak + gailuak babestutako biltegiratzea + Tresna-barraren norabide aldakorra + ► Arbeleko tekla luze sakatuz (iradokizunen zerrendako aukerakoa) sistemako arbeleko edukia itsatsi egiten da. <br> <br> Iradokizun-zerrendako tresna-barrako teklak luze sakatuz gero, iradokizun-zerrendan finkatzen dira. <br> <br> Luze sakatu koma tekla arbelaren ikuspegia, emoji ikuspegia, esku bakarreko modua, ezarpenak edo hizkuntza aldatzeko atzitzeko: <br> • Emoji ikuspegia eta hizkuntza aldatzea desagertuko dira dagokion tekla baduzu. gaituta; <br> • Diseinu batzuetan ez da koma-tekla, posizio berean dagoen tekla baizik (adibidez, \'q\' da Dvorak diseinurako). <br> <br> Ezkutuko modua gaituta dagoenean, ez da hitzik ikasiko eta ez da emojirik gehituko azken berrietan. <br> <br> Sakatu ezkutuko ikonoa tresna-barrara sartzeko. <br> <br> Irristatu teklaren sarrera: Irristatu shift batetik beste tekla batera maiuskulazko tekla bakarra idazteko: <br> • Honek \'?123\' teklak ere balio du sinboloen teklatuan ikur bakarra idazteko eta erlazionatutako gakoak. <br> <br> Luze sakatu iradokizunen zerrendako iradokizun bat iradokizun gehiago erakusteko, eta ezabatzeko botoia iradokizun hau kentzeko. <br> <br> Irristatu gorantz iradokizun batetik iradokizun gehiago irekitzeko, eta askatu iradokizuna hautatzeko. <br> <br> Luze sakatu arbeleko historiako sarrera bat ainguratzeko (jar ezazu arbelean aingura kendu arte). <br> <br> Hiztegiak gehi ditzakezu fitxategi-esploratzaile batean irekita: <br> • Honek <i>content-uris</i>-ekin bakarrik funtzionatzen du eta ez <i>file-uris</i>-ekin , hau da, baliteke fitxategi-esploratzaile batzuekin ez funtzionatzea. <br> <br> <i>Arazte modua / APK arazketa</i> <br> <br> • Luze sakatu iradokizun bat iturburu-hiztegia erakusteko.<br> <br> • Arazketa APK erabiltzean, dezakezu bilatu Arazte-ezarpenak Hobespen aurreratuen barruan, nahiz eta erabilgarritasuna mugatua den hiztegiak erregistroan isurtzea izan ezik. <br> <br> • Aplikazioaren hutsegite bat gertatuz gero, Ezarpenak irekitzean hutsegiteen erregistroak nahi dituzun galdetuko zaizu. <br> <br> • Idazketa eleanitza erabiltzean, zuriune-barrak unean erabiltzen den hizkuntza zehazteko erabiltzen den konfiantza-balioa erakutsiko du. <br> <br> • Iradokizunek goian zenbaki txiki batzuk izango dituzte barne puntuazio eta iturburu-hiztegiren bat erakutsiz (desgaitu daiteke). <br> <br> Root sarbidearekin eskuzko babeskopiak egiten dituzten erabiltzaileentzat: Android 7-tik aurrera, partekatutako hobespen-fitxategia ez dago lehenetsitako kokapenean, aplikazioa %s erabiltzen ari delako. <br> Hau beharrezkoa da ezarpenak irakurri ahal izateko gailua desblokeatu aurretik, adibidez. abioan. <br> Fitxategia /data/user_de/0/package_id/shared_prefs/ helbidean dago, baina gailuaren eta Android bertsioaren araberakoa izan daiteke. + %s (Probhat) + Diseinu errorea: %s + Beltza + Kolore dinamikoak + Alderantzikatu norabidea eskuinetik ezkerrera teklatu mota hautatzen denean + %s (Ikasle) + Hiztegiak + Barneko hiztegi nagusia + Gehitu hiztegia fitxategitik + Zein hizkuntzari gehitu behar zaio %2$s-ren \"%1$s\" hiztegia? + Hautatu hizkuntza + Gehitu %s-ri + Erabiltzaileak gehitutako \"%1$s\" hiztegia benetan ordezkatu? +\n +\nUneko hiztegia: +\n%2$s +\n +\nHiztegi berria: +\n%3$s + Erabili beti erdiko iradokizuna + Zuriunea edo puntuazioa sakatzean, erdiko iradokizuna sartuko da + Itxi arbelaren historia + Hautatu arbeleko tresna-barrako teklak \ No newline at end of file diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml new file mode 100644 index 000000000..30b51741d --- /dev/null +++ b/app/src/main/res/values-fil/strings.xml @@ -0,0 +1,62 @@ + + + Mga Setting ng HeliBoard + Gagamitin ng spell checker ang mga entry mula sa iyong listahan ng contact + Mag-vibrate sa keypress + Tunog sa keypress + Mag-popup sa keypress + Mga Kagustuhan + Gesture na Pagta-type + Pagwawasto ng text + Advanced + Tema + Input + Mga karagdagang key + History ng clipboard + Pagwawasto + Pang-eksperimento + Miscellaneous + Paganahin ang split keyboard + Distansya ng split + Lumipat sa iba pang mga input method + Magpalit ng Wika + Ilipat ang pareho + Key sa paglipat ng wika + Key ng emoji + %s min + Default ng sistema + Walang limitasyon + Imungkahi ang mga pangalan ng Mga Contact + Babala: Ang pag-disable ng setting na ito ay iki-clear ang mga natutong data + Personalized na mungkahi + Gamitin ang personal na diksyunaryo ng device para ilagay ang mga natutong salita + Double-space period + Auto-capitalization + I-capitalize ang unang salita ng bawat pangungusap + Personal na diksyunaryo + Mga add-on na diksyunaryo + Paunang diksyunaryo + Ipakita ang mga suhestiyon sa pagwawasto + Ipakita ang mga iminumungkahing salita habang nagta-type + Palaging ipakita ang mga suhestiyon + Huwag pansinin ang hiling ng mga ibang app na i-disable ang mga suhestiyon (maaring magdulot ng mga isyu) + I-block ang masakit na salita + Auto na pagwawasto + Automatikong tinatama ng spacebar at bantas ang maling na-type + Higit pang auto na pagwawasto + Awtomatikong iwasto kahit hindi malinaw hiniling ng input field + Tapang ng auto na pagwawasto + Off + Mababang-loob + Agresibo + Spell Checker ng HeliBoard + Mga Setting ng Spell Checker ng Heliboard + Hanapin ang mga pangalan ng contact + Mga mungkahi + %s ms + Gamitin ang pangalan mula sa Mga Contact para sa mungkahi at pagtatama + Idagdag ang mga salita sa personal na diksyunaryo + Naglalagay ng tuldok na may puwang ang pag-double tap sa spacebar + Huwag magmungkahi ng mga maaring nakakapanakit na salita + Napaka-agresibo + \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index eeb808ba8..4d03b5d53 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -304,7 +304,40 @@ Nouveau dictionnaire: Description des fonctionnalités cachées Montre les caractéristiques qui peuvent passer inaperçues stockage protégé de l\'appareil - ► Un appui long sur la touche presse-papiers (celle qui est optionnelle dans la bande de suggestions) permet de coller le contenu du presse-papiers. <br> <br> ► Un appui long sur les touches de la barre d\'outils permet de les épingler à la bande de suggestions. <br> <br> ► Appuyez longuement sur la virgule pour accéder à l\'affichage du presse-papiers, des emojis, au mode à une main, aux paramètres ou au changement de langue : <br> \t• l\'affichage des emojis et le changement de langue disparaîtront si les touches correspondantes sont affichées ; <br> \t• pour certaines dispositions, ce n\'est pas sur la touche virgule mais sur la touche à la même position (par exemple, c\'est `q` pour la disposition Dvorak). <br> <br> ► Lorsque le mode incognito est activé, aucun mot n\'est appris et aucun emojis n\'est ajouté à la catégorie récent. <br> <br> ► Appuyez sur l\'icône incognito pour ouvrir la barre d\'outils. <br> <br> ► Saisie par balayage des touches : passez de la touche Maj à une autre touche pour taper un seul caractère en majuscule : <br> \t• fonctionne également avec la touche `?123` qui permet de taper un seul symbole à partir du clavier des symboles, ainsi que pour les touches associées. <br> <br> ► Un appui long sur une suggestion dans la bande de suggestions permet d\'afficher d\'autres suggestions et un bouton de suppression qui permet de supprimer cette suggestion. <br> <br> ► Balayez vers le haut à partir d\'une suggestion pour ouvrir d\'autres suggestions, puis relâchez la pression sur la suggestion pour la sélectionner. <br> <br> ► Appuyez longuement sur une entrée dans l\'historique du presse-papiers pour l\'épingler (elle reste dans le presse-papiers jusqu\'à ce que vous la désépingliez). <br> <br> ► Vous pouvez ajouter des dictionnaires en les ouvrant dans un explorateur de fichiers : <br> \t• ne fonctionne qu\'avec <i>content-uris</i> et non avec <i>file-uris</i>, ce qui signifie que cela peut ne pas fonctionner avec certains explorateurs de fichiers. <br> <br> <i> Mode debug / APK de debogage </i> <br> <br> \t• Un appui-long sur une suggestion montre la source du dictionnaire.<br> <br> \t• Lorsque vous utilisez l\'APK de débogage, vous trouverez les paramètres de débogage dans les préférences avancées, bien qu\'ils ne soient pas très utiles, sauf pour transférer les dictionnaires dans le journal de débogage. <br> <br> \t• Lorsque l\'application se bloque, un message vous demande où vous souhaitez enregistrer les journaux d\'incidents lorsque vous ouvrez les paramètres. <br> <br> \t• Lors de la saisie multilingue, la barre d\'espace affiche une valeur de fiabilité utilisée pour déterminer la langue en cours d\'utilisation. <br> <br> \t• Les suggestions seront accompagnées de petits chiffres indiquant un score interne et la source du dictionnaire (peut être désactivé). <br> <br> ► Pour les utilisateurs effectuant des sauvegardes manuelles avec un accès root : à partir d\'Android 7, le fichier de préférences partagées ne se trouve pas dans l\'emplacement par défaut car l\'application utilise le %s. <br> Cela est nécessaire pour que les paramètres puissent être lus avant que l\'appareil ne soit déverrouillé, par exemple au démarrage. <br> Le fichier se trouve dans <i>/data/user_de/0/package_id/shared_prefs/</i>, mais cela peut dépendre de l\'appareil et de la version d\'Android. + ► L\'appui long sur les touches de la barre d\'outils permet d\'obtenir des fonctionnalités supplémentaires : <br> +\n\t• presse-papier &#65515; coller <br> +\n\t• déplacer vers la gauche/droite &#65515; début de ligne/fin de ligne <br> +\n\t• déplacer vers le haut/bas &#65515; début de page/fin de page <br> +\n\t• copier &#65515; tout copier <br> +\n\t• sélectionner mot &#65515; tout sélectionner <br> +\n\t• annuler &#8596; restaurer <br> <br> +\n► Un appui long sur les touches de la barre d\'outils permet de les épingler à la bande de suggestions. <br> <br> +\n► Appuyez longuement sur la virgule pour accéder à l\'affichage du presse-papiers, des emojis, au mode à une main, aux paramètres ou au changement de langue : <br> +\n\t• L\'affichage des emojis et le changement de langue disparaîtront si les touches correspondantes sont affichées ; <br> +\n\t• Pour certaines dispositions, ce n\'est pas sur la touche virgule mais sur la touche à la même position (par exemple, c\'est `q` pour la disposition Dvorak <br> <br> +\n► Lorsque le mode incognito est activé, aucun mot n\'est appris et aucun emojis n\'est ajouté à la catégorie récent. <br> <br> +\n► Appuyez sur l\'icône incognito pour ouvrir la barre d\'outils. <br> <br> +\n► Saisie par glissement des touches : passez de la touche majuscule à une autre touche pour taper un seul caractère en majuscule : <br> +\n\t• Fonctionne également avec la touche `?123` qui permet de taper un seul symbole à partir du clavier des symboles, ainsi que pour les touches associées <br> <br> +\n► Maintenez la touche majuscule ou symbole enfoncée, appuyez sur une ou plusieurs touches, puis relâchez la touche majuscule ou symbole pour revenir au clavier précédent. <br> <br> +\n► Un appui long sur une suggestion dans la bande de suggestions permet d\'afficher d\'autres suggestions et un bouton de suppression qui permet de supprimer cette suggestion. <br> <br> +\n► Balayez vers le haut à partir d\'une suggestion pour ouvrir d\'autres suggestions, puis relâchez la pression sur la suggestion pour la sélectionner. <br> <br> +\n► Appuyez longuement sur une entrée dans l\'historique du presse-papiers pour l\'épingler (elle reste dans le presse-papiers jusqu\'à ce que vous la désépingliez). <br> <br> +\n► Glissez vers la gauche dans l\'affichage du presse-papiers pour supprimer une entrée (sauf lorsqu\'elle est épinglée). <br> <br> +\n► Sélectionnez un texte et appuyez sur la touche majuscule pour passer des majuscules aux minuscules. <br> <br> +\n► Vous pouvez ajouter des dictionnaires en les ouvrant dans un explorateur de fichiers : <br> +\n\t• Ne fonctionne qu\'avec <i>content-uris</i> et non avec <i>file-uris</i>, ce qui signifie que cela peut ne pas fonctionner avec certains explorateurs de fichiers. <br> <br> +\n► Pour les utilisateurs effectuant des sauvegardes manuelles avec un accès root : <br> +\n\t• À partir d\'Android 7, le fichier de préférences partagées ne se trouve pas dans l\'emplacement par défaut car l\'application utilise le %s. Cela est nécessaire pour que les paramètres puissent être lus avant que l\'appareil ne soit déverrouillé, par exemple au démarrage ; <br> +\n\t• Le fichier se trouve dans <i>/data/user_de/0/package_id/shared_prefs/</i> mais cela peut dépendre de l\'appareil et de la version d\'Android. <br> <br> +\n<i><b>Mode debug / APK de debogage</b></i> <br> <br> +\n► Un appui-long sur une suggestion montre la source du dictionnaire. <br> <br> +\n► Lorsque vous utilisez l\'APK de débogage, vous trouverez les paramètres de débogage dans les préférences avancées, bien qu\'ils ne soient pas très utiles, sauf pour transférer les dictionnaires dans le journal de débogage. <br> +\n\t• Pour un APK en version release, vous devez taper plusieurs fois sur la version dans <i>À propos</i>, puis vous trouverez les paramètres de débogage dans les <i>Paramètres avancés</i>. <br> +\n\t• Lorsque l\'option <i>Show suggestion infos</i> est activée, les suggestions sont accompagnées de petits chiffres indiquant un score interne et le dictionnaire source. <br> <br> +\n► Lorsque l\'application se bloque, un message vous demande où vous souhaitez enregistrer les journaux d\'incidents lorsque vous ouvrez les paramètres. <br> <br> +\n► Lors de la saisie multilingue, la barre d\'espace affiche une valeur de fiabilité utilisée pour déterminer la langue en cours d\'utilisation. <br> <br> +\n► Les suggestions seront accompagnées de petits chiffres indiquant un score interne et la source du dictionnaire (peut être désactivé). "Améliore les suggestions en fonction des messages et des données saisies" "Accès" "Suiv." @@ -342,4 +375,13 @@ Nouveau dictionnaire: %s (Étudiant) %s (Probhat) Les mises en page (à l\'exception des symboles) exposent des paramètres internes qui peuvent encore changer. Dans ce cas, la mise en page personnalisée risque de ne plus fonctionner correctement + Toujours utiliser la suggestion du milieu + En appuyant sur espace ou sur un signe de ponctuation, la suggestion du milieu sera saisie + Fermer le presse-papiers + Sélectionner les touches de la barre d\'outils du presse-papiers + %s (Étendu) + Mansi + Mansi (%s) + Afficher plus de couleurs + Ce paramètre expose toutes les couleurs utilisées en interne. La liste des couleurs peut être modifiée à tout moment. Il n\'y a pas de couleur par défaut et les noms ne seront pas traduits. \ No newline at end of file diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index e5ed8df76..3db7a2e12 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -345,4 +345,13 @@ %s (Estudante) Tecla con pulsación longa para teclado numérico Cortar + Usar sempre a suxestión do medio + Ao premer no espazo ou puntuación, escríbese a suxestión do medio + Escoller teclas da barra de ferramentas do portapapeis + Pechar historial do portapapeis + %s (Extendido) + Mansi + Mansi (%s) + Mostrar máis cores + Este axuste expón todas as cores que se usan internamente. A lista de cores pode cambiar en calquera momento. Non hai cor por defecto, e os nomes non serán traduciddos. \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index c368e0947..617a8c493 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -59,8 +59,8 @@ Hiszpański (USA) (%s) hinglish (%s) serbski (%s) - %s (tradycyjny) - %s (kompaktowa) + %s (tradycyjny) + %s (kompaktowy) standardowy (Alfabet) "Alfabet (QWERTY)" "Alfabet (QWERTZ)" @@ -143,7 +143,7 @@ Pokazuj wskazówki klawiszy Skala wysokości klawiatury Alfabet (Workman) - %s (Akkhor) + %s (Akkhor) Ustawienia HeliBoard Dodatkowe klawisze Wprowadzanie @@ -219,7 +219,40 @@ Błąd podczas dodawania słownika Preferuj liczby lokalne zamiast łacińskich Nieznany plik biblioteki. Czy na pewno pochodzi z zaufanego źródła i jest przeznaczony dla \\\'%s\\\'? - ► Długie naciśnięcie klawisza schowka (opcjonalnego na pasku sugestii) spowoduje wklejenie zawartości schowka systemowego. <br> <br> ► Długie naciśnięcie przycisków na pasku narzędzi spowoduje przypięcie ich do paska sugestii. <br> <br> ► Przytrzymaj klawisz przecinka, aby uzyskać dostęp do schowka, emotikonów, trybu jednej ręki, ustawień lub zmiany języka: <br> \t• Przyciski emotikonów i zmiany języka znikną, jeśli masz włączone odpowiednie klawisze; <br> \t• W niektórych układach nie jest to klawisz przecinka, tylko inny w tym samym miejscu (np. w układzie Dvorak jest to \'q\'). <br> <br> ► Gdy tryb incognito jest włączony, słowa nie będą pamiętane, a emotikony nie zostaną dodane do ostatnio używanych. <br> <br> ► Dotknij ikonkę incognito, aby uzyskać dostęp do paska narzędzi. <br> <br> ► Pisanie za pomocą przesuwania po klawiszu: przesuń palcem od klawisza Shift do innego klawisza, aby wpisać pojedynczą wielką literę: <br> \t• Działa to również w przypadku klawisza \\\'?123\\\' do wpisywania pojedynczego symbolu z klawiatury symboli oraz podobnych klawiszy. <br> <br> ► Przytrzymaj sugestię na pasku sugestii, aby wyświetlić więcej sugestii oraz przycisk usuwania, aby ją usunąć. <br> <br> ► Przesuń palcem w górę na sugestii, aby wyświetlić więcej sugestii i puść, aby wybrać sugestię. <br> <br> ► Przytrzymaj wpis w schowku, aby go przypiąć (pozostanie w schowku do czasu odpięcia). <br> <br> ► Możesz dodać słowniki otwierając je w eksploratorze plików: <br> \t• Działa to tylko z <i>content-uris</i> nie z <i>file-uris</i>, co oznacza, że może nie działać z niektórymi eksploratorami plików. <br> <br> <i>Tryb debugowania / aplikacja w wersji debug</i> <br> <br> \t• Przytrzymaj sugestię, aby wyświetlić słownik źródłowy.<br> <br> \t• Korzystając z aplikacji w wersji debug, w ustawieniach zaawansowanych znajdziesz ustawienia debugowania, choć ich użyteczność jest ograniczona, z wyjątkiem dodawania słowników do dziennika zdarzeń. <br> <br> \t• W przypadku awarii aplikacji, po otwarciu ustawień zostaniesz zapytany, czy chcesz zapisać dzienniki awarii. <br> <br> \t• Podczas pisania wielojęzycznego spacja wyświetli wartość służącą do określenia aktualnie używanego języka. <br> <br> \t• Sugestie będą miały na górze cyferki pokazujące wewnętrzne numery i słownik źródłowy (można to wyłączyć). <br> <br> ► Do użytkowników wykonujących ręczne kopie zapasowe z dostępem do roota: od Androida 7 udostępniony plik ustawień nie znajduje się w domyślnej lokalizacji, ponieważ aplikacja korzysta z %s. <br> Jest to konieczne, aby można było odczytać ustawienia przed odblokowaniem urządzenia, np. podczas uruchamiania. <br> Plik zwykle znajduje się w /data/user_de/0/package_id/shared_prefs/, ale może to zależeć od urządzenia i wersji Androida. + ► Długie naciśnięcie przypiętych klawiszy paska narzędzi zapewnia dodatkową funkcję: <br> +\n\t• schowek &#65515; wklej <br> +\n\t• w lewo/w prawo &#65515; na początek/na koniec <br> +\n\t• w górę/w dół &#65515; strona w górę/w dół <br> +\n\t• skopiuj &#65515; skopiuj wszystko <br> +\n\t• wybierz słowo &#65515; zaznacz wszystko <br> +\n\t• cofnij &#8596; ponów <br> <br> +\n► Długie naciśnięcie przycisków na pasku narzędzi spowoduje przypięcie ich do paska sugestii. <br> <br> +\n► Przytrzymaj klawisz przecinka, aby uzyskać dostęp do schowka, emotikonów, trybu jednej ręki, ustawień lub zmiany języka: <br> +\n\t• Przyciski emotikonów i zmiany języka znikną, jeśli masz włączone odpowiednie klawisze; <br> +\n\t• W niektórych układach nie jest to klawisz przecinka, tylko inny w tym samym miejscu (np. w układzie Dvorak jest to \'q\'). <br> <br> +\n► Gdy tryb incognito jest włączony, słowa nie będą pamiętane, a emotikony nie zostaną dodane do ostatnio używanych. <br> <br> +\n► Dotknij ikonkę incognito, aby uzyskać dostęp do paska narzędzi. <br> <br> +\n► Pisanie za pomocą przesuwania po klawiszu: przesuń palcem od klawisza Shift do innego klawisza, aby wpisać pojedynczą wielką literę: <br> +\n\t• Działa to również w przypadku klawisza \\\'?123\\\' do wpisywania pojedynczego symbolu z klawiatury symboli oraz podobnych klawiszy. <br> <br> +\n► Przytrzymaj Shift lub klawisz symboli, naciśnij jeden lub więcej klawiszy, a następnie puść Shift lub klawisz symboli, aby powrócić do poprzedniej klawiatury. <br> <br> +\n► Przytrzymaj sugestię na pasku sugestii, aby wyświetlić więcej sugestii oraz przycisk usuwania, aby ją usunąć. <br> <br> +\n► Przesuń palcem w górę na sugestii, aby wyświetlić więcej sugestii i puść, aby wybrać sugestię. <br> <br> +\n► Przytrzymaj wpis w schowku, aby go przypiąć (pozostanie w schowku do czasu odpięcia). <br> <br> +\n► Przesuń w lewo w widoku schowka, aby usunąć wpis (jeśli nie jest przypięty) <br> <br> +\n► Zaznacz tekst i naciśnij Shift, aby przełączać się między wielkimi i małymi literami. <br> <br> +\n► Możesz dodać słowniki otwierając je w eksploratorze plików: <br> +\n\t• Działa to tylko z <i>content-uris</i> nie z <i>file-uris</i>, co oznacza, że może nie działać z niektórymi eksploratorami plików. <br> <br> +\n► Do użytkowników wykonujących ręczne kopie zapasowe z dostępem do roota: <br> +\n\t• Od Androida 7 udostępniony plik ustawień nie znajduje się w domyślnej lokalizacji, ponieważ aplikacja korzysta z %s. Jest to konieczne, aby można było odczytać ustawienia przed odblokowaniem urządzenia, np. podczas uruchamiania. <br> +\n\t• Plik zwykle znajduje się w /data/user_de/0/package_id/shared_prefs/, ale może to zależeć od urządzenia i wersji Androida. <br> <br> +\n<i><b>Tryb debugowania / aplikacja debug</b></i> <br> <br> +\n► Przytrzymaj sugestię, aby wyświetlić słownik źródłowy. <br> <br> +\n► Korzystając z aplikacji debug, w ustawieniach zaawansowanych znajdziesz ustawienia debugowania, choć ich użyteczność jest ograniczona, z wyjątkiem dodawania słowników do dziennika zdarzeń. <br> +\n\t• W przypadku aplikacji release należy kilkukrotnie dotknąć jej wersję w sekcji <i>O aplikacji</i>, a następnie w <i>Ustawieniach zaawansowanych</i> znaleźć ustawienia debugowania. <br> +\n\t• Po włączeniu opcji <i>Pokaż informacje o sugestii</i>, sugestie będą miały na górze cyferki pokazujące wewnętrzne numery i słownik źródłowy. <br> <br> +\n► W przypadku awarii aplikacji, po otwarciu ustawień zostaniesz zapytany, czy chcesz zapisać dzienniki awarii. <br> <br> +\n► Podczas pisania wielojęzycznego spacja wyświetli wartość służącą do określenia aktualnie używanego języka. <br> <br> +\n► Sugestie będą miały na górze cyferki pokazujące wewnętrzne numery i słownik źródłowy (można to wyłączyć). Niebiesko-szare Tekst spacji Jasne @@ -243,7 +276,7 @@ Wersja Wybierz źródło wskazówek Dodaj natywną bibliotekę, aby umożliwić pisanie gestami - Więcej symboli + Dodatkowe symbole Próbuj wykrywać adresy URL i podobne jako pojedyncze słowo Wybierz język Tekst klawiszy @@ -345,4 +378,13 @@ %s (Uczeń) Przytrzymaj klawisz symboli, aby wyświetlić klawiaturę numeryczną Wytnij + Zawsze używaj środkowej sugestii + Po naciśnięciu spacji lub znaku interpunkcyjnego zostanie wpisana środkowa sugestia + Zamknij schowek + Wybierz klawisze paska narzędzi w schowku + %s (rozszerzony) + mansyjski (%s) + mansyjski + Pokaż więcej kolorów + To ustawienie wyświetla wszystkie kolory używane wewnętrznie. Lista kolorów może w każdej chwili ulec zmianie. Nie ma domyślnego koloru, a nazwy nie zostaną przetłumaczone. \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index decbb1913..040b0bc6b 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -102,7 +102,7 @@ Área de transferência Sugestões %sms - %smin. + %s min Sem limite Padrão do Sistema Sugerir nomes de contatos @@ -167,24 +167,23 @@ Digitação multilinguagem Carregar biblioteca de digitação por gesto Fornecer uma biblioteca nativa para ativar a digitação por gesto - Você precisará da biblioteca para \'%s\'. -\nBibliotecas incompatíveis podem causar um crash quando a digitação por gesto é usada. + Você precisará da biblioteca para \'%s\'. Bibliotecas incompatíveis podem causar um crash quando a digitação por gesto é usada. \n \nAviso: carregar código externo pode ser um risco à segurança. Use somente uma biblioteca de uma fonte que você confia. Arquivo de biblioteca desconhecido. Você tem certeza que você pegou ele de uma fonte confiável, e que é para \'%s\'? Carregar biblioteca - Mostrar variantes definidas nas línguas do teclado (padrão) + Mostrar variantes definidas nos idiomas do teclado (padrão) Adicionar variantes comuns Adicionar todas as variantes disponíveis Detecção de URL Tentar detectar URLs e similares como uma única palavra - Línguas & Layouts + Idiomas & Layouts Localizar linha de números Selecionar fonte de dicas Selecionar ordem do popup de teclas Linha de números - Língua - Língua (prioridade) + Idioma + Idioma (prioridade) Layout Símbolos Área de transferência @@ -197,12 +196,12 @@ Preferir números localizados em vez de latim Mostrar mais letras com diacríticos no popup Direção da barra de ferramentas variável - Distância de separação + Distância da separação Mudar ambos Adicionar palavras ao dicionário pessoal Mais autocorreção Automaticamente corrigir mesmo quando não explicitamente solicitado pelo campo de entrada - Fazer backup e restaurar + Backup e restauração Salvar ou carregar para/de um arquivo. Aviso: A restauração substituirá dados existentes Carregar arquivo Não foi possível ler o arquivo @@ -211,11 +210,11 @@ Definir imagem para o modo de dia ou noite? Dia Noite - Selecionar língua + Selecionar idioma Aprender de suas comunicações e dados digitados para melhorar sugestões Versão Sempre mostrar sugestões - Aviso: Desativar isso limpará dados aprendidos + Aviso: Desativar esta opção limpará dados aprendidos Ignorar a solicitação de outros apps de desativar as sugestões (pode causar problemas) Deseja realmente excluir o layout customizado %s? Definir imagem de fundo @@ -228,26 +227,25 @@ "Sem um dicionário, você só receberá sugestões de texto que você já escreveu. <br> \n Você pode baixar dicionários %1$s, ou verificar se um dicionário para \"%2$s\" pode ser baixado diretamente. %3$s." Alfabeto (Bépo) - Para qual língua o dicionário \"%1$s\" para %2$s deve ser adicionado? + Para qual idioma o dicionário \"%1$s\" para %2$s deve ser adicionado? Essa palavra já está presente no dicionário do usuário %s. Digite outra. Definido pelo usuário (noite) Texto da tecla Fundo da barra de espaço Mostrar funções que podem passar despercebido - Mudar língua + Mudar Idioma Selecionar teclas da barra de ferramentas - Totalmente para a esquerda - Totalmente para a direita + Inteiramente para a esquerda + Inteiramente para a direita Esquerda Direita Cima Baixo Mostrar dicas se um toque longo numa tecla ativa funcionalidade adicional - Escala do preenchimento inferior + Escala do preenchimento da parte inferior Kaitag Kaitag (%s) Acento - ► Toque longo no botão da área de transferência (o opcional, na faixa de sugestões) cola o conteúdo da área de transferência do sistema. <br> <br> ► Long-pressing keys in the suggestion strip toolbar pins them to the suggestion strip. <br> <br> ► Long-press the Comma-key to access Clipboard View, Emoji View, One-handed Mode, Settings, or Switch Language: <br> \t• Emoji View and Language Switch will disappear if you have the corresponding key enabled; <br> \t• For some layouts it\'s not the Comma-key, but the key at the same position (e.g. it\'s \'q\' for Dvorak layout). <br> <br> ► When incognito mode is enabled, no words will be learned, and no emojis will be added to recents. <br> <br> ► Press the Incognito icon to access the toolbar. <br> <br> ► Sliding key input: Swipe from shift to another key to type a single uppercase key: <br> \t• This also works for the \'?123\' key to type a single symbol from the symbols keyboard, and for related keys. <br> <br> ► Long-press a suggestion in the suggestion strip to show more suggestions, and a delete button to remove this suggestion. <br> <br> ► Swipe up from a suggestion to open more suggestions, and release on the suggestion to select it. <br> <br> ► Long-press an entry in the clipboard history to pin it (keep it in clipboard until you unpin). <br> <br> ► You can add dictionaries by opening them in a file explorer: <br> \t• This only works with <i>content-uris</i> and not with <i>file-uris</i>, meaning that it may not work with some file explorers. <br> <br> <i>Debug mode / debug APK</i> <br> <br> \t• Long-press a suggestion to show the source dictionary.<br> <br> \t• When using debug APK, you can find Debug Settings within the Advanced Preferences,though the usefulness is limited except for dumping dictionaries into the log. <br> <br> \t• In the event of an application crash, you will be prompted whether you want the crash logs when you open the Settings. <br> <br> \t• When using multilingual typing, space bar will show an confidence value used for determining the currently used language. <br> <br> \t• Suggestions will have some tiny numbers on top showing some internal score and source dictionary (can be disabled). ► For users doing manual backups with root access: Starting at Android 7, the shared preferences file is not in the default location, because the app is using %s. <br> This is necessary so the settings can be read before the device is unlocked, e.g. at boot. <br> The file is located in /data/user_de/0/package_id/shared_prefs/, though this may depend on the device and Android version. Gesto de deslizar horizontal na barra de espaço Mover cursor %s (Sebeolsik 390) @@ -255,8 +253,8 @@ Adicionar layout customizado Selecionar um arquivo num formato compatível. Infomações sobre os formatos estão disponíveis em %s. Copiar layout existente - Toque para editar o layout cru - Customizar símbolos e layouts de números + Toque para editar o layout bruto + Customizar layouts de símbolos ou números Layouts (exceto símbolos) expõem configurações internas que podem mudar ainda. Se isso acontecer, o layout customizado pode não mais funcionar corretamente. Símbolos Símbolos (Árabe) @@ -264,7 +262,7 @@ Telefone Símbolos de telefone Números - Numpad + Teclado númerico Numpad (paisagem) Dicionários Dicionário interno principal @@ -328,7 +326,7 @@ Salvar log Licença de código aberto Fechar - Toque na língua para abrir configurações + Toque no idioma para abrir configurações Aparência Descrição de funções escondidas armazenamento protegido pelo dispositivo @@ -346,4 +344,9 @@ Cortar %s (Estudante) Licença Pública Geral GNU v3.0 + Sempre usar a sugestão do meio + Quando pressionar espaço ou adicionar pontuação, a sugestão do meio será inserida + Fechar histórico da área de transferência + Selecionar teclas da barra de ferramentas de área de transferência + %s (Extendido) \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index d660f567f..d62c7ef9d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -94,7 +94,7 @@ Приложение %s уже включено в настройках «Язык и ввод». Перейдите к следующему шагу! Включить в настройках Переключитесь на %s - Выберите приложение \"%s\" как текущий способ ввода. + Выберите «%s» текущем методом ввода. Другой способ ввода Поздравляем, всё готово! Теперь вы можете использовать приложение %s для набора текста. @@ -160,7 +160,7 @@ Стиль темы Закруглённый Цвета темы - Цвета темы (Тёмная тема) + Цвета темы (тёмная тема) Внешний вид будет следовать настройкам системы Автоматически добавлять пробел после знаков препинания при вводе нового слова Если отключено, клавиша буфера обмена будет вставлять содержимое буфера обмена, если таковое имеется @@ -181,9 +181,9 @@ Серая Чёрная Пользовательская - Пользовательская (Тёмная тема) + Пользовательская (тёмная тема) Настройка цветов темы - Настройка цветов темы (Тёмная тема) + Настройка цветов темы (тёмная тема) Автоматический выбор цвета Нажмите для предпросмотра Выберите цвета для текста и фона diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index f1029bf18..5d7337758 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -3,10 +3,9 @@ Copyright (C) 2008 The Android Open Source Project modified SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only ---> - +--> "Потражи имена контаката" - "Контролор правописа користи уносе са листе контаката" + Контрола правописа користи уносе са листе контаката "Вибрирај на притисак тастера" "Звук на притисак тастера" "Искачући прозор приликом притиска тастера" @@ -18,12 +17,12 @@ "Омогући подељену тастатуру" "Пребаци на друге методе уноса" "Тастер за пребацивање језика" - "%s ms" + %s мс "Подразумевано" "Предложи имена контаката" "Користи имена из Контаката за предлоге и исправке" "Персонализовани предлози" - "Тачка и размак" + Дупли размак тачка "Двоструким додиром размака умеће се тачка праћена размаком" "Аутоматски унос великих слова" "Писање великог слова на почетку сваке реченице" @@ -120,9 +119,9 @@ " АБВГДЂЕЖЗИЈКЛЉМНЊОПРСТЋУФХЦЧЏШ" "Користи језике система" "Избор метода уноса" - ОпенБоард провера правописа - ОпенБоард подешавања провере правописа - ОпенБоард подешавања + ХелиБоард провера правописа + ХелиБоард подешавања провере правописа + ХелиБоард подешавања Унос Додатни кључеви Поузданост ауто-корекције @@ -147,7 +146,7 @@ Омогући историју међуспремника Време задржавања историје Висина тастатуре - %sмин. + %sмин Прикажи савете за дуги притисак Уклањање превлачењем Емоји кључ @@ -168,4 +167,14 @@ "Тражи" "Пауза" "Чекај" + Променити обоје + Додати речи у лични речник + Користити лични речник уређаја за чување научених речи + Увек приказати сугестије + Више ауткорекције + Аутокорекција чак и кад није експлицитно захтевана од поља за унос + Локализуј ред бројева + Додај уобичајене варијанте + Промена језика + Упозорење: онемогућавање овог подешавања ће обрисати научене податке \ No newline at end of file diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index d23243781..a72f9af19 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -15,8 +15,8 @@ "Avancerat" "Tema" "Aktivera delat tangentbord" - "Byt till annan inmatning" - "Knapp för att byta språk" + Byt till andra inmatningsmet. + Språkbytestangent %sms "Standardinställning" "Föreslå kontaktnamn" @@ -44,7 +44,7 @@ "Aktivera svepskrivning" "Skriv genom att dra från tecken till tecken utan att lyfta handen" "Visa spår efter rörelse" - "Visa ordförslag vid svepskrivning" + Dynamisk flytande förhandsvis. "Ordförslaget visas i rörelsen medan du skriver" "Frasrörelse" "Infoga blanksteg genom att dra fingret över blankstegstangenten" @@ -98,9 +98,9 @@ "Visa appikonen i startprogrammet" "Tilläggsordlistor" "Inställningar för ordlistor" - "En ordlista är tillgänglig" + Ordlista tillgänglig "Problem med att ansluta till ordlistetjänsten" - "Inga ordlistor finns" + Inga ordlistor tillgängliga "Informationen uppdaterades senast" "Huvudordlista" "Inställningar" @@ -121,7 +121,7 @@ Förslag Experimentella Diverse - %smin. + %s min Ingen gräns Långtryckning på mellanslagstangenten öppnar menyn för val av inmatningsmetod Ändra inmatningsmetod med mellanslagstangenten @@ -139,14 +139,14 @@ Inaktivera inlärningen av nya ord Inmatning Aktivera urklippshistorik - Urklippsknappen kommer att klistra in eventuellt urklippsinnehåll om denna är inaktiverad + Om inaktiverad, kommer urklippstangenten att klistra in eventuellt urklippsinnehåll Lagringstid för historik Backstegssvepning Gör en svepning från backstegstangenten för att markera och ta bort större mängder text på en gång Infoga automatiskt mellanslag efter skiljetecken när ett nytt ord skrivs Automatiskt mellanslag efter skiljetecken Fler tangenter - Tangentbordets höjdskala + Skala för tangentbordets höjd %s (akkhor) Alfabet (Colemak Mod-DH) Alfabet (Workman) @@ -196,4 +196,78 @@ Säkerhetskopiering och återställning Stäng Lägg till ett ord + Byt båda + Lägg till ord i personlig ordbok + Använd enhetens personliga ordbok för lagring av inlärda ord + Visa alltid förslag + Mer autokorrigering + Spara till eller ladda från fil. Varning: Återställning kommer att skriva över befintliga data + Byt språk + Ignorera andra appars begäran om att inaktivera förslag (kan orsaka problem) + Visa alla färger + Autokorrigera även när det inte uttryckligen begärs av inmatningsfältet + Varning: Inaktivering av denna inställning kommer att rensa inlärda data + Tryck på språket för att öppna inställningar + Blågrått + Brunt + Ljust + Dynamiska färger + Mörkt + Mörkare + Svart + Choklad + Molnigt + Skog + Indigo + Stil + Avrundad + Färger + Färger (natt) + Rosa + Hav + Sand + Violett + Användardefinierat + Smala tangentmellanrum + Skala för avstånd till nederkant + Välj färger på text och bakgrunder + Delningsavstånd + Visa endast huvudfärger + Färga navigeringsfält + Anpassa färger + Anpassa färger (natt) + Intern huvudordlista + Välj ordning på popup-tangenter + Välj tipskälla + Natt + Lägg till ordlista från fil + Välj en ordlista att lägga till. Ordlistor i .dict-format kan laddas ner %s. + Välj språk + här + Dag + Flerspråkigt skrivande + Tangenttipstext + Ordlistor + Vänster + Höger + Språk (prioriterat) + Sifferrad + Symboler + Layout + Språk + Välj verktygsfältstangenter + Stäng urklippshistorik + Välj urklippsverktygsfältstangenter + Visa tips om långtryck på en tangent aktiverar ytterligare funktionalitet + Visa funktionalitetstips + Urklipp + Rensa urklipp + Röstinmatning + Markera ord + Klipp ut + Enhandsläge + Upp + Ner + Längst åt vänster + Längst åt höger \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 17d465896..1f991631f 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -3,8 +3,7 @@ Copyright (C) 2008 The Android Open Source Project modified SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only ---> - +--> "Kişi adlarını denetle" "Yazım denetleyici, kişi listenizdeki girişleri kullanır" "Tuşa basıldığında titret" @@ -168,4 +167,20 @@ "Arama" "Dur" "Bekle" + Uyarı: Bu ayarı devre dışı bırakmak öğrenilmiş verileri temizler + Kişisel sözlüğe kelime ekleyin + Her zaman önerileri göster + İkisini de değiştir + Yedekleme + Yedekle ve geri yükle + Yedekleme hatası: %s + Yedeklemeyi yüklerken hata: %s + Dili Değiştir + Geri yükle + Çok dilli yazmak + Kitaplığı yükle + Kitaplığı sil + Diller ve Düzenler + Daha fazla otomatik düzenleme + Öğrenilmiş kelimeleri depolamak için cihaz kişisel sözlüğünü kullan \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index df5e9139b..1d2ab0f8b 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -90,7 +90,7 @@ Застосунок %s уже увімкнено в налаштуваннях «Мова та введення». Переходьте до наступного кроку! Увімкнути в налаштуваннях Перемкнути на %s - Далі виберіть \"%s\" як ваш активний спосіб введення тексту. + Виберіть «%s» поточним методом введення. Змінити метод введення Вітаємо, налаштування завершено! Тепер ви можете писати в усіх улюблених застосунках за допомогою %s. @@ -124,7 +124,7 @@ Стиль теми Закруглений Кольори теми - Кольори теми (Темна тема) + Кольори теми (темна тема) Використати тему системи Введення %s (Аккгор) @@ -176,7 +176,7 @@ Вимкнути вивчення нових слів Показувати підказки довгим натисканням Показувати підказки клавіш - Змінювати спосіб введення клавішею пробіл + Змінювати метод введення клавішею пробіл Довге натискання клавіші пробіл виводить меню вибору способу введення Алфавіт (Workman) Перевірка правопису HeliBoard diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68222c3f2..dc5e9b79f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -753,30 +753,40 @@ New dictionary: device protected storage - - ► Long-pressing the Clipboard Key (the optional one in the suggestion strip) pastes system clipboard contents. <br> <br> - ► Long-pressing keys in the suggestion strip toolbar pins them to the suggestion strip. <br> <br> - ► Long-press the Comma-key to access Clipboard View, Emoji View, One-handed Mode, Settings, or Switch Language: <br> - \t• Emoji View and Language Switch will disappear if you have the corresponding key enabled; <br> - \t• For some layouts it\'s not the Comma-key, but the key at the same position (e.g. it\'s \'q\' for Dvorak layout). <br> <br> - ► When incognito mode is enabled, no words will be learned, and no emojis will be added to recents. <br> <br> - ► Press the Incognito icon to access the toolbar. <br> <br> - ► Sliding key input: Swipe from shift to another key to type a single uppercase key: <br> - \t• This also works for the \'?123\' key to type a single symbol from the symbols keyboard, and for related keys. <br> <br> - ► Long-press a suggestion in the suggestion strip to show more suggestions, and a delete button to remove this suggestion. <br> <br> - ► Swipe up from a suggestion to open more suggestions, and release on the suggestion to select it. <br> <br> - ► Long-press an entry in the clipboard history to pin it (keep it in clipboard until you unpin). <br> <br> - ► You can add dictionaries by opening them in a file explorer: <br> - \t• This only works with <i>content-uris</i> and not with <i>file-uris</i>, meaning that it may not work with some file explorers. <br> <br> - <i>Debug mode / debug APK</i> <br> <br> - \t• Long-press a suggestion to show the source dictionary.<br> <br> - \t• When using debug APK, you can find Debug Settings within the Advanced Preferences,though the usefulness is limited except for dumping dictionaries into the log. <br> <br> - \t• In the event of an application crash, you will be prompted whether you want the crash logs when you open the Settings. <br> <br> - \t• When using multilingual typing, space bar will show an confidence value used for determining the currently used language. <br> <br> - \t• Suggestions will have some tiny numbers on top showing some internal score and source dictionary (can be disabled). <br> <br> - ► For users doing manual backups with root access: Starting at Android 7, the shared preferences file is not in the default location, because the app is using %s. <br> - This is necessary so the settings can be read before the device is unlocked, e.g. at boot. <br> - The file is located in /data/user_de/0/package_id/shared_prefs/, though this may depend on the device and Android version. + ► Long-pressing pinned toolbar keys results in additional functionality: <br> +\n\t• clipboard &#65515; paste <br> +\n\t• move left/right &#65515; move full left/right <br> +\n\t• move up/down &#65515; page up/down <br> +\n\t• copy &#65515; copy all <br> +\n\t• select word &#65515; select all <br> +\n\t• undo &#8596; redo <br> <br> +\n► Long-pressing keys in the suggestion strip toolbar pins them to the suggestion strip. <br> <br> +\n► Long-press the Comma-key to access Clipboard View, Emoji View, One-handed Mode, Settings, or Switch Language: <br> +\n\t• Emoji View and Language Switch will disappear if you have the corresponding key enabled; <br> +\n\t• For some layouts it\\\'s not the Comma-key, but the key at the same position (e.g. it\\\'s \\\'q\\\' for Dvorak layout). <br> <br> +\n► When incognito mode is enabled, no words will be learned, and no emojis will be added to recents. <br> <br> +\n► Press the Incognito icon to access the toolbar. <br> <br> +\n► Sliding key input: Swipe from shift to another key to type a single uppercase key: <br> +\n\t• This also works for the \\\'?123\\\' key to type a single symbol from the symbols keyboard, and for related keys. <br> <br> +\n► Hold shift or symbol key, press one or more keys, and then release shift or symbol key to return to the previous keyboard. <br> <br> +\n► Long-press a suggestion in the suggestion strip to show more suggestions, and a delete button to remove this suggestion. <br> <br> +\n► Swipe up from a suggestion to open more suggestions, and release on the suggestion to select it. <br> <br> +\n► Long-press an entry in the clipboard history to pin it (keep it in clipboard until you unpin). <br> <br> +\n► Swipe left in clipboard view to remove an entry (except when it\\\'s pinned) <br> <br> +\n► Select text and press shift to switch between uppercase, lowercase and capitalize words. <br> <br> +\n► You can add dictionaries by opening them in a file explorer: <br> +\n\t• This only works with <i>content-uris</i> and not with <i>file-uris</i>, meaning that it may not work with some file explorers. <br> <br> +\n► For users doing manual backups with root access: <br> +\n\t• Starting at Android 7, the shared preferences file is not in the default location, because the app is using %s. This is necessary so the settings can be read before the device is unlocked, e.g. at boot; <br> +\n\t• The file is located in /data/user_de/0/package_id/shared_prefs/ though this may depend on the device and Android version. <br> <br> +\n<i><b>Debug mode / debug APK</b></i> <br> <br> +\n► Long-press a suggestion to show the source dictionary. <br> <br> +\n► When using debug APK, you can find Debug Settings within the Advanced Preferences,though the usefulness is limited except for dumping dictionaries into the log. <br> +\n\t• For a release APK, you need to tap the version in <i>About</i> several times, then you can find debug settings in <i>Advanced Preferences</i>. <br> +\n\t• When enabling <i>Show suggestion infos</i>, suggestions will have some tiny numbers on top showing some internal score and source dictionary. <br> <br> +\n► In the event of an application crash, you will be prompted whether you want the crash logs when you open the Settings. <br> <br> +\n► When using multilingual typing, space bar will show an confidence value used for determining the currently used language. <br> <br> +\n► Suggestions will have some tiny numbers on top showing some internal score and source dictionary (can be disabled). Learn from your communications and typed data to improve suggestions Go diff --git a/fastlane/metadata/android/ar/changelogs/1001.txt b/fastlane/metadata/android/ar/changelogs/1001.txt new file mode 100644 index 000000000..1df442381 --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/1001.txt @@ -0,0 +1,20 @@ +* أيقونة جديدة من @FabianOvrWrt مع مساهمات من @the-eclectic-dyslexic (#517, #592) +* لوحة تتبع وشريط المسافة قابلة للتخصيص بشكل أكبر بواسطة @arcarum (#486) +* إضافة % لتغيير تخطيط الرموز (#568، #428) +* تحسين السلوك عند ضبط مفتاح تبديل اللغة على تبديل اللغة ولوحة المفاتيح +* إظهار الروابط إلى القواميس الموجودة عند إضافة قاموس +* إضافة تصميم Kaitag بواسطة @alkaitagi (#519) +* إضافة تصميم Probhat بواسطة @fahimscirex (#489) +* عكس ترتيب شريط الأدوات اختياريًا للغات RTL بواسطةcodokie (#557، #574) +* السماح بتخصيص تخطيطات خاصة (اللوحة الرقمية، الهاتف، ...) + * لا يزال تجريبيًا، حيث قد تتغير التخطيطات الأساسية +* تم تحديث Spellchecker.xml ليشمل اللغات التي تتوفر فيها القواميس، ولكن لم يتم تضمينها في التطبيق +* تحديث الترجمات (شكرًا لجميع المترجمين!) +* ترقية ndk بواسطةSyphyr (#560) +* ترقية رمز الملء التلقائي المضمّن بواسطة @arcarum (#595) +* إصلاح مشكلة مربع حوار مفتاح شريط الأدوات (#505) +*إصلاح مشكلة التخطيط التركي (#508) +* إصلاح حالات التبديل الخاطئة عند تدوير شاشة تخصيص الألوان (#563) +* إصلاح مشكلة عدم تحميل الرموز التعبيرية الحديثة (#527) +*إصلاح مشكلة عدم ظهور الأرقام في بعض الحقول (#585) +* بعض الإصلاحات الطفيفة diff --git a/fastlane/metadata/android/ar/changelogs/1003.txt b/fastlane/metadata/android/ar/changelogs/1003.txt new file mode 100644 index 000000000..003dbf1db --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/1003.txt @@ -0,0 +1,7 @@ +* تغيير أيقونات التصحيح التلقائي وتحديد جميع مفاتيح شريط الأدوات بواسطة @codokie (#524, #651) +* إضافة تخطيط Chuvash بواسطة @tenextractor (#677) +* إضافة مفتاح شريط الأدوات بواسطة @codokie (#678) +* تحديث تخطيط Probhat بواسطة @fahimscirex (#628) +* إظهار أيقونات شريط الأدوات في مربع حوار مفتاح شريط الأدوات +* إضافة زر إغلاق في سجل الحافظة بواسطة @codokie (#403, #649) +* إضافة التصميم الروسي (الطالب) بواسطة @Zolax9 (#640) diff --git a/fastlane/metadata/android/ar/changelogs/1004.txt b/fastlane/metadata/android/ar/changelogs/1004.txt new file mode 100644 index 000000000..1f966f0e5 --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/1004.txt @@ -0,0 +1,7 @@ +- تحديث تخطيط السيريلية الصربية بواسطة @markokocic (#704, #705) +- تحديث تخطيط إستونيا بواسطة @tenextractor (#693) +- إصلاح الإدخالات المكررة في سجل الحافظة بواسطة @codokie (#616, #680) +- قم فقط بإضافة الإدخالات النصية إلى سجل الحافظة بواسطة @codokie (#711) +- صور أفضل في البيانات الوصفية بواسطة @RHJihan (#713) +- ضبط لون الأيقونات بشكل صحيح في مربع حوار تحديد شريط الأدوات بواسطة @codokie (#715, #716) +- إصلاحات أخرى (#684، #723 والمزيد) diff --git a/fastlane/metadata/android/ar/full_description.txt b/fastlane/metadata/android/ar/full_description.txt new file mode 100644 index 000000000..3bf6a3aa4 --- /dev/null +++ b/fastlane/metadata/android/ar/full_description.txt @@ -0,0 +1,29 @@ +HeliBoard عبارة عن لوحة مفاتيح مفتوحة المصدر تهتم بالخصوصية، وتعتمد على AOSP / OpenBoard. +لا يستخدم إذن الإنترنت، وبالتالي فهو غير متصل بالإنترنت بنسبة 100٪. + +المميزات: +

      +
    • أضف قواميس للاقتراحات والتدقيق الإملائي
    • +
        +
      • قم ببناء قاموس خاص بك، أو احصل عليه هنا، أو في القسم التجريبي (قد تختلف الجودة)
      • +
      • يمكن استخدام قواميس إضافية للرموز التعبيرية أو الرموز العلمية لتقديم اقتراحات (على غرار "البحث عن الرموز التعبيرية")
      • +
      • لاحظ أنه بالنسبة للتخطيطات الكورية، تعمل الاقتراحات فقط باستخدام هذا القاموس، والأدوات الموجودة في مستودع القاموس غير قادرين على إنشاء قواميس صالحة للعمل
      • +
      +
    • تخصيص سمات لوحة المفاتيح (النمط والألوان وصورة الخلفية)
    • +
        +
      • يمكنه متابعة إعداد النهار/الليل للنظام على نظام التشغيل Android 10+ (وفي بعض إصدارات Android 9)
      • +
      • يمكنه متابعة الألوان الديناميكية لنظام التشغيل Android 12+
      • +
      +
    • تخصيص لوحة المفاتيح تخطيطات (متوفرة فقط عند تعطيل استخدام لغات النظام)
    • +
    • الكتابة بلغات متعددة
    • +
    • الكتابة بالتمرير (فقط مع مكتبة المصادر المغلقة ☹️)
    • +
        +
      • المكتبة غير مضمنة في التطبيق، حيث لا تتوفر مكتبة متوافقة مفتوحة المصدر
      • +
      • يمكن استخراجه من حزم GApps ("swypelibs")، أو تنزيله هنا
      • +
      +
    • سجل الحافظة
    • +
    • وضع اليد الواحدة
    • +
    • تقسيم لوحة المفاتيح (متاح فقط إذا كانت الشاشة كبيرة بدرجة كافية)
    • +
    • لوحة الأرقام
    • +
    • النسخ الاحتياطي واستعادة بيانات الكلمات / السجل التي تعلمتها
    • +
    diff --git a/fastlane/metadata/android/ar/short_description.txt b/fastlane/metadata/android/ar/short_description.txt new file mode 100644 index 000000000..1a70a647d --- /dev/null +++ b/fastlane/metadata/android/ar/short_description.txt @@ -0,0 +1 @@ +لوحة مفاتيح مفتوحة المصدر قابلة للتخصيص diff --git a/fastlane/metadata/android/ar/title.txt b/fastlane/metadata/android/ar/title.txt new file mode 100644 index 000000000..e9841ace0 --- /dev/null +++ b/fastlane/metadata/android/ar/title.txt @@ -0,0 +1 @@ +HeliBoard diff --git a/fastlane/metadata/android/cs-CZ/changelogs/1001.txt b/fastlane/metadata/android/cs-CZ/changelogs/1001.txt new file mode 100644 index 000000000..77ee5b7e7 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/1001.txt @@ -0,0 +1,20 @@ +* nová ikona od @FabianOvrWrt s přispěním @the-eclectic-dyslexic (#517, #592) +* více přizpůsobitelný trackpad s mezerníkem a přepínačem jazyků od @arcarum (#486) +* přidání % do rozložení symbolů pro posun (#568, #428) +* zlepšení chování, když je klávesa přepínače jazyka nastavena na přepínání jazyka i klávesnice +* při přidávání slovníku zobrazit odkazy na existující slovníky +* přidat rozložení Kaitag od @alkaitagi (#519) +* přidat rozložení Probhat od @fahimscirex (#489) +* volitelné obrácení pořadí panelu nástrojů pro jazyky RTL od @codokie (#557, #574) +* umožnit přizpůsobení speciálních rozvržení (numpad, telefon, ...) +* stále experimentální, protože základní rozvržení se mohou změnit +* aktualizován spellchecker.xml tak, aby zahrnoval lokality, kde jsou k dispozici slovníky, ale nejsou součástí aplikace +* aktualizace překladů (děkujeme všem překladatelům!) +* aktualizace ndk podle @Syphyr (#560) +* aktualizace kódu automatického doplňování inline od @arcarum (#595) +* oprava problému s dialogovým oknem s klíčem na panelu nástrojů (#505) +* oprava problému s tureckým rozložením (#508) +* oprava špatných stavů přepínačů při otáčení na obrazovce přizpůsobení barev (#563) +* oprava problému s nenačítáním posledních emotikonů (#527) +* oprava problému s nezobrazováním čísel v některých polích (#585) +* některé drobné opravy diff --git a/fastlane/metadata/android/cs-CZ/changelogs/1003.txt b/fastlane/metadata/android/cs-CZ/changelogs/1003.txt new file mode 100644 index 000000000..29569aee4 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/1003.txt @@ -0,0 +1,9 @@ +* změna ikon pro automatickou opravu a výběr všech kláves na panelu nástrojů od @codokie (#524, #651) +* přidání čuvašského rozložení od @tenextractor (#677) +* přidání klávesy pro řezání na panelu nástrojů od @codokie (#678) +* aktualizovat rozložení Probhat od @fahimscirex (#628) +* zobrazit ikony panelu nástrojů v dialogovém okně klíče panelu nástrojů +* přidat tlačítko zavřít v historii schránky od @codokie (#403, #649) +* přidat ruské (studentské) rozložení od @Zolax9 (#640) +* volitelný numerický blok při dlouhém stisku klávesy se symboly (#588) +* drobné opravy a vylepšení, včetně #632, #637, #638 od @RHJihan diff --git a/fastlane/metadata/android/cs-CZ/changelogs/1004.txt b/fastlane/metadata/android/cs-CZ/changelogs/1004.txt new file mode 100644 index 000000000..caa2ae214 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/1004.txt @@ -0,0 +1,7 @@ +- aktualizace rozložení srbské cyrilice od @markokocic (#704, #705) +- aktualizace rozvržení Estonsko od @tenextractor (#693) +- oprava duplicitních záznamů v historii schránky od @codokie (#616, #680) +- do historie schránky přidávat pouze textové záznamy od @codokie (#711) +- lepší obrázky v metadatech od @RHJihan (#713) +- správně nastavit barvu ikon v dialogovém okně výběru panelu nástrojů od @codokie (#715, #716) +- další opravy (#684, #723 a další) diff --git a/fastlane/metadata/android/cs-CZ/short_description.txt b/fastlane/metadata/android/cs-CZ/short_description.txt new file mode 100644 index 000000000..e6545014f --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/short_description.txt @@ -0,0 +1 @@ +Přizpůsobitelná open-source klávesnice diff --git a/fastlane/metadata/android/cs-CZ/title.txt b/fastlane/metadata/android/cs-CZ/title.txt new file mode 100644 index 000000000..e9841ace0 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/title.txt @@ -0,0 +1 @@ +HeliBoard diff --git a/fastlane/metadata/android/de-DE/changelogs/1001.txt b/fastlane/metadata/android/de-DE/changelogs/1001.txt new file mode 100644 index 000000000..55f3fd812 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/1001.txt @@ -0,0 +1,20 @@ +* neues Icon von @FabianOvrWrt mit Beiträgen von @the-eclectic-dyslexic (#517, #592) +* anpassbareres Leertaste-Trackpad und Sprachschalter von @arcarum (#486) +* % zum Layout der Schaltsymbole hinzufügen (#568, #428) +* Verhalten verbessern, wenn die Sprachumschalttaste so eingestellt ist, dass sowohl Sprache als auch Tastatur umgeschaltet werden +* Beim Hinzufügen eines Wörterbuchs Links zu vorhandenen Wörterbüchern anzeigen +* Kaitag-Layout von @alkaitagi hinzufügen (#519) +* Probhat-Layout von @fahimscirex hinzufügen (#489) +* optional umgekehrte Symbolleistenreihenfolge für RTL-Sprachen von @codokie (#557, #574) +* ermöglicht das Anpassen spezieller Layouts (Nummernblock, Telefon, ...) + * Noch experimentell, da sich die Grundlayouts ändern können +* Rechtschreibprüfung.xml aktualisiert, um Gebietsschemata einzuschließen, in denen Wörterbücher verfügbar, aber nicht in der App enthalten sind +* Übersetzungen aktualisieren (Danke an alle Übersetzer!) +* ndk aktualisieren von @Syphyr (#560) +* Inline-Autofill-Code von @arcarum aktualisieren (#595) +* Problem mit dem Tastendialog in der Symbolleiste behoben (#505) +* Problem mit türkischem Layout behoben (#508) +* Falsche Schalterzustände beim Drehen im Bildschirm „Farben anpassen“ behoben (#563) +* Problem behoben, bei dem aktuelle Emojis nicht geladen wurden (#527) +* Problem behoben, bei dem Zahlen in bestimmten Feldern nicht angezeigt wurden (#585) +* einige kleinere Korrekturen diff --git a/fastlane/metadata/android/de-DE/changelogs/1003.txt b/fastlane/metadata/android/de-DE/changelogs/1003.txt new file mode 100644 index 000000000..f3ae9ec43 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/1003.txt @@ -0,0 +1,9 @@ +* Symbole für die Autokorrektur ändern und alle Symbolleistentasten auswählen von @codokie (#524, #651) +* Tschuwaschisches Layout von @tenextractor hinzufügen (#677) +* Ausschneiden-Symbolleistentaste von @codokie hinzufügen (#678) +* Probhat-Layout von @fahimscirex aktualisieren (#628) +* Symbolleistensymbole im Tastendialog der Symbolleiste anzeigen +* Schaltfläche „Schließen“ im Verlauf der Zwischenablage hinzufügen von @codokie (#403, #649) +* Russisches (Studenten-)Layout von @Zolax9 hinzufügen (#640) +* Nummernblock auf Symboltaste durch langes Drücken optional machen (#588) +* kleinere Korrekturen und Verbesserungen, einschließlich #632, #637, #638 von @RHJihan diff --git a/fastlane/metadata/android/de-DE/changelogs/1004.txt b/fastlane/metadata/android/de-DE/changelogs/1004.txt new file mode 100644 index 000000000..bcae5aa26 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/1004.txt @@ -0,0 +1,7 @@ +- Aktualisierung des serbisch-kyrillischen Layouts von @markokocic (#704, #705) +- Estland-Layout von @tenextractor aktualisieren (#693) +- Doppelte Einträge im Zwischenablageverlauf von @codokie behoben (#616, #680) +- Fügen Sie nur Texteinträge zum Zwischenablageverlauf von @codokie hinzu (#711) +- bessere Bilder in Metadaten von @RHJihan (#713) +- Farbe der Symbole im Auswahldialog der Symbolleiste richtig eingestellt von @codokie (#715, #716) +- andere Korrekturen (#684, #723 und mehr) diff --git a/fastlane/metadata/android/de-DE/full_description.txt b/fastlane/metadata/android/de-DE/full_description.txt new file mode 100644 index 000000000..de046a148 --- /dev/null +++ b/fastlane/metadata/android/de-DE/full_description.txt @@ -0,0 +1,29 @@ +HeliBoard ist eine datenschutzbewusste quelloffene Tastatur, die auf AOSP / OpenBoard basiert. +Verwendet keine Internetberechtigung und ist daher zu 100 % offline. + +Features: +
      +
    • Wörterbücher für Vorschläge und Rechtschreibprüfung hinzufügen
    • +
        +
      • Erstelle deine eigene oder hole sie sich hier oder im experimenteller Abschnitt (Qualität kann variieren)
      • +
      • Zusätzliche Wörterbücher für Emojis oder wissenschaftliche Symbole können zur Bereitstellung von Vorschlägen genutzt werden (ähnlich wie bei der „Emoji-Suche“)
      • +
      • Beachten Sie, dass Vorschläge für koreanische Layouts nur mit diesem Wörterbuch, den Tools im Wörterbuch-Repository, funktionieren können keine funktionierenden Wörterbücher erstellen
      • +
      +
    • Tastaturdesigns anpassen (Stil, Farben und Hintergrundbild)
    • +
        +
      • kann die Tag-/Nachteinstellung des Systems auf Android 10+ (und auf einigen Versionen von Android 9) verfolgen
      • +
      • kann dynamischen Farben für Android 12+ folgen
      • +
      +
    • Tastatur-Layouts anpassen (nur verfügbar, wenn Systemsprachen verwenden)
    • +
    • Mehrsprachiges Tippen
    • +
    • Glide Typing (nur mit quellgeschlossener Bibliothek ☹️)
    • +
        +
      • Bibliothek nicht in der App enthalten, da keine kompatible quelloffene Bibliothek verfügbar ist
      • +
      • kann aus GApps-Paketen („swypelibs“) extrahiert oder heruntergeladen werden hier
      • +
      +
    • Zwischenablageverlauf
    • +
    • Einhandmodus
    • +
    • Geteilte Tastatur (nur verfügbar, wenn der Bildschirm groß genug ist)
    • +
    • Ziffernblock
    • +
    • Sichere gelernte Wort-/Verlaufsdaten und stelle sie wieder her
    • +
    diff --git a/fastlane/metadata/android/de-DE/short_description.txt b/fastlane/metadata/android/de-DE/short_description.txt new file mode 100644 index 000000000..b64342590 --- /dev/null +++ b/fastlane/metadata/android/de-DE/short_description.txt @@ -0,0 +1 @@ +Anpassbare quelloffene Tastatur diff --git a/fastlane/metadata/android/de-DE/title.txt b/fastlane/metadata/android/de-DE/title.txt new file mode 100644 index 000000000..e9841ace0 --- /dev/null +++ b/fastlane/metadata/android/de-DE/title.txt @@ -0,0 +1 @@ +HeliBoard diff --git a/fastlane/metadata/android/en-US/changelogs/2000.txt b/fastlane/metadata/android/en-US/changelogs/2000.txt new file mode 100644 index 000000000..bd1ce4e3b --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/2000.txt @@ -0,0 +1,10 @@ +* add basic support for modifier keys +* add long press functions to more toolbar keys +* and more clipboard history toolbar keys +* make clipboard history toolbar customizable +* allow customizing all colors +* add setting to always show word to be enterd as middle suggestion +* add caps lock indicator +* add Piedmontese, Eastern Mari, Mansi, extended layouts for Kannada and Hungarian +* fix cut off text in key preview popup on some devices +* further fixes and improvements, see release notes diff --git a/fastlane/metadata/android/fr-FR/full_description.txt b/fastlane/metadata/android/fr-FR/full_description.txt new file mode 100644 index 000000000..c1cac934b --- /dev/null +++ b/fastlane/metadata/android/fr-FR/full_description.txt @@ -0,0 +1,29 @@ +HeliBoard est un clavier open-source respectueux de la vie privée, basé sur AOSP / OpenBoard. +N'utilise pas l'autorisation internet et est donc 100% hors ligne. + +Caractéristiques : +
      +
    • Ajouter des dictionnaires pour les suggestions et la vérification de l'orthographe
    • +
        +
      • construisez les vôtres ou obtenez-les ici, ou dans la section expérimental (la qualité peut varier)
      • +
      • des dictionnaires supplémentaires pour les emojis ou les symboles scientifiques peuvent être utilisés pour fournir des suggestions (similaires à la "recherche d'emojis")
      • +
      • Notez que pour les mises en page coréennes, les suggestions ne fonctionnent qu'en utilisant ce dictionnaire ; les outils du dépôt des dictionnaires ne sont pas en mesure de créer des dictionnaires fonctionnels
      • +
      +
    • Personnalisation des thèmes du clavier (style, couleurs et image de fond)
    • +
        +
      • peut utiliser les paramètres jour/nuit du système sur Android 10+ (et sur certaines versions d'Android 9)
      • +
      • peut utiliser les couleurs dynamiques pour Android 12+
      • +
      +
    • Personnaliser les dispositions du clavier (disponible uniquement lorsque l'option "utiliser les langues du système" est désactivée)
    • +
    • Saisie multilingue
    • +
    • Saisie par glissement (uniquement avec une bibliothèque à code source privé ☹️)
    • +
        +
      • bibliothèque non incluse dans l'application car il n'y a pas de bibliothèque open source compatible disponible
      • +
      • peut être extraite des paquets GApps ("swypelibs"), ou téléchargée ici
      • +
      +
    • Historique du presse-papiers
    • +
    • Mode à une main
    • +
    • Clavier fractionné (disponible uniquement si l'écran est suffisamment grand)
    • +
    • Pavé numérique
    • +
    • Sauvegarde et restauration des mots appris et des historiques
    • +
    diff --git a/fastlane/metadata/android/fr-FR/short_description.txt b/fastlane/metadata/android/fr-FR/short_description.txt new file mode 100644 index 000000000..995627e89 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/short_description.txt @@ -0,0 +1 @@ +Clavier open-source personnalisable diff --git a/fastlane/metadata/android/fr-FR/title.txt b/fastlane/metadata/android/fr-FR/title.txt new file mode 100644 index 000000000..e9841ace0 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/title.txt @@ -0,0 +1 @@ +HeliBoard diff --git a/fastlane/metadata/android/gl-ES/changelogs/1001.txt b/fastlane/metadata/android/gl-ES/changelogs/1001.txt new file mode 100644 index 000000000..5b0bbb450 --- /dev/null +++ b/fastlane/metadata/android/gl-ES/changelogs/1001.txt @@ -0,0 +1,20 @@ +* nova icona por @FabianOvrWrt coa colaboración de @the-eclectic-dyslexic (#517, #592) +* barra de espazo de cambio de idioma máis personalizables, por @arcarum (#486) +* engadir % á disposición alternativa de símbolos (#568, #428) +* mellora do comportamento cando a tecla de cambio de idioma se configura para cambiar de teclado e de idioma +* mostra ligazón aos dicionarios existentes ao engadir un dicionario +* disposición Kaitag por @alkaitagi (#519) +* disposición Probhat por @fahimscirex (#489) +* darlle a volta á barra de ferramentas para idiomas RTL por @codokie (#557, #574) +* permitir personalizar disposicións especiais (telcado numérico, teléfono, ...) + * aínda experimental, polo que podería cambiar a disposición base +* actualizado spellchecker.xml para incluír os idiomas cando están dispoñibles os dicionarios, pero non incluídos na app +* traducións actualizadas (grazas ás tradutoras!) +* actualización de ndk por @Syphyr (#560) +* actualización do código do autocompletado por @arcarum (#595) +* arranxo da incidencia co diálogo da tecla da barra de ferramentas (#505) +* arranco da incidencia coa disposición do teclado turco (#508) +* arranxo dos estados non válidos ao rotar o dispositivo na pantalla de cores personalizadas (#563) +* arranxo da incidencia dos emojis recentes que non se cargaban (#527) +* arranxo da incidencia cos número que non aparecían en determinados campos (#585) +* outros arranxos menores diff --git a/fastlane/metadata/android/gl-ES/changelogs/1003.txt b/fastlane/metadata/android/gl-ES/changelogs/1003.txt new file mode 100644 index 000000000..07712a989 --- /dev/null +++ b/fastlane/metadata/android/gl-ES/changelogs/1003.txt @@ -0,0 +1,9 @@ +* cambia as iconas para a corrección automática e selecciona todas as teclas da barra de ferramentas por @codokie (#524, #651) +* engadir a disposición Chuvash por @tenextractor (#677) +* engadir a tecla de Cortar á barra por @codokie (#678) +* actualizar a disposición Probhat por @fahimscirex (#628) +* mostrar as iconas da barra de ferramentas no diálogo de teclas da barra de ferramentas +* engadir o botón de pechar no historial do portapapeis por @codokie (#403, #649) +* engadir o disposición rusa (Studet) por @Zolax9 (#640) +* facer que o teclado numérico sexa optativo coa pulsación longa da tecla de símbolos (#588) +* arranxos e melloras menores, incluíndo #632, #637, #638 por @RHJihan diff --git a/fastlane/metadata/android/gl-ES/changelogs/1004.txt b/fastlane/metadata/android/gl-ES/changelogs/1004.txt new file mode 100644 index 000000000..a230205d7 --- /dev/null +++ b/fastlane/metadata/android/gl-ES/changelogs/1004.txt @@ -0,0 +1,7 @@ +- actualizar a disposición Serbio Cirílico por @markokocic (#704, #705) +- actualizar a disposición do Estonio por @tenextractor (#693) +- corrixir as entradas duplicadas no historial do portapapeis por @codokie (#616, #680) +- só engade entradas de texto ao historial do portapapeis por @codokie (#711) +- mellores imaxes nos metadatos por @RHJihan (#713) +- pór a cor correcta das iconas no diálogo de selección da barra de ferramentas por @codokie (#715, #716) +- outros arranxos (#684, #723 e outras) diff --git a/fastlane/metadata/android/gl-ES/full_description.txt b/fastlane/metadata/android/gl-ES/full_description.txt new file mode 100644 index 000000000..83ba89982 --- /dev/null +++ b/fastlane/metadata/android/gl-ES/full_description.txt @@ -0,0 +1,29 @@ +HeliBoard é un teclado de código aberto para coidar a túa privacidade, baseado en AOSP / OpenBoard. +Non precisa permiso de acceso a internet, xa que é 100% sen conexión á rede. + +Características: +
      +
    • Engade dicionarios para suxestións e ortografía.
    • +
        +
      • fai o teu propio, ou descárgao aquí, ou na sección experimental (calidade variable)
      • +
      • pódense usar dicionarios adicionais para emojis ou símbolos científicos para obter suxestións (semellante a "buscar emoji")
      • +
      • ten en conta que para suxestións da disposición Coreana, éstas so funcionan se usas este dicionario, as ferramentas no repositorio de dicionarios non son para crear dicionarios funcionais
      • +
      +
    • Personaliza a aparencia do teclado (estilo, cores e imaxe de fondo)
    • +
        +
      • pode seguir o axuste do sistema día/noite en Android 10+ (e algunhas versións de Android 9)
      • +
      • pode seguir as cores dinámicas de Android 12+
      • +
      +
    • Personaliza a disposición teclado (só dispoñible ao desactivar Usar Idiomas do Sistema)
    • +
    • Escritura con varios idiomas
    • +
    • Escribe desprazándote (só cunha biblioteca de código privativo ☹️)
    • +
        +
      • a biblioteca non está incluída na app ao non haber unha biblioteca de código aberto compatible
      • +
      • pode extraerse dos paquetes GApps ("swypelibs"), ou descargala aquí
      • +
      +
    • Historial do portapapeis
    • +
    • Modo Unha sóa man
    • +
    • Teclado separado (só se a pantalla é grande abondo)
    • +
    • Teclado numérico
    • +
    • Copia e restablecemento das palabras aprendidas e historial
    • +
    diff --git a/fastlane/metadata/android/pl-PL/changelogs/1001.txt b/fastlane/metadata/android/pl-PL/changelogs/1001.txt new file mode 100644 index 000000000..2a95ff21e --- /dev/null +++ b/fastlane/metadata/android/pl-PL/changelogs/1001.txt @@ -0,0 +1,20 @@ +* nowa ikona autorstwa @FabianOvrWrt we współpracy z @the-eclectic-dyslexic (#517, #592) +* bardziej konfigurowalne przesuwanie po spacji i zmiana języka dzięki @arcarum (#486) +* dodano % do układu dodatkowych symboli (#568, #428) +* poprawiono zachowanie, gdy klawisz przełączania języka jest ustawiony na przełączanie obu języków i klawiatury +* wyświetlanie linków do istniejących słowników podczas dodawania słownika +* dodano układ kaitag dzięki @alkaitagi (#519) +* dodano układ probhat dzięki @fahimscirex (#489) +* opcjonalne odwrócenie kolejności na pasku narzędzi dla języków RTL dzięki @codokie (#557, #574) +* możliwość dostosowywania układów specjalnych (klawiatura numeryczna, telefon, ...) + * wciąż eksperymentalne, ponieważ podstawowe układy mogą ulec zmianie +* zaktualizowano spellchecker.xml, aby uwzględniał lokalizacje, w których słowniki są dostępne, ale nie są zawarte w aplikacji +* aktualizacja tłumaczeń (dzięki wszystkim tłumaczom!) +* aktualizacja ndk dzięki @Syphyr (#560) +* aktualizacja kodu autouzupełniania dzięki @arcarum (#595) +* naprawiono błąd z oknem wybierania przycisków paska narzędzi (#505) +* poprawka błędu w układzie tureckim (#508) +* naprawiono nieprawidłowe stany przełączników na ekranie dostosowywania kolorów po obróceniu urządzenia (#563) +* naprawiono błąd związany z brakiem ładowania najnowszych emoji (#527) +* naprawiono błąd powodujący, że liczby nie pojawiały się w niektórych polach (#585) +* kilka drobnych poprawek diff --git a/fastlane/metadata/android/pl-PL/changelogs/1003.txt b/fastlane/metadata/android/pl-PL/changelogs/1003.txt new file mode 100644 index 000000000..c41adafcf --- /dev/null +++ b/fastlane/metadata/android/pl-PL/changelogs/1003.txt @@ -0,0 +1,9 @@ +* zmiana ikon na pasku narzędzi dla autokorekty i wybierania wszystkiego dzięki @codokie (#524, #651) +* dodano układ czuwaski dzięki @tenextractor (#677) +* dodano przycisk wycinania dzięki @codokie (#678) +* aktualizacja układu probhat dzięki @fahimscirex (#628) +* pokazywanie ikon paska narzędzi w oknie wybierania przycisków +* dodano przycisk zamykania historii schowka dzięki @codokie (#403, #649) +* dodano układ rosyjski (uczeń) dzięki @Zolax9 (#640) +* wyświetlanie klawiatury numerycznej poprzez przytrzymanie klawisza symboli jest teraz opcjonalne (#588) +* drobne poprawki i ulepszenia, w tym #632, #637, #638 dzięki @RHJihan diff --git a/fastlane/metadata/android/pl-PL/changelogs/1004.txt b/fastlane/metadata/android/pl-PL/changelogs/1004.txt new file mode 100644 index 000000000..3a1baabfa --- /dev/null +++ b/fastlane/metadata/android/pl-PL/changelogs/1004.txt @@ -0,0 +1,7 @@ +- aktualizacja układu serbskiego (cyrylica) dzięki @markokocic (#704, #705) +- aktualizacja układu estońskiego dzięki @tenextractor (#693) +- naprawa zduplikowanych wpisów w historii schowka dzięki @codokie (#616, #680) +- dodawanie tylko wpisów tekstowych do historii schowka dzięki @codokie (#711) +- lepsze obrazy w metadanych dzięki @RHJihan (#713) +- prawidłowe ustawienie koloru ikon w oknie wybierania przycisków paska narzędzi dzięki @codokie (#715, #716) +- inne poprawki (#684, #723 i więcej) diff --git a/fastlane/metadata/android/pl-PL/full_description.txt b/fastlane/metadata/android/pl-PL/full_description.txt new file mode 100644 index 000000000..980cc13e4 --- /dev/null +++ b/fastlane/metadata/android/pl-PL/full_description.txt @@ -0,0 +1,29 @@ +HeliBoard to dbająca o prywatność klawiatura typu open source, oparta na AOSP / OpenBoard. +Nie korzysta z uprawnień do internetu, więc jest w 100% offline. + +Funkcje: +
      +
    • Dodawaj słowniki, aby otrzymywać sugestie i sprawdzać pisownię
    • +
        +
      • stwórz własny lub pobierz stąd albo z sekcji eksperymentalnej (jakość może być różna)
      • +
      • dodatkowe słowniki dla emoji lub symboli naukowych można wykorzystać do otrzymywania sugestii (coś w stylu "wyszukiwania emoji")
      • +
      • weź pod uwagę, że w przypadku układów koreańskich sugestie działają tylko przy użyciu tego słownika, narzędzia w repozytorium nie są w stanie stworzyć działających słowników
      • +
      +
    • Dostosuj motyw klawiatury (styl, kolory i obraz tła)
    • +
        +
      • może śledzić ustawienie dzień/noc w systemie Android 10+ (oraz w niektórych wersjach Androida 9)
      • +
      • może stosować dynamiczne kolory w systemie Android 12+
      • +
      +
    • Dostosuj układ klawiatury (możliwe tylko po wyłączeniu opcji użyj języków systemu)
    • +
    • Pisanie wielojęzyczne
    • +
    • Pisanie gestami (tylko z zamkniętą biblioteką ☹️)
    • +
        +
      • biblioteka nie znajduje się w aplikacji, ponieważ nie ma kompatybilnej biblioteki typu open source
      • +
      • można ją wyodrębnić z pakietów GApps ("swypelibs"), lub pobrać stąd
      • +
      +
    • Historia schowka
    • +
    • Tryb jednej ręki
    • +
    • Podzielona klawiatura (jeśli ekran jest wystarczająco duży)
    • +
    • Klawiatura numeryczna
    • +
    • Tworzenie kopii zapasowych i przywracanie nauczonych słów / danych
    • +
    diff --git a/fastlane/metadata/android/pt-BR/full_description.txt b/fastlane/metadata/android/pt-BR/full_description.txt new file mode 100644 index 000000000..ca6995045 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/full_description.txt @@ -0,0 +1,29 @@ +O HeliBoard é um teclado que é consciente sobre a privacidade, e de código de aberto, baseado no do AOSP / OpenBoard. +Não usa a permissão de internet, e então é 100% offline. + +Recursos: +
      +
    • Adicione dicionários para sugestões ou verificação de ortografia
    • +
        +
      • faça os seus, ou obtenha-os aqui, ou na seção experimental (a qualidade pode variar)
      • +
      • dicionários adicionas de emojis ou símbolos podem ser usados para dar sugestões (similar à "pesquisa de emoji")
      • +
      • é importante notar que para layouts Coreanos, sugestões só funcionam usando este dicionário, as ferramentas no repositório de dicionários não conseguem criar dicionários funcionais
      • +
      +
    • Customize o tema do teclado (estilo, cores e a imagem de fundo)
    • +
        +
      • pode seguir a configuração de dia/noite do sistema no Android 10+ (e em alguns versões do Android 9)
      • +
      • pode seguir as cores dinâmicas no Android 12+
      • +
      +
    • Customize os layouts do teclado (disponível somente quando a opção Usar idiomas do sistema é desativada)
    • +
    • Escrita multi-linguagem
    • +
    • Escrita por Gestos (somente com uma biblioteca proprietária ☹️)
    • +
        +
      • a biblioteca não é inclusa no app, pois não existe uma alternativa de código aberto
      • +
      • pode ser extraída de pacotes GApps ("swypelibs"), ou baixados aqui
      • +
      +
    • Histórico da área de transferência
    • +
    • Modo de uma mão só
    • +
    • Teclado separado (só disponível quando a tela é grande o sucifiente)
    • +
    • Teclado númerico
    • +
    • Faça backup e restaure seus dados de palavras aprendidas ou de histórico
    • +
    diff --git a/fastlane/metadata/android/pt-BR/short_description.txt b/fastlane/metadata/android/pt-BR/short_description.txt new file mode 100644 index 000000000..21bfa940d --- /dev/null +++ b/fastlane/metadata/android/pt-BR/short_description.txt @@ -0,0 +1 @@ +Teclado customizável de código aberto diff --git a/fastlane/metadata/android/pt-BR/title.txt b/fastlane/metadata/android/pt-BR/title.txt new file mode 100644 index 000000000..e9841ace0 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/title.txt @@ -0,0 +1 @@ +HeliBoard diff --git a/fastlane/metadata/android/ru-RU/full_description.txt b/fastlane/metadata/android/ru-RU/full_description.txt new file mode 100644 index 000000000..80c31b125 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/full_description.txt @@ -0,0 +1,29 @@ +HeliBoard это клавиатура с открытым исходным кодом, обеспечивающая приватность, основанная на AOSP / OpenBoard. +Не использует Интернет и, таким образом, на 100% автономна. + +Возможности: +
      +
    • Добавьте словари для подсказок и проверки правописания.
    • +
        +
      • Создайте свои собственные или получите их здесь или в экспериментальном разделе (качество может отличаться)
      • +
      • Для предоставления подсказок можно использовать дополнительные словари смайлов или научных символов (аналогично «поиску смайлов»)
      • +
      • Обратите внимание, что для корейских раскладок предложения работают только с использованием этого словаря, инструментов из репозитория словарей.
      • +
      +
    • Настройка тем клавиатуры (стиль, цвета и фоновое изображение)
    • +
        +
      • Может следовать настройкам дня/ночи системы на Android 10+ (и на некоторых версиях Android 9)
      • +
      • Может следовать динамическим цветам для Android 12+
      • +
      +
    • Настроить раскладки клавиатуры (доступно только при отключении использования системных языков)
    • +
    • Многоязычный набор текста
    • +
    • Скользящий ввод текста (только с закрытой библиотекой ☹️)
    • +
        +
      • Библиотека не включена в приложение, поскольку не совместима с открытым исходным кодом.
      • +
      • Можно извлечь из пакетов GApps (swypelibs) или загрузить здесь
      • +
      +
    • История буфера обмена
    • +
    • Режим одной руки
    • +
    • Раздельная клавиатура (доступна только при достаточно большом экране)
    • +
    • Цифровая клавиатура
    • +
    • Создание резервной копии и восстанавление запомненных слов/истории
    • +
    diff --git a/fastlane/metadata/android/ru-RU/short_description.txt b/fastlane/metadata/android/ru-RU/short_description.txt new file mode 100644 index 000000000..703299a9e --- /dev/null +++ b/fastlane/metadata/android/ru-RU/short_description.txt @@ -0,0 +1 @@ +Настраиваемая клавиатура с открытым исходным кодом From 17aa321fbd6e5357a8f51452da7d461fb1118e17 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Tue, 21 May 2024 17:55:50 +0200 Subject: [PATCH 37/58] move background image and customizing number/symbol layouts out of experimental settings --- .../settings/AdvancedSettingsFragment.kt | 63 ----------------- .../settings/AppearanceSettingsFragment.kt | 70 +++++++++++++++++++ app/src/main/res/values-ar/strings.xml | 3 +- app/src/main/res/values-be/strings.xml | 3 +- app/src/main/res/values-bn/strings.xml | 3 +- app/src/main/res/values-cs/strings.xml | 3 +- app/src/main/res/values-de/strings.xml | 3 +- app/src/main/res/values-es-rUS/strings.xml | 3 +- app/src/main/res/values-eu/strings.xml | 3 +- app/src/main/res/values-fr/strings.xml | 3 +- app/src/main/res/values-gl/strings.xml | 3 +- app/src/main/res/values-hu/strings.xml | 1 - app/src/main/res/values-in/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 3 +- app/src/main/res/values-nb/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 3 +- app/src/main/res/values-pt-rBR/strings.xml | 3 +- app/src/main/res/values-ro/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 3 +- app/src/main/res/values-sv/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 3 +- app/src/main/res/values/strings.xml | 4 +- .../main/res/xml/prefs_screen_advanced.xml | 13 ++-- .../main/res/xml/prefs_screen_appearance.xml | 4 ++ 24 files changed, 94 insertions(+), 108 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt index 937441df1..33751a6ae 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt @@ -9,7 +9,6 @@ import android.annotation.SuppressLint import android.app.Activity import android.content.Intent import android.content.SharedPreferences -import android.graphics.BitmapFactory import android.net.Uri import android.os.Build import android.os.Bundle @@ -103,18 +102,6 @@ class AdvancedSettingsFragment : SubScreenFragment() { restore(uri) } - private val dayImageFilePicker = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - if (it.resultCode != Activity.RESULT_OK) return@registerForActivityResult - val uri = it.data?.data ?: return@registerForActivityResult - loadImage(uri, false) - } - - private val nightImageFilePicker = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - if (it.resultCode != Activity.RESULT_OK) return@registerForActivityResult - val uri = it.data?.data ?: return@registerForActivityResult - loadImage(uri, true) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setupPreferences() @@ -138,7 +125,6 @@ class AdvancedSettingsFragment : SubScreenFragment() { setupKeyLongpressTimeoutSettings() findPreference("load_gesture_library")?.setOnPreferenceClickListener { onClickLoadLibrary() } findPreference("backup_restore")?.setOnPreferenceClickListener { showBackupRestoreDialog() } - findPreference("custom_background_image")?.setOnPreferenceClickListener { onClickLoadImage() } findPreference("custom_symbols_number_layouts")?.setOnPreferenceClickListener { showCustomizeLayoutsDialog() @@ -239,55 +225,6 @@ class AdvancedSettingsFragment : SubScreenFragment() { } } - private fun onClickLoadImage(): Boolean { - if (Settings.readDayNightPref(sharedPreferences, resources)) { - AlertDialog.Builder(requireContext()) - .setMessage(R.string.day_or_night_image) - .setPositiveButton(R.string.day_or_night_day) { _, _ -> customImageDialog(false) } - .setNegativeButton(R.string.day_or_night_night) { _, _ -> customImageDialog(true) } - .setNeutralButton(android.R.string.cancel, null) - .show() - } else { - customImageDialog(false) - } - return true - } - - private fun customImageDialog(night: Boolean) { - val imageFile = Settings.getCustomBackgroundFile(requireContext(), night) - val builder = AlertDialog.Builder(requireContext()) - .setMessage(R.string.customize_background_image) - .setPositiveButton(R.string.button_load_custom) { _, _ -> - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) - .addCategory(Intent.CATEGORY_OPENABLE) - .setType("image/*") - if (night) nightImageFilePicker.launch(intent) - else dayImageFilePicker.launch(intent) - } - .setNegativeButton(android.R.string.cancel, null) - if (imageFile.exists()) { - builder.setNeutralButton(R.string.delete) { _, _ -> - imageFile.delete() - Settings.clearCachedBackgroundImages() - KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext()) - } - } - builder.show() - } - - private fun loadImage(uri: Uri, night: Boolean) { - val imageFile = Settings.getCustomBackgroundFile(requireContext(), night) - FileUtils.copyContentUriToNewFile(uri, requireContext(), imageFile) - try { - BitmapFactory.decodeFile(imageFile.absolutePath) - } catch (_: Exception) { - infoDialog(requireContext(), R.string.file_read_error) - imageFile.delete() - } - Settings.clearCachedBackgroundImages() - KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext()) - } - @SuppressLint("ApplySharedPref") private fun renameToLibfileAndRestart(file: File, checksum: String) { libfile.delete() diff --git a/app/src/main/java/helium314/keyboard/latin/settings/AppearanceSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/AppearanceSettingsFragment.kt index f066847d6..d372633e0 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/AppearanceSettingsFragment.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/AppearanceSettingsFragment.kt @@ -2,10 +2,16 @@ package helium314.keyboard.latin.settings +import android.app.Activity +import android.content.Intent import android.content.SharedPreferences import android.content.res.Configuration +import android.graphics.BitmapFactory +import android.net.Uri import android.os.Build import android.os.Bundle +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AlertDialog import androidx.core.util.TypedValueCompat import androidx.preference.ListPreference import androidx.preference.Preference @@ -13,7 +19,9 @@ import androidx.preference.TwoStatePreference import helium314.keyboard.keyboard.KeyboardSwitcher import helium314.keyboard.keyboard.KeyboardTheme import helium314.keyboard.latin.R +import helium314.keyboard.latin.common.FileUtils import helium314.keyboard.latin.utils.getStringResourceOrName +import helium314.keyboard.latin.utils.infoDialog import java.lang.Float.max import java.lang.Float.min import java.util.* @@ -33,6 +41,18 @@ class AppearanceSettingsFragment : SubScreenFragment() { 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) } + private val dayImageFilePicker = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode != Activity.RESULT_OK) return@registerForActivityResult + val uri = it.data?.data ?: return@registerForActivityResult + loadImage(uri, false) + } + + private val nightImageFilePicker = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode != Activity.RESULT_OK) return@registerForActivityResult + val uri = it.data?.data ?: return@registerForActivityResult + loadImage(uri, true) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) addPreferencesFromResource(R.xml.prefs_screen_appearance) @@ -51,6 +71,7 @@ class AppearanceSettingsFragment : SubScreenFragment() { true } } + findPreference("custom_background_image")?.setOnPreferenceClickListener { onClickLoadImage() } } override fun onPause() { @@ -144,6 +165,55 @@ class AppearanceSettingsFragment : SubScreenFragment() { userColorsPrefNight?.isVisible = dayNightPref?.isChecked == true && colorsNightPref?.value == KeyboardTheme.THEME_USER_NIGHT } + private fun onClickLoadImage(): Boolean { + if (Settings.readDayNightPref(sharedPreferences, resources)) { + AlertDialog.Builder(requireContext()) + .setMessage(R.string.day_or_night_image) + .setPositiveButton(R.string.day_or_night_day) { _, _ -> customImageDialog(false) } + .setNegativeButton(R.string.day_or_night_night) { _, _ -> customImageDialog(true) } + .setNeutralButton(android.R.string.cancel, null) + .show() + } else { + customImageDialog(false) + } + return true + } + + private fun customImageDialog(night: Boolean) { + val imageFile = Settings.getCustomBackgroundFile(requireContext(), night) + val builder = AlertDialog.Builder(requireContext()) + .setMessage(R.string.customize_background_image) + .setPositiveButton(R.string.button_load_custom) { _, _ -> + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) + .addCategory(Intent.CATEGORY_OPENABLE) + .setType("image/*") + if (night) nightImageFilePicker.launch(intent) + else dayImageFilePicker.launch(intent) + } + .setNegativeButton(android.R.string.cancel, null) + if (imageFile.exists()) { + builder.setNeutralButton(R.string.delete) { _, _ -> + imageFile.delete() + Settings.clearCachedBackgroundImages() + KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext()) + } + } + builder.show() + } + + private fun loadImage(uri: Uri, night: Boolean) { + val imageFile = Settings.getCustomBackgroundFile(requireContext(), night) + FileUtils.copyContentUriToNewFile(uri, requireContext(), imageFile) + try { + BitmapFactory.decodeFile(imageFile.absolutePath) + } catch (_: Exception) { + infoDialog(requireContext(), R.string.file_read_error) + imageFile.delete() + } + Settings.clearCachedBackgroundImages() + KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext()) + } + private fun setupScalePrefs(prefKey: String, defaultValue: Float) { val prefs = sharedPreferences val pref = findPreference(prefKey) as? SeekBarDialogPreference diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index e0dcb8620..ff6b291d4 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -259,7 +259,6 @@ لوحة ارقام الهاتف تخصيص الرموز وتخطيطات الأرقام - تكشف التخطيطات (باستثناء الرموز) عن الإعدادات الداخلية التي قد تتغير. إذا حدث ذلك، فقد لا يعمل التخطيط المخصص بشكل صحيح بعد الآن. رموز الهاتف لوحة رقمية (أفقي) هل تريد حقًا إزالة القاموس الذي أضافه المستخدم \"%s\"؟ @@ -344,7 +343,7 @@ اختر ترتيب المفاتيح المنبثقه الأبجدية (بيبو) اضغط لفترة طويلة على مفتاح الرموز للوحة الرقمية - قصّ + قصّ %s (طالب) استخدم دائمًا الاقتراح الأوسط عند الضغط على مسافة أو علامات الترقيم، سيتم إدخال الاقتراح الأوسط diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index cbaf32b54..cc12cadc5 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -275,7 +275,6 @@ Памылка загрузкі файла слоўніка кайтагскі Настройка размяшчэння знакаў і лікаў - Раскладкі (акрамя знакавых) залежаць ад ўнутраных налад, якія яшчэ могуць мяняцца. Калі гэта адбудзецца, кастамныя раскладкі могуць перастаць працаваць карэктна. Сімвалы Сімвалы (арабскія) Вертыкальны свайп па прабелу @@ -283,7 +282,7 @@ Гарызантальны свайп па прабелу Аддаваць перавагу лакалізаваныя лацінскія лічбы Паказваць падказкі, калі працяглы націск клавішы запускае дадатковыя функцыі - Выразаць + Выразаць Памылка раскладкі: %s Націсніце, каб адрэдагаваць базавую раскладку Больш сімвалаў diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 4d92a285f..5d4dfd094 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -330,13 +330,12 @@ নিজস্বীকৃত লেআউট %s বিলোপ করতে নিশ্চিত? আলো অথবা অন্ধকার মোডের জন্য ছবি নির্বাচিত হবে? প্রতীক এবং সংখ্যার লেআউট নিজস্বীকরণ - লেআউট (প্রতীক ব্যতীত) এখনও পরিবর্তিত হতে পারে এমন অভ্যন্তরীণ সেটিংস প্রকাশ করে। এমন হলে নিজস্বীকৃত লেআউট সঠিকভাবে কাজ নাও করতে পারে। নম্বর প্যাড এই শব্দটি ইতোমধ্যে %s ব্যবহারকারী অভিধানে উপলব্ধ। অন্য আরেকটি শব্দ টাইপ করুন। স্পেসবারের পটভূমি স্পেসবারে উল্লম্ব অঙ্গুলিহেলন স্পেসবারে অনুভূমিক অঙ্গুলিহেলন - কাটো + কাটো নাম্বারপ্যাডের জন্য সিম্বল বোতাম কিছুক্ষণ ধরে রাখো পরিবর্তনশীল টুলবার দিক ডান থেকে বাম কিবোর্ড নির্বাচন করলে উল্টো দিকে যাও diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index fe60dd5d4..f6b5d80ea 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -197,7 +197,7 @@ Jazyk (prioritní) Rozvržení Symboly - Vyjmout + Vyjmout Vybrat slovo Režim ovládání jednou rukou Úplně vlevo @@ -237,7 +237,6 @@ Opravdu odstranit vlastní rozložení %s? Chyba rozložení: %s Klepnutím lze upravit původní rozvržení - Rozložení (kromě symbolů) obsahují vnitřní nastavení, které se může ještě změnit. Pokud se tak stane, vlastní rozvržení již nemusí fungovat správně. Numerická klávesnice (na šířku) Nastavení obrázku na pozadí Nastavit obrázek pro denní nebo noční režim? diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a64b38b3b..8aa02cc81 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -332,7 +332,6 @@ Zahlen Ziffernblock Ziffernblock (Querformat) - Layouts (außer Symbole) legen interne Einstellungen offen, die sich möglicherweise noch ändern. In diesem Fall funktioniert das benutzerdefinierte Layout möglicherweise nicht mehr ordnungsgemäß. Kaitakisch Kaitakisch (%s) Vertikale Wischgeste der Leertaste @@ -340,7 +339,7 @@ Cursor bewegen Horizontale Wischgeste der Leertaste Symbole-Taste für Ziffernblock gedrückt halten - Ausschneiden + Ausschneiden Variable Symbolleisten-Richtung Richtung umkehren, wenn die Tastatur einer Rechts-nach-Links-Sprache ausgewählt ist %s (Student) diff --git a/app/src/main/res/values-es-rUS/strings.xml b/app/src/main/res/values-es-rUS/strings.xml index a016bf9fe..af07b3cbc 100644 --- a/app/src/main/res/values-es-rUS/strings.xml +++ b/app/src/main/res/values-es-rUS/strings.xml @@ -232,7 +232,7 @@ Licencia Pública General GNU v3.0 Cerrar Descripción de los elementos ocultos - Cortar + Cortar Limpiar portapapeles Entrada de voz Seleccionar palabra @@ -257,7 +257,6 @@ Error de diseño: %s Pulse para editar el diseño sin procesar Personaliza los símbolos y la disposición de los números - Los diseños (excepto los símbolos) exponen ajustes internos que aún pueden cambiar. Si esto ocurre, es posible que el diseño personalizado deje de funcionar correctamente. Símbolos Símbolos (árabe) Establecer imagen de fondo diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 07b4d2dcd..ba463fa84 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -247,7 +247,7 @@ Erakutsi aholku funtzionalak Erakutsi aholkuak tekla bat luze sakatzeak funtzionalitate gehigarriak abiarazten baditu Argia - Moztu + Moztu Garbitu arbela Hautatu hitza %s (Sebeolsik 390) @@ -262,7 +262,6 @@ Benetan ezabatu %s diseinu pertsonalizatua? Sakatu diseinu gordina editatzeko Pertsonalizatu ikurrak eta zenbaki-diseinuak - Diseinuek (ikurrak izan ezik) oraindik alda daitezkeen barne ezarpenak erakusten dituzte. Hori gertatzen bada, baliteke diseinu pertsonalizatuak behar bezala ez funtzionatzea. Ikurrak (Arabiarrak) ikur gehiago Telefonoa diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 4d03b5d53..160d44841 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -362,7 +362,7 @@ Nouveau dictionnaire: Aucun Balayage horizontal de la barre d\'espace Pavé numérique (paysage) - Couper + Couper Kaitag (%s) Symboles (Arabe) Symboles du pavé téléphonique @@ -374,7 +374,6 @@ Nouveau dictionnaire: Symboles %s (Étudiant) %s (Probhat) - Les mises en page (à l\'exception des symboles) exposent des paramètres internes qui peuvent encore changer. Dans ce cas, la mise en page personnalisée risque de ne plus fonctionner correctement Toujours utiliser la suggestion du milieu En appuyant sur espace ou sur un signe de ponctuation, la suggestion du milieu sera saisie Fermer le presse-papiers diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 3db7a2e12..49a8543d2 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -331,7 +331,6 @@ Números Teclado numérico Teclado Num (paisaxe) - As disposicións (excepto os símbolos) usan axustes internos que aínda poderían cambiar. Se acontece, a disposición podería deixar de funcionar. ► Coa pulsación longa na tecla do Portapapeis (opcional nas barra coas suxestións) pegas o contido do portapapeis. <br><br> ► Coa pulsación longa nunha tecla la barra de suxestións fíxala na barra. <br> <br> ► Coa pulsación longa na telca da vírgula accedes a Vista do Portapapeis, Vista de Emojis, Modo dunha soa man, Axustes ou Cambiar de Idioma: <br> • A Vista de Emoji e Cambio de Idioma aparecerán se tes a tecla correspondente activada; <br> \t • Nalgunhas disposicións non é a tecla da Vírgula, se non a tecla que está na mesma posición (ex. é \'q\' no teclado Dvorak). <br> <br> ► Co modo Incógnito activado, non aprenderá palabras, e tampouco se engaden emojis aos recentes. <br> <br> ► Preme na icona Incógnito para acceder ás ferramentas. <br> <br> ► Escribir con tecla esvaradía: Despraza desde a tecla Shift hacia outra letra para escribir unha sóa maiúscula: <br> \t• Isto tamén funciona para a tecla \'?123\" para escribir un só símbolo dos do teclado de símbolos, e as teclas relacionadas. <br> <br> ► Coa pulsación longa nunha suxestión da barra de suxestións aparecen máis suxestións, e tamén un botón para eliminar esa suxestión. <br> <br> ► Desprázate hacia arriba para ter máis suxestións, e solta onde estea suxestión que queres. <br> <br> ► Fixa unha entrada do historial do portapapeis coa pulsación longa sobre ela (mantela no portapapeis ata que a liberes). <br> <br> ► Podes engadir dicionarios abríndoos nun explorador de ficheiros: <br> \t• Isto só funciona con <i>content-uris</i> e non con <i>file-uris</i>, polo que podería non funcionar nalgúns exploradores de ficheiros. <br> <br> <i>Modo depuración / APK debug</i> <br> <br> \t • Pulsación longa sobre unha suxestión para mostrar o dicionario de orixe.<br> <br> \t • No modo depuración APK, podes atopar os Axustes de Depuración dentro de Preferencias Avanzadas, aínda que a utilidade é limitada excepto para meter os dicionarios no ficheiro de depuración.<br> <br> \t • Se fallase a aplicación, pediráseche se queres ver os ficheiros de depuración ao abrir os Axustes. <br> <br> \t• Ao usar a escritura multi-idioma, a barra espazadora mostrará un valor de confianza usado para determinar o idioma que estás a usar. <br> <br> \t• As suxestións terán un número pequeniño enriba mostrando unha puntuación interna e o dicionario de orixe (pode desactivarse).<br> <br> ► Para as usuarias que fan copias de apoio con acceso root: Desde Android 7, o ficheiro de preferencias compartidas non está na localización por defecto, xa que a app usa %s. <br> isto é preciso para que os axustes se poidan ler antes de que o dispositivo se desbloquee, por exemplo no inicio (boot). <br> O ficheiro está en /data/user_de/0/package_id/shared_prefs/, aínda que podería depender da versión do dispositivo Android. Kaitag (%s) Kaitag @@ -344,7 +343,7 @@ %s (Probhat) %s (Estudante) Tecla con pulsación longa para teclado numérico - Cortar + Cortar Usar sempre a suxestión do medio Ao premer no espazo ou puntuación, escríbese a suxestión do medio Escoller teclas da barra de ferramentas do portapapeis diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 08eeebdd5..a23698e90 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -229,7 +229,6 @@ Nyomjon a nyelvre a beállítások megnyitásához Rejtett funkciók leírása Szimbólumok és számok elrendezésének testre szabása - Elrendezések (kivéve a szimbólumok) felfedik a belső beállításokat, melyek tovább változhatnak. Ha ez megtörténik, az egyéni elrendezés nem fog tovább működni. Szimbólumok Saját ízlés szerint Saját ízlés szerint (éjjel) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index fc88adbf0..235eba42f 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -291,7 +291,6 @@ "Tanpa kamus, Anda hanya akan mendapatkan saran untuk teks yang Anda masukkan sebelumnya.<br> \n Anda dapat mengunduh kamus %1$s, atau memeriksa apakah kamus untuk \"%2$s\" dapat diunduh secara langsung dari %3$s." Hitam - Tata letak (kecuali simbol) mengekspos pengaturan internal yang mungkin masih dapat berubah. Jika hal itu terjadi, tata letak kustom mungkin tidak akan berfungsi dengan baik lagi. Mengonfigurasi papan ketik Pada bahasa apa kamus \"%1$s\" untuk %2$s harus ditambahkan? Jangan tampilkan lagi diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 30493d8c2..2ca4c50c5 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -328,7 +328,6 @@ Numeri Tastierino numerico (orizzontale) Personalizzazione dei simboli e dei layout dei numeri - I layout (eccetto i simboli) espongono impostazioni interne che possono essere modificate. In tal caso, il layout personalizzato potrebbe non funzionare più correttamente. Tastierino numerico Simboli telefono %s (studenti) @@ -340,7 +339,7 @@ Kaitag (%s) %s (Probhat) Premi a lungo il tasto dei simboli per il tastierino numerico - Taglia + Taglia Inverte la direzione quando è selezionato un sottotipo di tastiera da destra a sinistra Direzione barra strumenti variabile Colore accento diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index b3eb990f3..f8b84dde4 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -216,7 +216,6 @@ Open-source lisens enhetsbeskyttet lagring Tilpass symboler og talloppsett - Oppsett (unntatt symboler) viser interne innstillinger som fortsatt kan endres. Hvis det skjer, kan det hende at det tilpassede oppsettet ikke fungerer som det skal lenger. Symboler Symboler (Arabisk) Flere symboler diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 617a8c493..3ee18f428 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -358,7 +358,6 @@ Zmień oba %s (eksperymentalny) Dostosuj układ symboli i liczb - Układy (z wyjątkiem symbolicznych) zależą od ustawień wewnętrznych, które mogą jeszcze ulec zmianie. W takim przypadku układy niestandardowe mogą przestać działać poprawnie. Symbole Symbole (arabski) Telefon @@ -377,7 +376,7 @@ %s (Probhat) %s (Uczeń) Przytrzymaj klawisz symboli, aby wyświetlić klawiaturę numeryczną - Wytnij + Wytnij Zawsze używaj środkowej sugestii Po naciśnięciu spacji lub znaku interpunkcyjnego zostanie wpisana środkowa sugestia Zamknij schowek diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 040b0bc6b..d01bc77aa 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -255,7 +255,6 @@ Copiar layout existente Toque para editar o layout bruto Customizar layouts de símbolos ou números - Layouts (exceto símbolos) expõem configurações internas que podem mudar ainda. Se isso acontecer, o layout customizado pode não mais funcionar corretamente. Símbolos Símbolos (Árabe) Mais símbolos @@ -341,7 +340,7 @@ Nenhum Inverter a direção quando um teclado do subtipo direita-para-esquerda é selecionado Toque longo na tecla de símbolos para abrir o numpad - Cortar + Cortar %s (Estudante) Licença Pública Geral GNU v3.0 Sempre usar a sugestão do meio diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 2f98fa96e..32cff0a92 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -219,7 +219,6 @@ Setează numele aspectului Atinge pentru a edita aspectul brut Personalizează simbolurile și modelele de numere - Aspectele (cu excepția simbolurilor) expun setările interne care se pot schimba în continuare. Dacă se întâmplă acest lucru, este posibil ca aspectul personalizat să nu mai funcționeze corect. Simboluri Simboluri (Arabă) Mai multe simboluri diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index d62c7ef9d..4bb26078e 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -333,7 +333,6 @@ Числа Числовая клавиатура Числовая клавиатура (горизонтальная) - Раскладки (кроме символьных) зависят от внутренних настроек, которые ещё могут меняться. Если это произойдёт, кастомные раскладки могут перестать работать корректно. Телефон Символы телефона ► Долгое нажатие клавиши буфера обмена (закреплённой в полосе подсказок) вставляет содержимое буфера обмена. <br> <br> ► Долгое нажатие клавиш на панели инструментов полосы подсказок закрепляет их на полосу подсказок. <br> <br> ► Удерживайте клавишу запятой для доступа к буферу обмена, меню эмодзи, режиму одной руки, настройкам или смене языка: <br> \t• Меню эмодзи и смена языка исчезнет, если соответствующая клавиша включена; <br> \t• Для некоторых раскладок это не клавиша запятой, а клавиша в той же позиции (например, это \'q\' для раскладки Dvorak). <br> <br> ► Когда включён режим инкогнито, ни одно слово не будет выучено и ни один эмодзи не будет добавлен в недавние. <br> <br> ► Нажмите на иконку Инкогнито для доступа к панели инструментов. <br> <br> ► Непрерывный ввод: Проведите от Shift до другой клавиши, чтобы ввести одну заглавную букву: <br> \t• Это также работает для клавиши \'?123\', чтобы ввести один символ из клавиатуры символов, и для связанных клавиш. <br> <br> ► Удерживайте подсказку в полосе подсказок, чтобы показать больше подсказок, и кнопку удалить, чтобы убрать эту подсказку. <br> <br> ► Проведите вверх от подсказки, чтобы открыть больше подсказок, и отпустите на подсказке, чтобы выбрать её. <br> <br> ► Удерживайте элемент в журнале буфера обмена, чтобы закрепить его (сохранять его в буфере обмена, пока не открепите). <br> <br> ► Вы можете добавить словари, открыв их в проводнике: <br> \t• Это работает только с <i>content-uris</i>, а не с <i>file-uris</i>, что означает, что это может не работать в некоторых проводниках. <br> <br> <i>Режим отладки / отладочный APK</i> <br> <br> \t• Удерживайте подсказку, чтобы показать исходный словарь.<br> <br> \t• При использовании отладочного APK, вы можете найти настройки отладки в дополнительных настройках, хотя польза от них невелика, за исключением сброса словарей в журнал. <br> <br> \t• При сбое приложения, вам будет предложен журнал отладки, когда вы откроете настройки. <br> <br> \t• При использовании многоязыкового ввода, на клавише пробела будет показано доверительное значение, используемое для определения текущего языка. <br> <br> \t• У подсказок будет некоторые маленькие числа сверху, показывающие внутренние показатели и исходный словарь (может быть отключено). <br> <br> ► Для пользователей, делающих ручное резервное копирование с root-доступом: Начиная с Android 7, общий файл настроек не находится в локации по умолчанию, потому что приложение использует %s. <br> Это необходимо, чтобы настройки могли быть прочитаны перед разблокировкой устройства, например при запуске. <br> Файл находится в /data/user_de/0/package_id/shared_prefs/, хотя это может зависеть от устройства и версии Android. @@ -346,5 +345,5 @@ Перемещение курсора Длительное нажатие клавиши символов для цифровой клавиатуры %s (Студенческая) - Вырезать + Вырезать \ No newline at end of file diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index a72f9af19..86b09d109 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -264,7 +264,7 @@ Rensa urklipp Röstinmatning Markera ord - Klipp ut + Klipp ut Enhandsläge Upp Ner diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 1d2ab0f8b..cc5406d29 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -261,7 +261,7 @@ Очистити буфер обміну Голосове введення Вниз - Вирізати + Вирізати Виберіть слово Режим однієї руки У лівий кінець @@ -310,7 +310,6 @@ сховище, захищене пристроєм Словники До якої мови слід додати словник «%1$s» для %2$s? - Розкладки (крім символьних) залежать від внутрішніх налаштувань, які можуть змінюватися. Якщо це станеться, кастомні розкладки можуть не працювати коректно. День Ніч Налаштування клавіатури diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dc5e9b79f..54b9163d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -233,7 +233,7 @@ Select toolbar keys @android:string/copy - Cut + Cut Clipboard Clear clipboard Voice input @@ -462,8 +462,6 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM Tap to edit raw layout Customize symbols and number layouts - - Layouts (except symbols) expose internal settings which may still change. If that happens, the custom layout may not work properly any more. Symbols diff --git a/app/src/main/res/xml/prefs_screen_advanced.xml b/app/src/main/res/xml/prefs_screen_advanced.xml index 5099910ec..061104f65 100644 --- a/app/src/main/res/xml/prefs_screen_advanced.xml +++ b/app/src/main/res/xml/prefs_screen_advanced.xml @@ -86,6 +86,10 @@ android:persistent="true" latin:singleLineTitle="false" /> + + @@ -106,15 +110,6 @@ android:summary="@string/url_detection_summary" android:defaultValue="false" /> - - - - + + Date: Wed, 22 May 2024 22:34:17 +0200 Subject: [PATCH 38/58] add setting for customizing functional key layouts --- .../keyboard_parser/KeyboardParser.kt | 8 +-- .../keyboard_parser/RawKeyboardParser.kt | 45 ++++--------- .../main/java/helium314/keyboard/latin/App.kt | 12 ++-- .../settings/AdvancedSettingsFragment.kt | 43 ++++++++++-- .../latin/settings/SettingsValues.java | 3 + .../keyboard/latin/utils/CustomLayoutUtils.kt | 66 ++++++++++++++++++- .../keyboard/latin/utils/SubtypeSettings.kt | 4 +- app/src/main/res/values/strings.xml | 8 +++ .../main/res/xml/prefs_screen_advanced.xml | 4 ++ layouts.md | 18 ++--- 10 files changed, 150 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index 30a5f42f3..c492e8d59 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -197,8 +197,8 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co val functionalKeysBottom = allFunctionalKeys.lastOrNull() ?: return if (!params.mId.isAlphaOrSymbolKeyboard || functionalKeysBottom.isEmpty() || functionalKeysBottom.any { it.isKeyPlaceholder() }) return - if (true /* Settings.getInstance().current.mSingleFunctionalLayout */) { // todo with the customizable functional layout - // remove unwanted keys (emoji, numpad, language switch) + if (!Settings.getInstance().current.mHasCustomFunctionalLayout) { + // remove keys that should only exist on specific layouts or depend on setting (emoji, numpad, language switch) if (!Settings.getInstance().current.mShowsEmojiKey || !params.mId.isAlphabetKeyboard) functionalKeysBottom.removeFirst { it.label == KeyLabel.EMOJI } if (!Settings.getInstance().current.isLanguageSwitchKeyEnabled || !params.mId.isAlphabetKeyboard) @@ -314,7 +314,3 @@ const val LAYOUT_NUMPAD_LANDSCAPE = "numpad_landscape" const val LAYOUT_NUMBER = "number" const val LAYOUT_PHONE = "phone" const val LAYOUT_PHONE_SYMBOLS = "phone_symbols" -const val FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED = "functional_keys_symbols_shifted" -const val FUNCTIONAL_LAYOUT_SYMBOLS = "functional_keys_symbols" -const val FUNCTIONAL_LAYOUT = "functional_keys" -const val FUNCTIONAL_LAYOUT_TABLET = "functional_keys_tablet" diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt index 7c47dae93..1dbb2310a 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt @@ -22,8 +22,9 @@ import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.ScriptUtils import helium314.keyboard.latin.utils.ScriptUtils.script +import helium314.keyboard.latin.utils.getCustomFunctionalLayoutName import helium314.keyboard.latin.utils.getCustomLayoutFile -import helium314.keyboard.latin.utils.getCustomLayoutsDir +import helium314.keyboard.latin.utils.getCustomLayoutFiles import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic @@ -35,13 +36,12 @@ object RawKeyboardParser { val symbolAndNumberLayouts = listOf(LAYOUT_SYMBOLS, LAYOUT_SYMBOLS_SHIFTED, LAYOUT_SYMBOLS_ARABIC, LAYOUT_NUMBER, LAYOUT_NUMPAD, LAYOUT_NUMPAD_LANDSCAPE, LAYOUT_PHONE, LAYOUT_PHONE_SYMBOLS) - // todo: cache is by layout name, this is inefficient for functional keys by default fun clearCache() = rawLayoutCache.clear() fun parseLayout(params: KeyboardParams, context: Context, isFunctional: Boolean = false): MutableList> { val layoutName = if (isFunctional) { if (!params.mId.isAlphaOrSymbolKeyboard) return mutableListOf(mutableListOf()) - else getFunctionalLayoutName(params) + else getFunctionalLayoutName(params, context) } else { getLayoutName(params, context) } @@ -121,43 +121,24 @@ object RawKeyboardParser { else -> params.mId.mSubtype.keyboardLayoutSetName.substringBeforeLast("+") } - // todo (later, see also keyboardParser): use Settings.getInstance().current.mSingleFunctionalLayout - private fun getFunctionalLayoutName(params: KeyboardParams) = when (params.mId.mElementId) { - KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED - KeyboardId.ELEMENT_SYMBOLS -> FUNCTIONAL_LAYOUT_SYMBOLS - else -> if (Settings.getInstance().isTablet) FUNCTIONAL_LAYOUT_TABLET else FUNCTIONAL_LAYOUT + private fun getFunctionalLayoutName(params: KeyboardParams, context: Context): String { + if (Settings.getInstance().current.mHasCustomFunctionalLayout) { + getCustomFunctionalLayoutName(params.mId.mElementId, params.mId.mSubtype.rawSubtype, context) + ?.let { return it } + } + return if (Settings.getInstance().isTablet) "functional_keys_tablet" else "functional_keys" } /** returns the file name matching the layout name, making sure the file exists (falling back to qwerty.txt) */ private fun getLayoutFileName(layoutName: String, context: Context): String { - val customFiles = getCustomLayoutsDir(context).list() + val customFiles = getCustomLayoutFiles(context).map { it.name } if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) { - return if (customFiles?.contains(layoutName) == true) layoutName - else "qwerty.txt" // fallback + return customFiles.firstOrNull { it.startsWith(layoutName)} + ?: if (layoutName.contains("functional")) "functional_keys.json" else "qwerty.txt" // fallback to defaults } val assetsFiles by lazy { context.assets.list("layouts")!! } - if (layoutName.startsWith("functional")) { - // return custom match if we have one - val customMatch = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") } - if (customMatch != null) return customMatch - if (layoutName == FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) { - // no custom symbols shifted layout, try custom symbols layout - val customSymbols = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT_SYMBOLS.") } - if (customSymbols != null) return customSymbols - } - // no custom symbols layout, try custom functional layout - if (Settings.getInstance().isTablet) { - val customTablet = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT_TABLET.") } - if (customTablet != null) return customTablet - } - val customFunctional = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT.") } - if (customFunctional != null) return customFunctional - // no custom functional layout, use the default functional layout - return if (Settings.getInstance().isTablet) "$FUNCTIONAL_LAYOUT_TABLET.json" - else "$FUNCTIONAL_LAYOUT.json" - } return if (layoutName in symbolAndNumberLayouts) { - customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.")} + customFiles.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.")} ?: assetsFiles.first { it.startsWith(layoutName) } } else { // can't be custom layout, so it must be in assets diff --git a/app/src/main/java/helium314/keyboard/latin/App.kt b/app/src/main/java/helium314/keyboard/latin/App.kt index a97a168a4..7932ed2c0 100644 --- a/app/src/main/java/helium314/keyboard/latin/App.kt +++ b/app/src/main/java/helium314/keyboard/latin/App.kt @@ -11,7 +11,8 @@ import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.DictionaryInfoUtils -import helium314.keyboard.latin.utils.getCustomLayoutsDir +import helium314.keyboard.latin.utils.getCustomLayoutFile +import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged import helium314.keyboard.latin.utils.upgradeToolbarPrefs import java.io.File @@ -51,10 +52,9 @@ fun checkVersionUpgrade(context: Context) { if (oldVersion == 0) // new install or restoring settings from old app name upgradesWhenComingFromOldAppName(context) if (oldVersion <= 1000) { // upgrade old custom layouts name - val layoutsDir = getCustomLayoutsDir(context) - val oldShiftSymbolsFile = File(layoutsDir, "${CUSTOM_LAYOUT_PREFIX}shift_symbols") + val oldShiftSymbolsFile = getCustomLayoutFile("${CUSTOM_LAYOUT_PREFIX}shift_symbols", context) if (oldShiftSymbolsFile.exists()) { - oldShiftSymbolsFile.renameTo(File(layoutsDir, "${CUSTOM_LAYOUT_PREFIX}symbols_shifted")) + oldShiftSymbolsFile.renameTo(getCustomLayoutFile("${CUSTOM_LAYOUT_PREFIX}symbols_shifted", context)) } // rename subtype setting, and clean old subtypes that might remain in some cases @@ -73,6 +73,7 @@ fun checkVersionUpgrade(context: Context) { putString(Settings.PREF_SELECTED_SUBTYPE, selectedSubtype) } } + onCustomLayoutFileListChanged() // just to be sure prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) } } @@ -80,9 +81,8 @@ fun checkVersionUpgrade(context: Context) { private fun upgradesWhenComingFromOldAppName(context: Context) { // move layout files try { - val layoutsDir = getCustomLayoutsDir(context) File(context.filesDir, "layouts").listFiles()?.forEach { - it.copyTo(File(layoutsDir, it.name), true) + it.copyTo(getCustomLayoutFile(it.name, context), true) it.delete() } } catch (_: Exception) {} diff --git a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt index 33751a6ae..3669fec44 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt @@ -41,12 +41,15 @@ import helium314.keyboard.latin.common.FileUtils import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.settings.SeekBarDialogPreference.ValueProxy import helium314.keyboard.latin.utils.AdditionalSubtypeUtils +import helium314.keyboard.latin.utils.CUSTOM_FUNCTIONAL_LAYOUT_NORMAL +import helium314.keyboard.latin.utils.CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS +import helium314.keyboard.latin.utils.CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.ExecutorUtils import helium314.keyboard.latin.utils.JniUtils import helium314.keyboard.latin.utils.editCustomLayout -import helium314.keyboard.latin.utils.getCustomLayoutsDir +import helium314.keyboard.latin.utils.getCustomLayoutFiles import helium314.keyboard.latin.utils.getStringResourceOrName import helium314.keyboard.latin.utils.infoDialog import helium314.keyboard.latin.utils.reloadEnabledSubtypes @@ -127,7 +130,11 @@ class AdvancedSettingsFragment : SubScreenFragment() { findPreference("backup_restore")?.setOnPreferenceClickListener { showBackupRestoreDialog() } findPreference("custom_symbols_number_layouts")?.setOnPreferenceClickListener { - showCustomizeLayoutsDialog() + showCustomizeSymbolNumberLayoutsDialog() + true + } + findPreference("custom_functional_key_layouts")?.setOnPreferenceClickListener { + showCustomizeFunctionalKeyLayoutsDialog() true } } @@ -145,7 +152,7 @@ class AdvancedSettingsFragment : SubScreenFragment() { } } - private fun showCustomizeLayoutsDialog() { + private fun showCustomizeSymbolNumberLayoutsDialog() { val layoutNames = RawKeyboardParser.symbolAndNumberLayouts.map { it.getStringResourceOrName("layout_", requireContext()) }.toTypedArray() AlertDialog.Builder(requireContext()) .setTitle(R.string.customize_symbols_number_layouts) @@ -158,8 +165,8 @@ class AdvancedSettingsFragment : SubScreenFragment() { } private fun customizeSymbolNumberLayout(layoutName: String) { - val customLayoutName = getCustomLayoutsDir(requireContext()).list() - ?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") } + val customLayoutName = getCustomLayoutFiles(requireContext()).map { it.name } + .firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") } val originalLayout = if (customLayoutName != null) null else { requireContext().assets.list("layouts")?.firstOrNull { it.startsWith("$layoutName.") } @@ -169,6 +176,32 @@ class AdvancedSettingsFragment : SubScreenFragment() { editCustomLayout(customLayoutName ?: "$CUSTOM_LAYOUT_PREFIX$layoutName.txt", requireContext(), originalLayout, displayName) } + private fun showCustomizeFunctionalKeyLayoutsDialog() { + val list = listOf(CUSTOM_FUNCTIONAL_LAYOUT_NORMAL, CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS, CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) + .map { it.substringBeforeLast(".") } + val layoutNames = list.map { it.substringAfter(CUSTOM_LAYOUT_PREFIX).getStringResourceOrName("layout_", requireContext()) }.toTypedArray() + AlertDialog.Builder(requireContext()) + .setTitle(R.string.customize_functional_key_layouts) + .setItems(layoutNames) { di, i -> + di.dismiss() + customizeFunctionalKeysLayout(list[i]) + } + .setNegativeButton(android.R.string.cancel, null) + .show() + } + + private fun customizeFunctionalKeysLayout(layoutName: String) { + val customLayoutName = getCustomLayoutFiles(requireContext()).map { it.name } + .firstOrNull { it.startsWith("$layoutName.") } + val originalLayout = if (customLayoutName != null) null + else { + val defaultLayoutName = if (Settings.getInstance().isTablet) "functional_keys_tablet.json" else "functional_keys.json" + requireContext().assets.open("layouts" + File.separator + defaultLayoutName).reader().readText() + } + val displayName = layoutName.substringAfter(CUSTOM_LAYOUT_PREFIX).getStringResourceOrName("layout_", requireContext()) + editCustomLayout(customLayoutName ?: "$layoutName.json", requireContext(), originalLayout, displayName) + } + @SuppressLint("ApplySharedPref") private fun onClickLoadLibrary(): Boolean { // get architecture for telling user which file to use diff --git a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java index f60df9715..92c523bf9 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java @@ -25,6 +25,7 @@ import helium314.keyboard.latin.RichInputMethodManager; import helium314.keyboard.latin.common.Colors; import helium314.keyboard.latin.permissions.PermissionsUtil; +import helium314.keyboard.latin.utils.CustomLayoutUtilsKt; import helium314.keyboard.latin.utils.InputTypeUtils; import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.PopupKeysUtilsKt; @@ -122,6 +123,7 @@ public class SettingsValues { public final SettingsValuesForSuggestion mSettingsValuesForSuggestion; public final boolean mIncognitoModeEnabled; public final boolean mLongPressSymbolsForNumpad; + public final boolean mHasCustomFunctionalLayout; // User-defined colors public final Colors mColors; @@ -237,6 +239,7 @@ public SettingsValues(final Context context, final SharedPreferences prefs, fina mSpacingAndPunctuations = new SpacingAndPunctuations(res, mUrlDetectionEnabled); mBottomPaddingScale = prefs.getFloat(Settings.PREF_BOTTOM_PADDING_SCALE, DEFAULT_SIZE_SCALE); mLongPressSymbolsForNumpad = prefs.getBoolean(Settings.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD, false); + mHasCustomFunctionalLayout = CustomLayoutUtilsKt.hasCustomFunctionalLayout(selectedSubtype, context); } public boolean isApplicationSpecifiedCompletionsOn() { diff --git a/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt index 3fa3d2e64..68bc7710e 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt @@ -5,6 +5,7 @@ import android.content.Context import android.net.Uri import android.provider.OpenableColumns import android.text.InputType +import android.view.inputmethod.InputMethodSubtype import android.widget.EditText import androidx.appcompat.app.AlertDialog import androidx.core.widget.doAfterTextChanged @@ -17,6 +18,7 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams import helium314.keyboard.latin.R +import helium314.keyboard.latin.common.Constants import helium314.keyboard.latin.common.FileUtils import java.io.File import java.io.IOException @@ -112,7 +114,7 @@ private fun checkKeys(keys: List>): Boolean { Log.w(TAG, "too many keys in one row") return false } - if (keys.any { row -> row.any { ((it.mLabel?.length ?: 0) > 6) } }) { + if (keys.any { row -> row.any { ((it.mLabel?.length ?: 0) > 20) } }) { Log.w(TAG, "too long text on key") return false } @@ -127,10 +129,24 @@ private fun checkKeys(keys: List>): Boolean { return true } +/** don't rename or delete the file without calling [onCustomLayoutFileListChanged] */ fun getCustomLayoutFile(layoutName: String, context: Context) = File(getCustomLayoutsDir(context), layoutName) -fun getCustomLayoutsDir(context: Context) = File(DeviceProtectedUtils.getFilesDir(context), "layouts") +// cache to avoid frequently listing files +/** don't rename or delete files without calling [onCustomLayoutFileListChanged] */ +fun getCustomLayoutFiles(context: Context): List { + customLayouts?.let { return it } + val layouts = getCustomLayoutsDir(context).listFiles()?.toList() ?: emptyList() + customLayouts = layouts + return layouts +} + +fun onCustomLayoutFileListChanged() { + customLayouts = null +} + +private fun getCustomLayoutsDir(context: Context) = File(DeviceProtectedUtils.getFilesDir(context), "layouts") // undo the name changes in loadCustomLayout when clicking ok fun getLayoutDisplayName(layoutName: String) = @@ -164,6 +180,7 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String? file.writeText(content) if (isJson != wasJson) // unlikely to be needed, but better be safe file.renameTo(File(file.absolutePath.substringBeforeLast(".") + "." + if (isJson) "json" else "txt")) + onCustomLayoutFileListChanged() KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(context) } } @@ -173,6 +190,7 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String? builder.setNeutralButton(R.string.delete) { _, _ -> confirmDialog(context, context.getString(R.string.delete_layout, displayName), context.getString(R.string.delete)) { file.delete() + onCustomLayoutFileListChanged() KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(context) } } @@ -182,6 +200,45 @@ fun editCustomLayout(layoutName: String, context: Context, startContent: String? builder.show() } +fun hasCustomFunctionalLayout(subtype: InputMethodSubtype, context: Context): Boolean { + val anyCustomFunctionalLayout = getCustomFunctionalLayoutName(KeyboardId.ELEMENT_ALPHABET, subtype, context) + ?: getCustomFunctionalLayoutName(KeyboardId.ELEMENT_SYMBOLS, subtype, context) + ?: getCustomFunctionalLayoutName(KeyboardId.ELEMENT_SYMBOLS_SHIFTED, subtype, context) + return anyCustomFunctionalLayout != null +} + +fun getCustomFunctionalLayoutName(elementId: Int, subtype: InputMethodSubtype, context: Context): String? { + val customFunctionalLayoutNames = getCustomLayoutFiles(context).filter { it.name.contains("functional") }.map { it.name.substringBeforeLast(".") + "." } + if (customFunctionalLayoutNames.isEmpty()) return null + val languageTag = subtype.locale().toLanguageTag() + val mainLayoutName = subtype.getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET) ?: "qwerty" + + if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { + findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) }, mainLayoutName, languageTag) + ?.let { return it } + } + if (elementId == KeyboardId.ELEMENT_SYMBOLS) { + findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS) }, mainLayoutName, languageTag) + ?.let { return it } + } + return findMatchingLayout(customFunctionalLayoutNames.filter { it.startsWith(CUSTOM_FUNCTIONAL_LAYOUT_NORMAL) }, mainLayoutName, languageTag) +} + +// todo (when adding custom layouts per locale or main layout): adjust mainLayoutName for custom layouts? +// remove language tag and file ending (currently name is e.g. custom.en-US.abcdfdsg3.json, and we could use abcdfdsg3 only) +// this way, custom layouts with same name could use same custom functional layouts +// currently there is no way to set the language tag or main layout name, so changes don't break backwards compatibility +private fun findMatchingLayout(layoutNames: List, mainLayoutName: String, languageTag: String): String? { + // first find layout with matching locale and main layout + return layoutNames.firstOrNull { it.endsWith(".$languageTag.$mainLayoutName.") } + // then find matching main layout + ?: layoutNames.firstOrNull { it.endsWith(".$mainLayoutName.") } + // then find matching language + ?: layoutNames.firstOrNull { it.endsWith(".$languageTag.") } + // then find "normal" functional layout (make use of the '.' separator + ?: layoutNames.firstOrNull { it.count { it == '.' } == 2 } +} + private fun encodeBase36(string: String): String = BigInteger(string.toByteArray()).toString(36) private fun decodeBase36(string: String) = BigInteger(string, 36).toByteArray().decodeToString() @@ -189,3 +246,8 @@ private fun decodeBase36(string: String) = BigInteger(string, 36).toByteArray(). // this goes into prefs and file names, so do not change! const val CUSTOM_LAYOUT_PREFIX = "custom." private const val TAG = "CustomLayoutUtils" +private var customLayouts: List? = null + +const val CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED = "${CUSTOM_LAYOUT_PREFIX}functional_keys_symbols_shifted." +const val CUSTOM_FUNCTIONAL_LAYOUT_SYMBOLS = "${CUSTOM_LAYOUT_PREFIX}functional_keys_symbols." +const val CUSTOM_FUNCTIONAL_LAYOUT_NORMAL = "${CUSTOM_LAYOUT_PREFIX}functional_keys." diff --git a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt index a57f1a4e3..ccd79c7cd 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt @@ -245,12 +245,12 @@ private fun loadResourceSubtypes(resources: Resources) { private fun removeInvalidCustomSubtypes(context: Context) { val prefs = DeviceProtectedUtils.getSharedPreferences(context) val additionalSubtypes = Settings.readPrefAdditionalSubtypes(prefs, context.resources).split(";") - val customSubtypeFiles by lazy { getCustomLayoutsDir(context).list() } + val customSubtypeFiles by lazy { getCustomLayoutFiles(context).map { it.name } } val subtypesToRemove = mutableListOf() additionalSubtypes.forEach { val name = it.substringAfter(":").substringBefore(":") if (!name.startsWith(CUSTOM_LAYOUT_PREFIX)) return@forEach - if (customSubtypeFiles?.contains(name) != true) + if (name !in customSubtypeFiles) subtypesToRemove.add(it) } if (subtypesToRemove.isEmpty()) return diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 54b9163d7..f8dcbb9e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -462,6 +462,14 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM Tap to edit raw layout Customize symbols and number layouts + + Customize functional key layouts + + Functional keys + + Functional keys (Symbols) + + Functional keys (More symbols) Symbols diff --git a/app/src/main/res/xml/prefs_screen_advanced.xml b/app/src/main/res/xml/prefs_screen_advanced.xml index 061104f65..80422afbe 100644 --- a/app/src/main/res/xml/prefs_screen_advanced.xml +++ b/app/src/main/res/xml/prefs_screen_advanced.xml @@ -90,6 +90,10 @@ android:key="custom_symbols_number_layouts" android:title="@string/customize_symbols_number_layouts" /> + + diff --git a/layouts.md b/layouts.md index fed2c015e..13dd05dce 100644 --- a/layouts.md +++ b/layouts.md @@ -117,13 +117,15 @@ You can also specify special key codes like `a|!code/key_action_previous`, but i * If a newly added language does not use latin script, please update the default scripts method `Locale.script` in [ScriptUtils](app/src/main/java/helium314/keyboard/latin/utils/ScriptUtils.kt) ## Functional key layouts -This is not yet customizable, but will be soon! -Mostly customizing functional keys works like other layouts, with some specific adjustments: -* you can either have a single layout for functional keys (default), or separate layouts for symbols and shift symbols - * when using a single layout - * emoji and language switch keys will only show in alphabet layout and when the option is enabled - * numpad key will only show in symbols layout - * otherwise the layout will be shown as it is in the layout file +Customizing functional keys mostly works like other layouts, with some specific adjustments: +* When using the default functional layout, emoji, language switch and numpad keys are actually always in the layout, but get removed depending on settings and the main layout (alphabet, symbols or more symbols). This removal is disabled when you customize any functional layout, so to not block you from adding e.g. a numpad key in alphabet layout. +* When you use a language that has a ZWNJ key, the key will automatically be added to the right of the (first) space bar in the bottom row +* Adding popups to keys that switch layout does not work properly, as usually the layout is switched as soon as the key gets pressed. * use keys with `"type": "placeholder"` for * separating left and right functional keys (e.g. shift and delete in default layout) - * separating top and bottom rows in case you want to have functional key rows aligned to the top of the keyboard + * separating top and bottom rows in case you want to have functional key rows aligned to the top of the keyboard (add a row with the placeholder as the only key) +* if the last row in functional keys does not contain a placeholder, it is used as bottom row (like in the default functional layout) +* When you functional keys only for some of alphabet, symbols and more symbols, behavior is as follows + * more symbols will fall back to symbols, then normal + * symbols will fall back to normal, then default (if you only customized more symbols functional layout) + * normal will fall back to default (if you only customized symbols and/or more symbols functional layout) From bd7461f628eae9d9a5f6eae95aa5d8c544e54762 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Wed, 22 May 2024 22:55:52 +0200 Subject: [PATCH 39/58] add / to symbols layout and adjust functional key layouts for symbols layouts i remember this was requested, can't find the issue though anyway, now with fully customizable layouts this is not blocked by "people might be used to this" any more --- app/src/main/assets/layouts/azerty.json | 2 +- app/src/main/assets/layouts/bepo.txt | 2 +- app/src/main/assets/layouts/halmak.txt | 2 +- app/src/main/assets/layouts/kabyle.json | 2 +- app/src/main/assets/layouts/kaitag.txt | 2 +- app/src/main/assets/layouts/symbols.txt | 1 + .../main/assets/layouts/symbols_shifted.txt | 3 +++ app/src/main/assets/layouts/workman.txt | 2 +- .../keyboard_parser/KeyboardParser.kt | 24 ++----------------- .../keyboard_parser/floris/TextKeyData.kt | 3 +++ 10 files changed, 15 insertions(+), 28 deletions(-) diff --git a/app/src/main/assets/layouts/azerty.json b/app/src/main/assets/layouts/azerty.json index 19ae671e7..92b5a95b2 100644 --- a/app/src/main/assets/layouts/azerty.json +++ b/app/src/main/assets/layouts/azerty.json @@ -21,7 +21,7 @@ { "label": "j" }, { "label": "k" }, { "label": "l" }, - { "label": "m", "popup": { "main": { "label": "/" } } } + { "label": "m" } ], [ { "label": "w" }, diff --git a/app/src/main/assets/layouts/bepo.txt b/app/src/main/assets/layouts/bepo.txt index 0aaa74a63..de5b027f5 100644 --- a/app/src/main/assets/layouts/bepo.txt +++ b/app/src/main/assets/layouts/bepo.txt @@ -18,7 +18,7 @@ t s r n -m / +m y x diff --git a/app/src/main/assets/layouts/halmak.txt b/app/src/main/assets/layouts/halmak.txt index 22f0ad773..2b1d7007b 100644 --- a/app/src/main/assets/layouts/halmak.txt +++ b/app/src/main/assets/layouts/halmak.txt @@ -18,7 +18,7 @@ t a e o -i / +i m v diff --git a/app/src/main/assets/layouts/kabyle.json b/app/src/main/assets/layouts/kabyle.json index 948c3dca4..ccaa5a0b2 100644 --- a/app/src/main/assets/layouts/kabyle.json +++ b/app/src/main/assets/layouts/kabyle.json @@ -21,7 +21,7 @@ { "label": "j" }, { "label": "k" }, { "label": "l" }, - { "label": "m", "popup": { "main": { "label": "/" } } } + { "label": "m" } ], [ { "label": "w" }, diff --git a/app/src/main/assets/layouts/kaitag.txt b/app/src/main/assets/layouts/kaitag.txt index 51dcff3ea..a79cd8c59 100644 --- a/app/src/main/assets/layouts/kaitag.txt +++ b/app/src/main/assets/layouts/kaitag.txt @@ -19,7 +19,7 @@ о л д -ж / +ж ъ ~ я diff --git a/app/src/main/assets/layouts/symbols.txt b/app/src/main/assets/layouts/symbols.txt index 40f67d11c..00cfd1159 100644 --- a/app/src/main/assets/layouts/symbols.txt +++ b/app/src/main/assets/layouts/symbols.txt @@ -18,6 +18,7 @@ _ % ‰ + ± ( < { [ ) > } ] +/ * † ‡ ★ " diff --git a/app/src/main/assets/layouts/symbols_shifted.txt b/app/src/main/assets/layouts/symbols_shifted.txt index a3471745e..3693b6b1f 100644 --- a/app/src/main/assets/layouts/symbols_shifted.txt +++ b/app/src/main/assets/layouts/symbols_shifted.txt @@ -26,3 +26,6 @@ $$$4 % ℅ [ ] + +< !fixedColumnOrder!3 ‹ ≤ « +> !fixedColumnOrder!3 › ≥ » diff --git a/app/src/main/assets/layouts/workman.txt b/app/src/main/assets/layouts/workman.txt index ef03de511..be1d1d8e6 100644 --- a/app/src/main/assets/layouts/workman.txt +++ b/app/src/main/assets/layouts/workman.txt @@ -18,7 +18,7 @@ y n e o -i / +i z x diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index c492e8d59..d99647db8 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -218,31 +218,11 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co ) baseKeys.removeLast() } - // add those extra keys depending on layout (remove later) - val spaceIndex = functionalKeysBottom.indexOfFirst { it.label == KeyLabel.SPACE && it.width <= 0 } // 0 or -1 + // add zwnj key next to space if necessary + val spaceIndex = functionalKeysBottom.indexOfFirst { it.label == KeyLabel.SPACE && it.width <= 0 } // width could be 0 or -1 if (spaceIndex >= 0) { if (params.mLocaleKeyboardInfos.hasZwnjKey && params.mId.isAlphabetKeyboard) { - // add zwnj key next to space functionalKeysBottom.add(spaceIndex + 1, TextKeyData(label = KeyLabel.ZWNJ)) - } else if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) { - // add / key next to space, todo (later): not any more, but keep it so this PR can be released without too many people complaining - functionalKeysBottom.add(spaceIndex + 1, TextKeyData(label = "/", type = KeyType.FUNCTION)) - } else if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { - // add < and > keys next to space, todo (later): not any more, but keep it so this PR can be released without too many people complaining - val key1 = TextKeyData( - label = "<", - popup = SimplePopups(listOf("!fixedColumnOrder!3", "‹", "≤", "«")), - labelFlags = Key.LABEL_FLAGS_HAS_POPUP_HINT, - type = KeyType.FUNCTION - ) - val key2 = TextKeyData( - label = ">", - popup = SimplePopups(listOf("!fixedColumnOrder!3", "›", "≥", "»")), - labelFlags = Key.LABEL_FLAGS_HAS_POPUP_HINT, - type = KeyType.FUNCTION - ) - functionalKeysBottom.add(spaceIndex + 1, key2) - functionalKeysBottom.add(spaceIndex, key1) } } baseKeys.add(mutableListOf()) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index fe8fa73d2..39f64a8ec 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -402,6 +402,9 @@ sealed interface KeyData : AbstractKeyData { KeyLabel.SHIFT -> return getShiftBackground(params) } if (type == KeyType.PLACEHOLDER) return Key.BACKGROUND_TYPE_EMPTY + if ((params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS || params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) + && (groupId == GROUP_COMMA || groupId == GROUP_PERIOD)) + return Key.BACKGROUND_TYPE_FUNCTIONAL return Key.BACKGROUND_TYPE_NORMAL } From a9ec7456500533777362b6909970b065761f042b Mon Sep 17 00:00:00 2001 From: Helium314 Date: Wed, 22 May 2024 23:28:44 +0200 Subject: [PATCH 40/58] understand ctrl/alt/fn/meta and toolbar key labels in keyboard layouts --- README.md | 2 -- .../keyboard/internal/KeyboardIconsSet.kt | 3 ++- .../keyboard_parser/floris/KeyLabel.kt | 4 +++ .../keyboard_parser/floris/TextKeyData.kt | 25 ++++++++++++++++--- .../keyboard/latin/utils/ToolbarUtils.kt | 5 +++- layouts.md | 6 ++++- 6 files changed, 37 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1e4fd0f6c..1d8f2cc00 100644 --- a/README.md +++ b/README.md @@ -123,8 +123,6 @@ See [Contribution Guidelines](CONTRIBUTING.md) # To-do __Planned features and improvements:__ -* Customizable functional key layout - * Will likely result in having the same functional key layout for alphabet and symbols layouts * Improve support for modifier keys (_alt_, _ctrl_, _meta_ and _fn_), some ideas: * keep modifier keys on with long press * keep modifier keys on until the next key press diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt index a8a7a2508..9c10953e8 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt @@ -7,6 +7,7 @@ import helium314.keyboard.latin.R import helium314.keyboard.latin.utils.Log import helium314.keyboard.latin.utils.ToolbarKey import helium314.keyboard.latin.utils.getStyleableIconId +import java.util.Locale class KeyboardIconsSet { private val iconsByName = HashMap(styleableIdByName.size) @@ -94,6 +95,6 @@ class KeyboardIconsSet { NAME_START_ONEHANDED_KEY to R.styleable.Keyboard_iconStartOneHandedMode, NAME_STOP_ONEHANDED_KEY to R.styleable.Keyboard_iconStopOneHandedMode, NAME_SWITCH_ONEHANDED_KEY to R.styleable.Keyboard_iconSwitchOneHandedMode, - ).apply { ToolbarKey.entries.forEach { put(it.name, getStyleableIconId(it)) } } + ).apply { ToolbarKey.entries.forEach { put(it.name.lowercase(Locale.US), getStyleableIconId(it)) } } } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt index 01752b662..938f65988 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt @@ -24,6 +24,10 @@ object KeyLabel { const val CURRENCY3 = "$$$3" const val CURRENCY4 = "$$$4" const val CURRENCY5 = "$$$5" + const val CTRL = "ctrl" + const val ALT = "alt" + const val FN = "fn" + const val META = "meta" /** to make sure a FlorisBoard label works when reading a JSON layout */ // resulting special labels should be names of FunctionalKey enum, case insensitive diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index 39f64a8ec..002fa7aa1 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -24,6 +24,10 @@ import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.spellcheck.AndroidSpellCheckerService import helium314.keyboard.latin.utils.InputTypeUtils import helium314.keyboard.latin.utils.Log +import helium314.keyboard.latin.utils.ToolbarKey +import helium314.keyboard.latin.utils.getCodeForToolbarKey +import helium314.keyboard.latin.utils.toolbarKeyStrings +import java.util.Locale // taken from FlorisBoard, small modifications (see also KeyData) // internal keys removed (currently no plan to support them) @@ -396,7 +400,8 @@ sealed interface KeyData : AbstractKeyData { // functional keys when (label) { // or use code? KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL, KeyLabel.ALPHA, KeyLabel.COMMA, KeyLabel.PERIOD, KeyLabel.DELETE, - KeyLabel.EMOJI, KeyLabel.COM, KeyLabel.LANGUAGE_SWITCH, KeyLabel.NUMPAD -> return Key.BACKGROUND_TYPE_FUNCTIONAL + KeyLabel.EMOJI, KeyLabel.COM, KeyLabel.LANGUAGE_SWITCH, KeyLabel.NUMPAD, KeyLabel.CTRL, KeyLabel.ALT, + KeyLabel.FN, KeyLabel.META -> return Key.BACKGROUND_TYPE_FUNCTIONAL KeyLabel.SPACE, KeyLabel.ZWNJ -> return Key.BACKGROUND_TYPE_SPACEBAR KeyLabel.ACTION -> return Key.BACKGROUND_TYPE_ACTION KeyLabel.SHIFT -> return getShiftBackground(params) @@ -445,7 +450,12 @@ sealed interface KeyData : AbstractKeyData { KeyLabel.CURRENCY3 -> params.mLocaleKeyboardInfos.currencyKey.second[2] KeyLabel.CURRENCY4 -> params.mLocaleKeyboardInfos.currencyKey.second[3] KeyLabel.CURRENCY5 -> params.mLocaleKeyboardInfos.currencyKey.second[4] - else -> label + KeyLabel.CTRL, KeyLabel.ALT, KeyLabel.FN, KeyLabel.META -> label.uppercase(Locale.US) + else -> { + if (label in toolbarKeyStrings) { + "!icon/$label|" + } else label + } } private fun processCode(): Int { @@ -454,7 +464,15 @@ sealed interface KeyData : AbstractKeyData { KeyLabel.SYMBOL_ALPHA -> KeyCode.SYMBOL_ALPHA KeyLabel.SYMBOL -> KeyCode.SYMBOL KeyLabel.ALPHA -> KeyCode.ALPHA - else -> code + KeyLabel.CTRL -> KeyCode.CTRL + KeyLabel.ALT -> KeyCode.ALT + KeyLabel.FN -> KeyCode.FN + KeyLabel.META -> KeyCode.META + else -> { + if (label in toolbarKeyStrings) { + getCodeForToolbarKey(ToolbarKey.valueOf(label.uppercase(Locale.US))) + } else code + } } } @@ -476,6 +494,7 @@ sealed interface KeyData : AbstractKeyData { KeyLabel.COM -> Key.LABEL_FLAGS_AUTO_X_SCALE or Key.LABEL_FLAGS_FONT_NORMAL or Key.LABEL_FLAGS_HAS_POPUP_HINT or Key.LABEL_FLAGS_PRESERVE_CASE KeyLabel.ZWNJ -> Key.LABEL_FLAGS_HAS_POPUP_HINT KeyLabel.CURRENCY -> Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO + KeyLabel.CTRL, KeyLabel.ALT, KeyLabel.FN, KeyLabel.META -> Key.LABEL_FLAGS_PRESERVE_CASE else -> 0 } } diff --git a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt index f3d78a804..8aac2046b 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt @@ -14,6 +14,7 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode import helium314.keyboard.latin.R import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.utils.ToolbarKey.* +import java.util.Locale fun createToolbarKey(context: Context, keyboardAttr: TypedArray, key: ToolbarKey): ImageButton { val button = ImageButton(context, null, R.attr.suggestionWordStyle) @@ -56,7 +57,7 @@ fun getCodeForToolbarKey(key: ToolbarKey) = when (key) { FULL_LEFT -> KeyCode.MOVE_START_OF_LINE FULL_RIGHT -> KeyCode.MOVE_END_OF_LINE SELECT_WORD -> KeyCode.CLIPBOARD_SELECT_WORD - CLEAR_CLIPBOARD -> KeyCode.UNSPECIFIED // not managed via code input + CLEAR_CLIPBOARD -> KeyCode.UNSPECIFIED // not managed via code input. todo: probably it should be CLOSE_HISTORY -> KeyCode.ALPHA } @@ -111,6 +112,8 @@ enum class ToolbarKey { FULL_LEFT, FULL_RIGHT, INCOGNITO, AUTOCORRECT, CLEAR_CLIPBOARD, CLOSE_HISTORY } +val toolbarKeyStrings: Set = entries.mapTo(HashSet()) { it.toString().lowercase(Locale.US) } + fun toToolbarKeyString(keys: Collection) = keys.joinToString(";") { it.name } val defaultToolbarPref = entries.filterNot { it == CLEAR_CLIPBOARD || it == CLOSE_HISTORY }.joinToString(";") { diff --git a/layouts.md b/layouts.md index 13dd05dce..19c3e5841 100644 --- a/layouts.md +++ b/layouts.md @@ -89,12 +89,16 @@ Usually the label is what is displayed on the key. However, there are some speci * _comma_: `,` key with special popups, will adapt to language-specific comma, or display `/` in URL fields and `@` in email fields * _space_: space key, with icon when using a number layout * _zwnj_: Zero-width non-joiner (automatically added next to space in alphabet layout for some languages) + * You can also use [toolbar keys](/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt#L109), e.g. _undo_. + * See [KeyLabel.kt](app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt) for more available labels +* In case a label clashes with text you want to add, put a `\` in front of the text you want, e.g. `\space` will write the label `space` instead of adding a space bar. + * Note that you need to escape the `\` in json files by adding a second `\`. * If you want different key label and input text, set the label to [label]|[text], e.g. `aa|bb` will show `aa`, but pressing the key will input `bb`. You can also specify special key codes like `a|!code/key_action_previous`, but it's cleaner to use a json layout and specify the code explicitly. Note that when specifying a code in the label, and a code in a json layout, the code in the label will be ignored. * It's also possible to specify an icon, like `!icon/previous_key|!code/key_action_previous`. * For normal keys, even if you specify a code, you will need to add a `|` to the label, e.g. `!icon/go_key|` or `!icon/go_key|ignored` (to be fixed). * For popups keys, you must _not_ add a `|` (to be fixed). - * You can find available icon names in [KeyboardIconsSet](/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.java). You can also use toolbar key icons using the uppercase name of the toolbar key, e.g. `!icon/REDO` + * You can find available icon names in [KeyboardIconsSet](/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt). You can also use toolbar key icons using the uppercase name of the [toolbar key](/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt#L109), e.g. `!icon/redo` ## Adding new layouts / languages * You need a layout file in one of the formats above, and add it to [layouts](app/src/main/assets/layouts) From c98d6d0cc6a8221028c3599bf35697f9d97a8158 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Wed, 22 May 2024 23:47:51 +0200 Subject: [PATCH 41/58] fix broken ctrl/alt/fn/meta --- app/src/main/java/helium314/keyboard/keyboard/Key.java | 5 ++--- .../java/helium314/keyboard/keyboard/PointerTracker.java | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/Key.java b/app/src/main/java/helium314/keyboard/keyboard/Key.java index 7f765ff4f..c288fefb9 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/Key.java +++ b/app/src/main/java/helium314/keyboard/keyboard/Key.java @@ -513,9 +513,8 @@ public final boolean isShift() { } public final boolean isModifier() { - return mCode == KeyCode.SHIFT || mCode == KeyCode.SYMBOL_ALPHA || mCode == KeyCode.ALPHA || mCode == KeyCode.SYMBOL; - // todo: if this is used, sliding input starts on those keys, but it's not yet implemented -// || mCode == KeyCode.CTRL || mCode == KeyCode.ALT || mCode == KeyCode.FN || mCode == KeyCode.META; + return mCode == KeyCode.SHIFT || mCode == KeyCode.SYMBOL_ALPHA || mCode == KeyCode.ALPHA || mCode == KeyCode.SYMBOL + || mCode == KeyCode.CTRL || mCode == KeyCode.ALT || mCode == KeyCode.FN || mCode == KeyCode.META; } public final boolean isRepeatable() { diff --git a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java index 332825a00..0c6b0da2e 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java @@ -705,7 +705,8 @@ private void onDownEventInternal(final int x, final int y, final long eventTime) private void startKeySelectionByDraggingFinger(final Key key) { if (!mIsInDraggingFinger) { - mIsInSlidingKeyInput = key.isModifier(); + final int code = key.getCode(); // todo: no sliding input yet for those keys, but it would be really useful + mIsInSlidingKeyInput = key.isModifier() && code != KeyCode.CTRL && code != KeyCode.ALT && code != KeyCode.FN && code != KeyCode.META; } mIsInDraggingFinger = true; } From 2c24f190c16eba95396321a67ca5456ecd1cee82 Mon Sep 17 00:00:00 2001 From: codokie <151087174+codokie@users.noreply.github.com> Date: Sun, 26 May 2024 00:12:11 +0300 Subject: [PATCH 42/58] Allow clear clipboard history toolbar key in normal toolbar (#679) Co-authored-by: codokie <@> Co-authored-by: Helium314 --- .../keyboard/keyboard/clipboard/ClipboardHistoryView.kt | 2 -- .../keyboard/internal/keyboard_parser/floris/KeyCode.kt | 4 ++-- .../helium314/keyboard/latin/inputlogic/InputLogic.java | 3 +++ .../java/helium314/keyboard/latin/utils/ToolbarUtils.kt | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt index 6624fb26d..acb7f76ba 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/clipboard/ClipboardHistoryView.kt @@ -240,8 +240,6 @@ class ClipboardHistoryView @JvmOverloads constructor( keyboardActionListener?.onCodeInput(code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false) return } - if (tag == ToolbarKey.CLEAR_CLIPBOARD) - clipboardHistoryManager?.clearHistory() } } diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt index 96f5173e6..0982da41f 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt @@ -146,11 +146,11 @@ object KeyCode { VOICE_INPUT, LANGUAGE_SWITCH, SETTINGS, DELETE, ALPHA, SYMBOL, EMOJI, CLIPBOARD, UNDO, REDO, ARROW_DOWN, ARROW_UP, ARROW_RIGHT, ARROW_LEFT, CLIPBOARD_COPY, CLIPBOARD_SELECT_ALL, CLIPBOARD_SELECT_WORD, TOGGLE_INCOGNITO_MODE, TOGGLE_AUTOCORRECT, MOVE_START_OF_LINE, MOVE_END_OF_LINE, - SHIFT, CAPS_LOCK, MULTIPLE_CODE_POINTS, UNSPECIFIED, CTRL, ALT, FN, META, + SHIFT, CAPS_LOCK, MULTIPLE_CODE_POINTS, UNSPECIFIED, CTRL, ALT, FN, CLIPBOARD_CLEAR_HISTORY, // heliboard only SYMBOL_ALPHA, START_ONE_HANDED_MODE, STOP_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SHIFT_ENTER, - ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, PAGE_UP, PAGE_DOWN + ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, PAGE_UP, PAGE_DOWN, META -> this // conversion diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java index 4efdd4e45..bb237bf4a 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java @@ -730,6 +730,9 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp case KeyCode.CLIPBOARD_COPY_ALL: mConnection.copyText(false); break; + case KeyCode.CLIPBOARD_CLEAR_HISTORY: + mLatinIME.getClipboardHistoryManager().clearHistory(); + break; case KeyCode.CLIPBOARD_CUT: if (mConnection.hasSelection()) { mConnection.copyText(true); diff --git a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt index 8aac2046b..8ab6b7da4 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt @@ -57,7 +57,7 @@ fun getCodeForToolbarKey(key: ToolbarKey) = when (key) { FULL_LEFT -> KeyCode.MOVE_START_OF_LINE FULL_RIGHT -> KeyCode.MOVE_END_OF_LINE SELECT_WORD -> KeyCode.CLIPBOARD_SELECT_WORD - CLEAR_CLIPBOARD -> KeyCode.UNSPECIFIED // not managed via code input. todo: probably it should be + CLEAR_CLIPBOARD -> KeyCode.CLIPBOARD_CLEAR_HISTORY CLOSE_HISTORY -> KeyCode.ALPHA } @@ -116,9 +116,9 @@ val toolbarKeyStrings: Set = entries.mapTo(HashSet()) { it.toString().lo fun toToolbarKeyString(keys: Collection) = keys.joinToString(";") { it.name } -val defaultToolbarPref = entries.filterNot { it == CLEAR_CLIPBOARD || it == CLOSE_HISTORY }.joinToString(";") { +val defaultToolbarPref = entries.filterNot { it == CLOSE_HISTORY }.joinToString(";") { when (it) { - INCOGNITO, AUTOCORRECT, UP, DOWN, ONE_HANDED, FULL_LEFT, FULL_RIGHT, CUT -> "${it.name},false" + INCOGNITO, AUTOCORRECT, UP, DOWN, ONE_HANDED, FULL_LEFT, FULL_RIGHT, CUT, CLEAR_CLIPBOARD -> "${it.name},false" else -> "${it.name},true" } } From 19ff4a5837533763ac453389efc6b3a42e721488 Mon Sep 17 00:00:00 2001 From: Knut Ingvald Dietzel Date: Sat, 25 May 2024 23:13:40 +0200 Subject: [PATCH 43/58] =?UTF-8?q?Add=20primary=20quotation=20marks=20for?= =?UTF-8?q?=20'Norwegian=20bokm=C3=A5l'=20(#806)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/locale_key_texts/nb.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/locale_key_texts/nb.txt b/app/src/main/assets/locale_key_texts/nb.txt index d22acc5d5..55f7af945 100644 --- a/app/src/main/assets/locale_key_texts/nb.txt +++ b/app/src/main/assets/locale_key_texts/nb.txt @@ -4,7 +4,7 @@ e é è ê ë ę ė ē o ø ö ô ò ó õ œ ō u ü û ù ú ū ' ‘ ‚ ’ -" “ „ ” +" ” „ “ « » [extra_keys] 1: å From 7d1627ffc6e66c7ba998831f8acbfdb2a16f7c97 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 26 May 2024 14:17:31 +0200 Subject: [PATCH 44/58] adjust locale confidences in incognito mode --- .../helium314/keyboard/latin/inputlogic/InputLogic.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java index bb237bf4a..808b5aa3b 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java @@ -1574,10 +1574,15 @@ private void performAdditionToUserHistoryDictionary(final SettingsValues setting // That's to avoid unintended additions in some sensitive fields, or fields that // expect to receive non-words. // mInputTypeNoAutoCorrect changed to !isSuggestionsEnabledPerUserSettings because this was cancelling learning way too often - if (!settingsValues.isSuggestionsEnabledPerUserSettings() || settingsValues.mIncognitoModeEnabled || TextUtils.isEmpty(suggestion)) + if (!settingsValues.isSuggestionsEnabledPerUserSettings() || TextUtils.isEmpty(suggestion)) return; final boolean wasAutoCapitalized = mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps(); final String word = stripWordSeparatorsFromEnd(suggestion, settingsValues); + if (settingsValues.mIncognitoModeEnabled) { + // still adjust confidences, otherwise incognito input fields can be very annoying when wrong language is active + mDictionaryFacilitator.adjustConfidences(word, wasAutoCapitalized); + return; + } if (mConnection.hasSlowInputConnection()) { // Since we don't unlearn when the user backspaces on a slow InputConnection, // turn off learning to guard against adding typos that the user later deletes. From 6c2a3e8b5fe4f0b1e2a433ec3069ae152746df21 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 26 May 2024 14:43:37 +0200 Subject: [PATCH 45/58] add tab key --- .../keyboard/internal/keyboard_parser/floris/KeyCode.kt | 3 ++- .../keyboard/internal/keyboard_parser/floris/KeyLabel.kt | 1 + .../keyboard/internal/keyboard_parser/floris/TextKeyData.kt | 2 ++ .../java/helium314/keyboard/latin/inputlogic/InputLogic.java | 3 +++ layouts.md | 2 +- 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt index 0982da41f..20f224c13 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt @@ -138,6 +138,7 @@ object KeyCode { const val PAGE_DOWN = -10011 const val META = -10012 const val META_LOCK = -10013 // to be consistent with the CTRL/ALT(/FN LOCK codes, not sure whether this will be used + const val TAB = -10014 /** to make sure a FlorisBoard code works when reading a JSON layout */ fun Int.checkAndConvertCode(): Int = if (this > 0) this else when (this) { @@ -150,7 +151,7 @@ object KeyCode { // heliboard only SYMBOL_ALPHA, START_ONE_HANDED_MODE, STOP_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SHIFT_ENTER, - ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, PAGE_UP, PAGE_DOWN, META + ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, PAGE_UP, PAGE_DOWN, META, TAB -> this // conversion diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt index 938f65988..8e0905d4d 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt @@ -28,6 +28,7 @@ object KeyLabel { const val ALT = "alt" const val FN = "fn" const val META = "meta" + const val TAB = "tab" /** to make sure a FlorisBoard label works when reading a JSON layout */ // resulting special labels should be names of FunctionalKey enum, case insensitive diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index 002fa7aa1..42372c418 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -451,6 +451,7 @@ sealed interface KeyData : AbstractKeyData { KeyLabel.CURRENCY4 -> params.mLocaleKeyboardInfos.currencyKey.second[3] KeyLabel.CURRENCY5 -> params.mLocaleKeyboardInfos.currencyKey.second[4] KeyLabel.CTRL, KeyLabel.ALT, KeyLabel.FN, KeyLabel.META -> label.uppercase(Locale.US) + KeyLabel.TAB -> "!icon/tab_key|" else -> { if (label in toolbarKeyStrings) { "!icon/$label|" @@ -468,6 +469,7 @@ sealed interface KeyData : AbstractKeyData { KeyLabel.ALT -> KeyCode.ALT KeyLabel.FN -> KeyCode.FN KeyLabel.META -> KeyCode.META + KeyLabel.TAB -> KeyCode.TAB else -> { if (label in toolbarKeyStrings) { getCodeForToolbarKey(ToolbarKey.valueOf(label.uppercase(Locale.US))) diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java index 808b5aa3b..85b0f891b 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java @@ -773,6 +773,9 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp case KeyCode.PAGE_DOWN: sendDownUpKeyEvent(KeyEvent.KEYCODE_PAGE_DOWN); break; + case KeyCode.TAB: + sendDownUpKeyEvent(KeyEvent.KEYCODE_TAB); + break; case KeyCode.VOICE_INPUT: // switching to shortcut IME, shift state, keyboard,... is handled by LatinIME, // {@link KeyboardSwitcher#onEvent(Event)}, or {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}. diff --git a/layouts.md b/layouts.md index 19c3e5841..efeb1768f 100644 --- a/layouts.md +++ b/layouts.md @@ -90,7 +90,7 @@ Usually the label is what is displayed on the key. However, there are some speci * _space_: space key, with icon when using a number layout * _zwnj_: Zero-width non-joiner (automatically added next to space in alphabet layout for some languages) * You can also use [toolbar keys](/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt#L109), e.g. _undo_. - * See [KeyLabel.kt](app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt) for more available labels + * See [KeyLabel.kt](app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyLabel.kt) for more available labels that are parsed to the corresponding key. * In case a label clashes with text you want to add, put a `\` in front of the text you want, e.g. `\space` will write the label `space` instead of adding a space bar. * Note that you need to escape the `\` in json files by adding a second `\`. * If you want different key label and input text, set the label to [label]|[text], e.g. `aa|bb` will show `aa`, but pressing the key will input `bb`. From af3d0535b24d1d9de711ca5bc76c6a53471c9960 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 26 May 2024 19:15:57 +0200 Subject: [PATCH 46/58] fix issue when trying to paste text while cursor is in a word --- .../keyboard/latin/inputlogic/InputLogic.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java index 85b0f891b..e844e12dc 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java @@ -228,8 +228,14 @@ public InputTransaction onTextInput(final SettingsValues settingsValues, final E getActualCapsMode(settingsValues, keyboardShiftMode)); mConnection.beginBatchEdit(); if (mWordComposer.isComposingWord()) { - commitCurrentAutoCorrection(settingsValues, rawText, handler); - addToHistoryIfEmoji(rawText, settingsValues); // add emoji after committing text + if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { + // stop composing, otherwise the text will end up at the end of the current word + mConnection.finishComposingText(); + resetComposingState(false); + } else { + commitCurrentAutoCorrection(settingsValues, rawText, handler); + addToHistoryIfEmoji(rawText, settingsValues); // add emoji after committing text + } } else { addToHistoryIfEmoji(rawText, settingsValues); // add emoji before resetting, otherwise lastComposedWord is empty resetComposingState(true /* alsoResetLastComposedWord */); From fb26eb8e7ae618bea9c40cfaf6ff38f2c18401c7 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 26 May 2024 19:34:55 +0200 Subject: [PATCH 47/58] fix inconsistent behavior when switching text fields actually the previous input attributes were used initially, which caused suggestions to show up or not depending on the previously selected field moving the settings reload should be safe, at least i checked for potential issues --- .../java/helium314/keyboard/latin/LatinIME.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java index 84a41d6ef..82e19e796 100644 --- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java +++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java @@ -947,6 +947,13 @@ void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restart // Note: This call should be done by InputMethodService? updateFullscreenMode(); + // we need to reload the setting before using them, e.g. in startInput or in postResumeSuggestions + // not sure why it was further below, but this introduced inconsistent behavior where wrong input attributes were used + if (isDifferentTextField || + !currentSettingsValues.hasSameOrientation(getResources().getConfiguration())) { + loadSettings(); + currentSettingsValues = mSettings.getCurrent(); + } // ALERT: settings have not been reloaded and there is a chance they may be stale. // In the practice, if it is, we should have gotten onConfigurationChanged so it should // be fine, but this is horribly confusing and must be fixed AS SOON AS POSSIBLE. @@ -990,14 +997,8 @@ void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restart needToCallLoadKeyboardLater = false; } - if (isDifferentTextField || - !currentSettingsValues.hasSameOrientation(getResources().getConfiguration())) { - loadSettings(); - } if (isDifferentTextField) { mainKeyboardView.closing(); - currentSettingsValues = mSettings.getCurrent(); - if (currentSettingsValues.mAutoCorrectEnabled) { suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold); } From b868b583dd4f6d56831ef8ae1685744bba876747 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 26 May 2024 20:25:13 +0200 Subject: [PATCH 48/58] remove old device-dependent behavior not updated in 10 years, only few of those are still in use device-specific default key press volume and vibration duration and a workaround for bad touch firmware on Motorola Xoom --- .../keyboard/keyboard/PointerTracker.java | 32 ---- .../settings/PreferencesSettingsFragment.java | 8 +- .../keyboard/latin/settings/Settings.java | 32 +--- .../latin/settings/SettingsValues.java | 4 +- .../keyboard/latin/utils/ResourceUtils.java | 144 ------------------ .../values/keypress-vibration-durations.xml | 49 ------ app/src/main/res/values/keypress-volumes.xml | 16 -- .../phantom-sudden-move-event-device-list.xml | 13 -- 8 files changed, 10 insertions(+), 288 deletions(-) delete mode 100644 app/src/main/res/values/keypress-vibration-durations.xml delete mode 100644 app/src/main/res/values/keypress-volumes.xml delete mode 100644 app/src/main/res/values/phantom-sudden-move-event-device-list.xml diff --git a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java index 0c6b0da2e..4b35442e2 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PointerTracker.java @@ -37,7 +37,6 @@ import helium314.keyboard.latin.settings.Settings; import helium314.keyboard.latin.settings.SettingsValues; import helium314.keyboard.latin.utils.Log; -import helium314.keyboard.latin.utils.ResourceUtils; import java.util.ArrayList; import java.util.Locale; @@ -84,10 +83,6 @@ public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) { private static final int sPointerStep = (int)TypedValueCompat.dpToPx(10, Resources.getSystem().getDisplayMetrics()); private static GestureStrokeRecognitionParams sGestureStrokeRecognitionParams; private static GestureStrokeDrawingParams sGestureStrokeDrawingParams; - private static boolean sNeedsPhantomSuddenMoveEventHack; - // Move this threshold to resource. - // TODO: Device specific parameter would be better for device specific hack? - private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth private static final ArrayList sTrackers = new ArrayList<>(); private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue(); @@ -102,7 +97,6 @@ public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) { // when new {@link Keyboard} is set by {@link #setKeyDetector(KeyDetector)}. private KeyDetector mKeyDetector = new KeyDetector(); private Keyboard mKeyboard; - private int mPhantomSuddenMoveThreshold; private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector(); private boolean mIsDetectingGesture = false; // per PointerTracker. @@ -166,9 +160,6 @@ public static void init(final TypedArray mainKeyboardViewAttr, final TimerProxy sParams.mSuppressKeyPreviewAfterBatchInputDuration); final Resources res = mainKeyboardViewAttr.getResources(); - sNeedsPhantomSuddenMoveEventHack = Boolean.parseBoolean( - ResourceUtils.getDeviceOverrideValue(res, - R.array.phantom_sudden_move_event_device_list, Boolean.FALSE.toString())); BogusMoveEventDetector.init(res); sTimerProxy = timerProxy; @@ -365,7 +356,6 @@ private void setKeyDetectorInner(final KeyDetector keyDetector) { // Keep {@link #mCurrentKey} that comes from previous keyboard. The key preview of // {@link #mCurrentKey} will be dismissed by {@setReleasedKeyGraphics(Key)} via // {@link onMoveEventInternal(int,int,long)} or {@link #onUpEventInternal(int,int,long)}. - mPhantomSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD); mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight); } @@ -799,20 +789,6 @@ private void processDraggingFingerInToNewKey(final Key newKey, final int x, fina setPressedKeyGraphics(key, eventTime); } - private void processPhantomSuddenMoveHack(final Key key, final int x, final int y, - final long eventTime, final Key oldKey, final int lastX, final int lastY) { - if (DEBUG_MODE) { - Log.w(TAG, String.format(Locale.US, "[%d] onMoveEvent:" - + " phantom sudden move event (distance=%d) is translated to " - + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId, - getDistance(x, y, lastX, lastY), - lastX, lastY, Constants.printableCode(oldKey.getCode()), - x, y, Constants.printableCode(key.getCode()))); - } - onUpEventInternal(x, y, eventTime); - onDownEventInternal(x, y, eventTime); - } - private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y, final long eventTime, final Key oldKey, final int lastX, final int lastY) { if (DEBUG_MODE) { @@ -849,14 +825,6 @@ private void dragFingerFromOldKeyToNewKey(final Key key, final int x, final int if (mIsAllowedDraggingFinger) { processDraggingFingerInToNewKey(key, x, y, eventTime); } - // HACK: On some devices, quick successive touches may be reported as a sudden move by - // touch panel firmware. This hack detects such cases and translates the move event to - // successive up and down events. - // TODO: Should find a way to balance gesture detection and this hack. - else if (sNeedsPhantomSuddenMoveEventHack - && getDistance(x, y, lastX, lastY) >= mPhantomSuddenMoveThreshold) { - processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY); - } // HACK: On some devices, quick successive proximate touches may be reported as a bogus // down-move-up event by touch panel firmware. This hack detects such cases and breaks // these events into separate up and down events. diff --git a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java index 4f98a2959..4ef8aa1c0 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java @@ -151,12 +151,12 @@ public void writeDefaultValue(final String key) { @Override public int readValue(final String key) { - return Settings.readKeypressVibrationDuration(prefs, res); + return Settings.readKeypressVibrationDuration(prefs); } @Override public int readDefaultValue(final String key) { - return Settings.readDefaultKeypressVibrationDuration(res); + return -1; } @Override @@ -206,12 +206,12 @@ public void writeDefaultValue(final String key) { @Override public int readValue(final String key) { - return getPercentageFromValue(Settings.readKeypressSoundVolume(prefs, res)); + return getPercentageFromValue(Settings.readKeypressSoundVolume(prefs)); } @Override public int readDefaultValue(final String key) { - return getPercentageFromValue(Settings.readDefaultKeypressSoundVolume(res)); + return getPercentageFromValue(-1f); } @Override diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index 60812d79e..b9f3c84e9 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -327,20 +327,8 @@ public static void writePrefAdditionalSubtypes(final SharedPreferences prefs, fi prefs.edit().putString(PREF_ADDITIONAL_SUBTYPES, prefSubtypes).apply(); } - public static float readKeypressSoundVolume(final SharedPreferences prefs, final Resources res) { - final float volume = prefs.getFloat( - PREF_KEYPRESS_SOUND_VOLUME, UNDEFINED_PREFERENCE_VALUE_FLOAT); - return (volume != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? volume - : readDefaultKeypressSoundVolume(res); - } - - // Default keypress sound volume for unknown devices. - // The negative value means system default. - private static final String DEFAULT_KEYPRESS_SOUND_VOLUME = Float.toString(-1.0f); - - public static float readDefaultKeypressSoundVolume(final Resources res) { - return Float.parseFloat(ResourceUtils.getDeviceOverrideValue(res, - R.array.keypress_volumes, DEFAULT_KEYPRESS_SOUND_VOLUME)); + public static float readKeypressSoundVolume(final SharedPreferences prefs) { + return prefs.getFloat(PREF_KEYPRESS_SOUND_VOLUME, UNDEFINED_PREFERENCE_VALUE_FLOAT); } public static int readKeyLongpressTimeout(final SharedPreferences prefs, final Resources res) { @@ -354,20 +342,8 @@ public static int readDefaultKeyLongpressTimeout(final Resources res) { return res.getInteger(R.integer.config_default_longpress_key_timeout); } - public static int readKeypressVibrationDuration(final SharedPreferences prefs, final Resources res) { - final int milliseconds = prefs.getInt( - PREF_VIBRATION_DURATION_SETTINGS, UNDEFINED_PREFERENCE_VALUE_INT); - return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds - : readDefaultKeypressVibrationDuration(res); - } - - // Default keypress vibration duration for unknown devices. - // The negative value means system default. - private static final String DEFAULT_KEYPRESS_VIBRATION_DURATION = Integer.toString(-1); - - public static int readDefaultKeypressVibrationDuration(final Resources res) { - return Integer.parseInt(ResourceUtils.getDeviceOverrideValue(res, - R.array.keypress_vibration_durations, DEFAULT_KEYPRESS_VIBRATION_DURATION)); + public static int readKeypressVibrationDuration(final SharedPreferences prefs) { + return prefs.getInt(PREF_VIBRATION_DURATION_SETTINGS, UNDEFINED_PREFERENCE_VALUE_INT); } public static boolean readClipboardHistoryEnabled(final SharedPreferences prefs) { diff --git a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java index 92c523bf9..fbd14a256 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java @@ -185,8 +185,8 @@ public SettingsValues(final Context context, final SharedPreferences prefs, fina // Compute other readable settings mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res); - mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res); - mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res); + mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs); + mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs); mEnableEmojiAltPhysicalKey = prefs.getBoolean(Settings.PREF_ENABLE_EMOJI_ALT_PHYSICAL_KEY, true); mGestureInputEnabled = Settings.readGestureInputEnabled(prefs); mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true); diff --git a/app/src/main/java/helium314/keyboard/latin/utils/ResourceUtils.java b/app/src/main/java/helium314/keyboard/latin/utils/ResourceUtils.java index 564736601..c52ae0e45 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/ResourceUtils.java +++ b/app/src/main/java/helium314/keyboard/latin/utils/ResourceUtils.java @@ -9,8 +9,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; -import android.os.Build; -import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.TypedValue; @@ -19,12 +17,7 @@ import helium314.keyboard.latin.R; import helium314.keyboard.latin.settings.SettingsValues; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.regex.PatternSyntaxException; - public final class ResourceUtils { - private static final String TAG = ResourceUtils.class.getSimpleName(); public static final float UNDEFINED_RATIO = -1.0f; public static final int UNDEFINED_DIMENSION = -1; @@ -33,143 +26,6 @@ private ResourceUtils() { // This utility class is not publicly instantiable. } - private static final HashMap sDeviceOverrideValueMap = new HashMap<>(); - - private static final String[] BUILD_KEYS_AND_VALUES = { - "HARDWARE", Build.HARDWARE, - "MODEL", Build.MODEL, - "BRAND", Build.BRAND, - "MANUFACTURER", Build.MANUFACTURER - }; - private static final HashMap sBuildKeyValues; - private static final String sBuildKeyValuesDebugString; - - static { - sBuildKeyValues = new HashMap<>(); - final ArrayList keyValuePairs = new ArrayList<>(); - final int keyCount = BUILD_KEYS_AND_VALUES.length / 2; - for (int i = 0; i < keyCount; i++) { - final int index = i * 2; - final String key = BUILD_KEYS_AND_VALUES[index]; - final String value = BUILD_KEYS_AND_VALUES[index + 1]; - sBuildKeyValues.put(key, value); - keyValuePairs.add(key + '=' + value); - } - sBuildKeyValuesDebugString = "[" + TextUtils.join(" ", keyValuePairs) + "]"; - } - - public static String getDeviceOverrideValue(final Resources res, final int overrideResId, - final String defaultValue) { - final int orientation = res.getConfiguration().orientation; - final String key = overrideResId + "-" + orientation; - if (sDeviceOverrideValueMap.containsKey(key)) { - return sDeviceOverrideValueMap.get(key); - } - - final String[] overrideArray = res.getStringArray(overrideResId); - final String overrideValue = findConstantForKeyValuePairs(sBuildKeyValues, overrideArray); - // The overrideValue might be an empty string. - if (overrideValue != null) { - Log.i(TAG, "Find override value:" - + " resource="+ res.getResourceEntryName(overrideResId) - + " build=" + sBuildKeyValuesDebugString - + " override=" + overrideValue); - sDeviceOverrideValueMap.put(key, overrideValue); - return overrideValue; - } - - sDeviceOverrideValueMap.put(key, defaultValue); - return defaultValue; - } - - static class DeviceOverridePatternSyntaxError extends Exception { - public DeviceOverridePatternSyntaxError(final String message, final String expression) { - this(message, expression, null); - } - - public DeviceOverridePatternSyntaxError(final String message, final String expression, - final Throwable throwable) { - super(message + ": " + expression, throwable); - } - } - - /** - * Find the condition that fulfills specified key value pairs from an array of - * "condition,constant", and return the corresponding string constant. A condition is - * "pattern1[:pattern2...] (or an empty string for the default). A pattern is - * "key=regexp_value" string. The condition matches only if all patterns of the condition - * are true for the specified key value pairs. - *

    - * For example, "condition,constant" has the following format. - * - HARDWARE=mako,constantForNexus4 - * - MODEL=Nexus 4:MANUFACTURER=LGE,constantForNexus4 - * - ,defaultConstant - * - * @param keyValuePairs attributes to be used to look for a matched condition. - * @param conditionConstantArray an array of "condition,constant" elements to be searched. - * @return the constant part of the matched "condition,constant" element. Returns null if no - * condition matches. - */ - static String findConstantForKeyValuePairs(final HashMap keyValuePairs, - final String[] conditionConstantArray) { - if (conditionConstantArray == null || keyValuePairs == null) { - return null; - } - String foundValue = null; - for (final String conditionConstant : conditionConstantArray) { - final int posComma = conditionConstant.indexOf(','); - if (posComma < 0) { - Log.w(TAG, "Array element has no comma: " + conditionConstant); - continue; - } - final String condition = conditionConstant.substring(0, posComma); - if (condition.isEmpty()) { - Log.w(TAG, "Array element has no condition: " + conditionConstant); - continue; - } - try { - if (fulfillsCondition(keyValuePairs, condition)) { - // Take first match - if (foundValue == null) { - foundValue = conditionConstant.substring(posComma + 1); - } - // And continue walking through all conditions. - } - } catch (final DeviceOverridePatternSyntaxError e) { - Log.w(TAG, "Syntax error, ignored", e); - } - } - return foundValue; - } - - private static boolean fulfillsCondition(final HashMap keyValuePairs, - final String condition) throws DeviceOverridePatternSyntaxError { - final String[] patterns = condition.split(":"); - // Check all patterns in a condition are true - boolean matchedAll = true; - for (final String pattern : patterns) { - final int posEqual = pattern.indexOf('='); - if (posEqual < 0) { - throw new DeviceOverridePatternSyntaxError("Pattern has no '='", condition); - } - final String key = pattern.substring(0, posEqual); - final String value = keyValuePairs.get(key); - if (value == null) { - throw new DeviceOverridePatternSyntaxError("Unknown key", condition); - } - final String patternRegexpValue = pattern.substring(posEqual + 1); - try { - if (!value.matches(patternRegexpValue)) { - matchedAll = false; - // And continue walking through all patterns. - } - } catch (final PatternSyntaxException e) { - throw new DeviceOverridePatternSyntaxError("Syntax error", condition, e); - } - } - return matchedAll; - } - public static int getKeyboardWidth(final Resources res, final SettingsValues settingsValues) { final int defaultKeyboardWidth = getDefaultKeyboardWidth(res); if (settingsValues.mOneHandedModeEnabled) { diff --git a/app/src/main/res/values/keypress-vibration-durations.xml b/app/src/main/res/values/keypress-vibration-durations.xml deleted file mode 100644 index 73b110552..000000000 --- a/app/src/main/res/values/keypress-vibration-durations.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - MODEL=Nexus S:BRAND=google,5 - - MODEL=Galaxy Nexus:BRAND=google,5 - - MODEL=Nexus 4:BRAND=google,8 - - MODEL=Nexus 10:BRAND=google,16 - - MODEL=GT-I(9100[GMPT]?|9108|9210T?):MANUFACTURER=samsung,8 - MODEL=SGH-(I9[27]7R?|I927|T989D?):MANUFACTURER=samsung,8 - MODEL=SHW-M250[KLS]?|SPH-D710|SCH-R760:MANUFACTURER=samsung,8 - MODEL=ISW11SC|SC-02C:MANUFACTURER=samsung,8 - - MODEL=(SAMSUNG-)?GT-I(930[05][NT]?|9308):MANUFACTURER=samsung,8 - MODEL=(SAMSUNG-)?SGH-(T999[V]?|I747[M]?|N064|N035):MANUFACTURER=samsung,8 - MODEL=(SAMSUNG-)?SCH-(J021|R530|I535|I939):MANUFACTURER=samsung,8 - MODEL=(SAMSUNG-)?(SCL21|SC-06D|SC-03E):MANUFACTURER=samsung,8 - MODEL=(SAMSUNG-)?(SHV-210[KLS]?|SPH-L710):MANUFACTURER=samsung,8 - - MODEL=(SAMSUNG-)?GT-I(950[0258][G]?):MANUFACTURER=samsung,7 - MODEL=(SAMSUNG-)?SGH-(I337|M919|N045):MANUFACTURER=samsung,7 - MODEL=(SAMSUNG-)?SCH-(I545|I959|R970):MANUFACTURER=samsung,7 - MODEL=(SAMSUNG-)?SPH-(L720):MANUFACTURER=samsung,7 - MODEL=(SAMSUNG-)?(SC-04E):MANUFACTURER=samsung,7 - MODEL=(SAMSUNG-)?(SHV-E300[KLS]?):MANUFACTURER=samsung,7 - - MODEL=LG-E97[013]|LS970|L-01E:MANUFACTURER=LGE,15 - - MODEL=HTC One X:MANUFACTURER=HTC,20 - - MODEL=HTC One:MANUFACTURER=HTC,15 - MODEL=HTL22:MANUFACTURER=HTC,15 - - MODEL=XT907:MANUFACTURER=motorola,30 - - MODEL=XT1035:MANUFACTURER=motorola,18 - - MODEL=C6603|C6806:MANUFACTURER=Sony,35 - - diff --git a/app/src/main/res/values/keypress-volumes.xml b/app/src/main/res/values/keypress-volumes.xml deleted file mode 100644 index be8d7a165..000000000 --- a/app/src/main/res/values/keypress-volumes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - HARDWARE=herring,0.5f - HARDWARE=tuna,0.5f - HARDWARE=stingray,0.4f - HARDWARE=grouper,0.3f - HARDWARE=mako,0.3f - HARDWARE=manta,0.2f - - diff --git a/app/src/main/res/values/phantom-sudden-move-event-device-list.xml b/app/src/main/res/values/phantom-sudden-move-event-device-list.xml deleted file mode 100644 index a497a520c..000000000 --- a/app/src/main/res/values/phantom-sudden-move-event-device-list.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - HARDWARE=stingray,true - - From 7473d5b32bb677be3bff04aa373803220ccc540b Mon Sep 17 00:00:00 2001 From: Helium314 Date: Mon, 27 May 2024 18:36:35 +0200 Subject: [PATCH 49/58] fix unwanted popups on language extra keys regression from recent parser updates --- .../internal/keyboard_parser/RawKeyboardParser.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt index 1dbb2310a..37b532b88 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt @@ -98,10 +98,12 @@ object RawKeyboardParser { return { params -> simpleKeyData.mapIndexedTo(mutableListOf()) { i, row -> val newRow = row.toMutableList() - val extraKeys = params.mId.mSubtype.keyboardLayoutSetName.endsWith("+") && params.mId.isAlphabetKeyboard - if (extraKeys) + if (params.mId.isAlphabetKeyboard + && params.mId.mSubtype.keyboardLayoutSetName.endsWith("+") + && "$layoutName+" == params.mId.mSubtype.keyboardLayoutSetName + ) { params.mLocaleKeyboardInfos.getExtraKeys(i+1)?.let { newRow.addAll(it) } - println("${newRow.size}: ${newRow.map { it.label }}") + } newRow } } From 0f503389b34b30a19cc3259c4f3086e4b1a408a2 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Mon, 27 May 2024 20:21:59 +0200 Subject: [PATCH 50/58] minor cleanup and add comment --- .../keyboard/keyboard/internal/KeyPreviewDrawParams.java | 1 - .../keyboard/internal/keyboard_parser/KeyboardParser.kt | 1 - .../keyboard/internal/keyboard_parser/floris/TextKeyData.kt | 3 ++- .../main/kotlin/com/majeur/inputmethod/tools/emoji/JarUtils.kt | 3 +-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyPreviewDrawParams.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyPreviewDrawParams.java index 013131a23..ab3f4c8bc 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyPreviewDrawParams.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyPreviewDrawParams.java @@ -10,7 +10,6 @@ import android.view.View; import helium314.keyboard.latin.R; -import helium314.keyboard.latin.settings.Settings; public final class KeyPreviewDrawParams { // XML attributes of {@link MainKeyboardView}. diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index d99647db8..db8610c76 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -11,7 +11,6 @@ import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyLabel import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyType -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.SimplePopups import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData import helium314.keyboard.latin.common.isEmoji import helium314.keyboard.latin.define.DebugFlags diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index 42372c418..7fab7d62f 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -108,10 +108,11 @@ sealed interface KeyData : AbstractKeyData { } private fun getSpaceLabel(params: KeyboardParams): String = - if (params.mId.mElementId <= KeyboardId.ELEMENT_SYMBOLS_SHIFTED) + if (params.mId.isAlphaOrSymbolKeyboard) "!icon/space_key|!code/key_space" else "!icon/space_key_for_number_layout|!code/key_space" + // todo: emoji and language switch popups should actually disappear depending on current layout (including functional keys) private fun getCommaPopupKeys(params: KeyboardParams): List { val keys = mutableListOf() if (!params.mId.mDeviceLocked) diff --git a/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/JarUtils.kt b/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/JarUtils.kt index 57f4822c6..9203b1407 100644 --- a/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/JarUtils.kt +++ b/tools/make-emoji-keys/src/main/kotlin/com/majeur/inputmethod/tools/emoji/JarUtils.kt @@ -11,7 +11,6 @@ import java.io.IOException import java.io.InputStream import java.io.UnsupportedEncodingException import java.net.URLDecoder -import java.util.HashMap import java.util.jar.JarFile import kotlin.RuntimeException @@ -73,7 +72,7 @@ object JarUtils { fun close(stream: Closeable?) { try { stream?.close() - } catch (e: IOException) { + } catch (_: IOException) { } } From 388366c2420fee57b29c4f4d13601e0278b02e78 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Mon, 27 May 2024 20:34:26 +0200 Subject: [PATCH 51/58] make long-press toolbar pinning optional --- README.md | 6 +-- app/build.gradle | 2 +- .../main/java/helium314/keyboard/latin/App.kt | 19 ++++++++- .../settings/PreferencesSettingsFragment.java | 10 ++++- .../keyboard/latin/settings/Settings.java | 26 +----------- .../latin/settings/SettingsValues.java | 2 + .../suggestions/SuggestionStripView.java | 10 ++--- .../keyboard/latin/utils/ToolbarUtils.kt | 30 +++++++++++++- app/src/main/res/values/strings.xml | 40 +++++++++++-------- .../main/res/xml/prefs_screen_preferences.xml | 11 +++++ 10 files changed, 98 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 1d8f2cc00..391e590b5 100644 --- a/README.md +++ b/README.md @@ -66,13 +66,10 @@ Does not use internet permission, and thus is 100% offline. The app checks the SHA256 checksum of the library and warns the user if it doesn't match with known library versions. A mismatch indicates the library was modified, but may also occur if the user intentionally provides a different library than expected (e.g. a self-built variant). Note that if the the app is installed as a system app, both versions have access to the system glide typing library (if it is installed). * __App crashing when using as system app__: This happens if you do not install the app, but just copy the APK. Then the app's own library is not extracted from the APK, and not accessible to the app. You will need tp either install the app over itself, or provide a library. -* (_to be expanded_...) ## Hidden Functionality Features that may go unnoticed, and further potentially useful information -* Long-pressing pinned toolbar keys results in additional functionality - * clipboard -> paste, move left/right -> move full left/right, move up/down -> page up/down, copy -> copy all, select word -> select all, undo <-> redo, -* Long-pressing keys in the suggestion strip toolbar pins them to the suggestion strip. +* Long-pressing toolbar keys results in additional functionality: clipboard -> paste, move left/right -> move full left/right, move up/down -> page up/down, copy -> copy all, select word -> select all, undo <-> redo * Long-press the Comma-key to access Clipboard View, Emoji View, One-handed Mode, Settings, or Switch Language: * Emoji View and Language Switch will disappear if you have the corresponding key enabled; * For some layouts it\'s not the Comma-key, but the key at the same position (e.g. it\'s `q` for Dvorak layout). @@ -139,7 +136,6 @@ __Planned features and improvements:__ * Make use of the `.com` key in URL fields (currently only available for tablets) * With language-dependent TLDs * Internal cleanup (a lot of over-complicated and convoluted code) -* optionally move toolbar key pinning to a setting, so long press actions on unpinned toolbar keys are available * [Bug fixes](https://github.com/Helium314/HeliBoard/issues?q=is%3Aissue+is%3Aopen+label%3Abug) __What will _not_ be added:__ diff --git a/app/build.gradle b/app/build.gradle index 2830cacbb..c270024d9 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,7 +10,7 @@ android { applicationId "helium314.keyboard" minSdkVersion 21 targetSdkVersion 34 - versionCode 2000 + versionCode 2001 versionName '2.0-alpha1' ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' diff --git a/app/src/main/java/helium314/keyboard/latin/App.kt b/app/src/main/java/helium314/keyboard/latin/App.kt index 7932ed2c0..cba11f306 100644 --- a/app/src/main/java/helium314/keyboard/latin/App.kt +++ b/app/src/main/java/helium314/keyboard/latin/App.kt @@ -11,6 +11,8 @@ import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.DictionaryInfoUtils +import helium314.keyboard.latin.utils.ToolbarKey +import helium314.keyboard.latin.utils.defaultPinnedToolbarPref import helium314.keyboard.latin.utils.getCustomLayoutFile import helium314.keyboard.latin.utils.onCustomLayoutFileListChanged import helium314.keyboard.latin.utils.upgradeToolbarPrefs @@ -39,7 +41,6 @@ fun checkVersionUpgrade(context: Context) { val oldVersion = prefs.getInt(Settings.PREF_VERSION_CODE, 0) if (oldVersion == BuildConfig.VERSION_CODE) return - upgradeToolbarPrefs(prefs) // clear extracted dictionaries, in case updated version contains newer ones DictionaryInfoUtils.getCachedDirectoryList(context)?.forEach { if (!it.isDirectory) return@forEach @@ -73,6 +74,22 @@ fun checkVersionUpgrade(context: Context) { putString(Settings.PREF_SELECTED_SUBTYPE, selectedSubtype) } } + if (oldVersion <= 2000) { + // upgrade pinned toolbar keys pref + val oldPinnedKeysPref = prefs.getString(Settings.PREF_PINNED_TOOLBAR_KEYS, "")!! + val pinnedKeys = oldPinnedKeysPref.split(";").mapNotNull { + try { + ToolbarKey.valueOf(it) + } catch (_: IllegalArgumentException) { + null + } + } + val newPinnedKeysPref = (pinnedKeys.map { "${it.name},true" } + defaultPinnedToolbarPref.split(";")) + .distinctBy { it.split(",").first() } + .joinToString(";") + prefs.edit { putString(Settings.PREF_PINNED_TOOLBAR_KEYS, newPinnedKeysPref) } + } + upgradeToolbarPrefs(prefs) onCustomLayoutFileListChanged() // just to be sure prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) } } diff --git a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java index 4ef8aa1c0..9379113f2 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java @@ -71,9 +71,14 @@ public void onCreate(final Bundle icicle) { R.string.toolbar_keys, (name) -> ToolbarUtilsKt.getToolbarIconByName(name, requireContext())); return true; }); + findPreference(Settings.PREF_PINNED_TOOLBAR_KEYS).setOnPreferenceClickListener((pref) -> { + DialogUtilsKt.reorderDialog(requireContext(), Settings.PREF_PINNED_TOOLBAR_KEYS, ToolbarUtilsKt.getDefaultPinnedToolbarPref(), + R.string.pinned_toolbar_keys, (name) -> ToolbarUtilsKt.getToolbarIconByName(name, requireContext())); + return true; + }); findPreference(Settings.PREF_CLIPBOARD_TOOLBAR_KEYS).setOnPreferenceClickListener((pref) -> { DialogUtilsKt.reorderDialog(requireContext(), Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, ToolbarUtilsKt.getDefaultClipboardToolbarPref(), - R.string.toolbar_keys, (name) -> ToolbarUtilsKt.getToolbarIconByName(name, requireContext())); + R.string.clipboard_toolbar_keys, (name) -> ToolbarUtilsKt.getToolbarIconByName(name, requireContext())); return true; }); } @@ -89,7 +94,8 @@ public void onSharedPreferenceChanged(final SharedPreferences prefs, final Strin if (key == null) return; switch (key) { case Settings.PREF_POPUP_KEYS_ORDER, Settings.PREF_SHOW_POPUP_HINTS, Settings.PREF_SHOW_NUMBER_ROW, - Settings.PREF_POPUP_KEYS_LABELS_ORDER, Settings.PREF_TOOLBAR_KEYS, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS + Settings.PREF_POPUP_KEYS_LABELS_ORDER, Settings.PREF_TOOLBAR_KEYS, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, + Settings.PREF_PINNED_TOOLBAR_KEYS, Settings.PREF_QUICK_PIN_TOOLBAR_KEYS -> mReloadKeyboard = true; case Settings.PREF_LOCALIZED_NUMBER_ROW -> KeyboardLayoutSet.onSystemLocaleChanged(); case Settings.PREF_SHOW_HINTS diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index b9f3c84e9..9940127d7 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -44,8 +44,6 @@ import helium314.keyboard.latin.utils.RunInLocaleKt; import helium314.keyboard.latin.utils.StatsUtils; import helium314.keyboard.latin.utils.SubtypeSettingsKt; -import helium314.keyboard.latin.utils.ToolbarKey; -import helium314.keyboard.latin.utils.ToolbarUtilsKt; import java.io.File; import java.util.ArrayList; @@ -148,6 +146,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_USE_SYSTEM_LOCALES = "use_system_locales"; public static final String PREF_URL_DETECTION = "url_detection"; public static final String PREF_DONT_SHOW_MISSING_DICTIONARY_DIALOG = "dont_show_missing_dict_dialog"; + public static final String PREF_QUICK_PIN_TOOLBAR_KEYS = "quick_pin_toolbar_keys"; public static final String PREF_PINNED_TOOLBAR_KEYS = "pinned_toolbar_keys"; public static final String PREF_TOOLBAR_KEYS = "toolbar_keys"; public static final String PREF_CLIPBOARD_TOOLBAR_KEYS = "clipboard_toolbar_keys"; @@ -486,29 +485,6 @@ public static void writePinnedClipString(final Context context, final String cli } } - public static ArrayList readPinnedKeys(final SharedPreferences prefs) { - final ArrayList list = new ArrayList<>(); - for (final String key : prefs.getString(Settings.PREF_PINNED_TOOLBAR_KEYS, "").split(";")) { - try { - list.add(ToolbarKey.valueOf(key)); - } catch (IllegalArgumentException ignored) { } // may happen if toolbar key is removed from app - } - return list; - } - - public static void addPinnedKey(final SharedPreferences prefs, final ToolbarKey key) { - final ArrayList keys = readPinnedKeys(prefs); - if (keys.contains(key)) return; - keys.add(key); - prefs.edit().putString(Settings.PREF_PINNED_TOOLBAR_KEYS, ToolbarUtilsKt.toToolbarKeyString(keys)).apply(); - } - - public static void removePinnedKey(final SharedPreferences prefs, final ToolbarKey key) { - final ArrayList keys = readPinnedKeys(prefs); - keys.remove(key); - prefs.edit().putString(Settings.PREF_PINNED_TOOLBAR_KEYS, ToolbarUtilsKt.toToolbarKeyString(keys)).apply(); - } - public static int readMorePopupKeysPref(final SharedPreferences prefs) { return switch (prefs.getString(Settings.PREF_MORE_POPUP_KEYS, "normal")) { case "all" -> LocaleKeyboardInfosKt.POPUP_KEYS_ALL; diff --git a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java index fbd14a256..d00c5b601 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java @@ -99,6 +99,7 @@ public class SettingsValues { public final boolean mEnableEmojiAltPhysicalKey; public final boolean mIsSplitKeyboardEnabled; public final float mSplitKeyboardSpacerRelativeWidth; + public final boolean mQuickPinToolbarKeys; public final int mScreenMetrics; public final boolean mAddToPersonalDictionary; public final boolean mUseContactsDictionary; @@ -181,6 +182,7 @@ public SettingsValues(final Context context, final SharedPreferences prefs, fina mSplitKeyboardSpacerRelativeWidth = 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; + mQuickPinToolbarKeys = prefs.getBoolean(Settings.PREF_QUICK_PIN_TOOLBAR_KEYS, false); mScreenMetrics = Settings.readScreenMetrics(res); // Compute other readable settings diff --git a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java index aea803001..af6699cba 100644 --- a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java +++ b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java @@ -217,13 +217,13 @@ public SuggestionStripView(final Context context, final AttributeSet attrs, fina mToolbarExpandKey.getLayoutParams().height *= 0.82; // shrink the whole key a little (drawable not affected) mToolbarExpandKey.getLayoutParams().width *= 0.82; - for (final ToolbarKey pinnedKey : Settings.readPinnedKeys(prefs)) { + for (final ToolbarKey pinnedKey : ToolbarUtilsKt.getPinnedToolbarKeys(prefs)) { final ImageButton button = createToolbarKey(context, keyboardAttr, pinnedKey); button.setLayoutParams(toolbarKeyLayoutParams); setupKey(button, colors); mPinnedKeys.addView(button); final View pinnedKeyInToolbar = mToolbar.findViewWithTag(pinnedKey); - if (pinnedKeyInToolbar != null) + if (pinnedKeyInToolbar != null && Settings.getInstance().getCurrent().mQuickPinToolbarKeys) pinnedKeyInToolbar.setBackground(mEnabledToolKeyBackground); } @@ -374,7 +374,7 @@ public boolean onLongClick(final View view) { private void onLongClickToolKey(final View view) { if (!(view.getTag() instanceof ToolbarKey tag)) return; - if (view.getParent() == mPinnedKeys) { + if (view.getParent() == mPinnedKeys || !Settings.getInstance().getCurrent().mQuickPinToolbarKeys) { final int longClickCode = getCodeForToolbarKeyLongClick(tag); if (longClickCode != KeyCode.UNSPECIFIED) { mListener.onCodeInput(longClickCode, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, false); @@ -384,9 +384,9 @@ private void onLongClickToolKey(final View view) { if (pinnedKeyView == null) { addKeyToPinnedKeys(tag); mToolbar.findViewWithTag(tag).setBackground(mEnabledToolKeyBackground); - Settings.addPinnedKey(DeviceProtectedUtils.getSharedPreferences(getContext()), tag); + ToolbarUtilsKt.addPinnedKey(DeviceProtectedUtils.getSharedPreferences(getContext()), tag); } else { - Settings.removePinnedKey(DeviceProtectedUtils.getSharedPreferences(getContext()), tag); + ToolbarUtilsKt.removePinnedKey(DeviceProtectedUtils.getSharedPreferences(getContext()), tag); mToolbar.findViewWithTag(tag).setBackground(mDefaultBackground.getConstantState().newDrawable(getResources())); mPinnedKeys.removeView(pinnedKeyView); } diff --git a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt index 8ab6b7da4..8c7ba0bec 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt @@ -114,8 +114,6 @@ enum class ToolbarKey { val toolbarKeyStrings: Set = entries.mapTo(HashSet()) { it.toString().lowercase(Locale.US) } -fun toToolbarKeyString(keys: Collection) = keys.joinToString(";") { it.name } - val defaultToolbarPref = entries.filterNot { it == CLOSE_HISTORY }.joinToString(";") { when (it) { INCOGNITO, AUTOCORRECT, UP, DOWN, ONE_HANDED, FULL_LEFT, FULL_RIGHT, CUT, CLEAR_CLIPBOARD -> "${it.name},false" @@ -123,6 +121,10 @@ val defaultToolbarPref = entries.filterNot { it == CLOSE_HISTORY }.joinToString( } } +val defaultPinnedToolbarPref = entries.filterNot { it == CLOSE_HISTORY }.joinToString(";") { + "${it.name},false" +} + val defaultClipboardToolbarPref by lazy { val default = listOf(ONE_HANDED, UNDO, UP, DOWN, LEFT, RIGHT, CLEAR_CLIPBOARD, COPY, CUT, SELECT_WORD, CLOSE_HISTORY) val others = entries.filterNot { it in default } @@ -132,6 +134,7 @@ val defaultClipboardToolbarPref by lazy { /** add missing keys, typically because a new key has been added */ fun upgradeToolbarPrefs(prefs: SharedPreferences) { upgradeToolbarPref(prefs, Settings.PREF_TOOLBAR_KEYS, defaultToolbarPref) + upgradeToolbarPref(prefs, Settings.PREF_PINNED_TOOLBAR_KEYS, defaultPinnedToolbarPref) upgradeToolbarPref(prefs, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, defaultClipboardToolbarPref) } @@ -159,8 +162,31 @@ private fun upgradeToolbarPref(prefs: SharedPreferences, pref: String, default: fun getEnabledToolbarKeys(prefs: SharedPreferences) = getEnabledToolbarKeys(prefs, Settings.PREF_TOOLBAR_KEYS, defaultToolbarPref) +fun getPinnedToolbarKeys(prefs: SharedPreferences) = getEnabledToolbarKeys(prefs, Settings.PREF_PINNED_TOOLBAR_KEYS, defaultPinnedToolbarPref) + fun getEnabledClipboardToolbarKeys(prefs: SharedPreferences) = getEnabledToolbarKeys(prefs, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, defaultClipboardToolbarPref) +fun addPinnedKey(prefs: SharedPreferences, key: ToolbarKey) { + // remove the existing version of this key and add the enabled one after the last currently enabled key + val string = prefs.getString(Settings.PREF_PINNED_TOOLBAR_KEYS, defaultPinnedToolbarPref)!! + val keys = string.split(";").toMutableList() + keys.removeAll { it.startsWith(key.name + ",") } + val lastEnabledIndex = keys.indexOfLast { it.endsWith("true") } + keys.add(lastEnabledIndex + 1, key.name + ",true") + prefs.edit { putString(Settings.PREF_PINNED_TOOLBAR_KEYS, keys.joinToString(";")) } +} + +fun removePinnedKey(prefs: SharedPreferences, key: ToolbarKey) { + // just set it to disabled + val string = prefs.getString(Settings.PREF_PINNED_TOOLBAR_KEYS, defaultPinnedToolbarPref)!! + val result = string.split(";").joinToString(";") { + if (it.startsWith(key.name + ",")) + key.name + ",false" + else it + } + prefs.edit { putString(Settings.PREF_PINNED_TOOLBAR_KEYS, result) } +} + private fun getEnabledToolbarKeys(prefs: SharedPreferences, pref: String, default: String): List { val string = prefs.getString(pref, default)!! return string.split(";").mapNotNull { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f8dcbb9e5..51132e979 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -207,21 +207,21 @@ More keys Languages & Layouts - + Number row Always show number row - + Localize number row Prefer localized over latin numbers - + Show key hints Show long-press hints - + Select hint source - + Select popup key order Number row @@ -229,7 +229,7 @@ Language (priority) Layout Symbols - + Select toolbar keys @android:string/copy @@ -250,9 +250,15 @@ Undo Redo Close clipboard history - + Select clipboard toolbar keys - + + Select pinned toolbar keys + + Pin toolbar key on long press + + This will disable other long press actions for toolbar keys that are not pinned + Show functional hints Show hints if long-pressing a key triggers additional functionality @@ -264,9 +270,9 @@ Long press symbols key for numpad Narrow key gaps - + Keyboard height scale - + Bottom padding scale @@ -494,13 +500,13 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM Day Night - + Keypress vibration duration - + Keypress sound volume - + Key long press delay - + Emoji for physical keyboard Physical Alt key shows the emoji palette @@ -810,15 +816,15 @@ New dictionary: Pause Wait - + Horizontal spacebar swipe gesture - + Vertical spacebar swipe gesture None Move Cursor - + Variable toolbar direction Reverse direction when a right-to-left keyboard subtype is selected diff --git a/app/src/main/res/xml/prefs_screen_preferences.xml b/app/src/main/res/xml/prefs_screen_preferences.xml index 7db5936d7..8bd1fc582 100644 --- a/app/src/main/res/xml/prefs_screen_preferences.xml +++ b/app/src/main/res/xml/prefs_screen_preferences.xml @@ -99,10 +99,21 @@ android:key="toolbar_keys" android:title="@string/toolbar_keys" /> + + + + Date: Tue, 28 May 2024 22:02:31 +0200 Subject: [PATCH 52/58] move toolbar preferences to a separate fragment with 2 more settings coming soon this is probably justified --- .../settings/PreferencesSettingsFragment.java | 21 +------ .../latin/settings/ToolbarSettingsFragment.kt | 61 +++++++++++++++++++ .../main/res/drawable/ic_settings_toolbar.xml | 4 ++ .../ic_settings_toolbar_foreground.xml | 13 ++++ app/src/main/res/values/strings.xml | 6 +- app/src/main/res/xml/prefs.xml | 6 ++ .../main/res/xml/prefs_screen_preferences.xml | 26 -------- app/src/main/res/xml/prefs_screen_toolbar.xml | 38 ++++++++++++ 8 files changed, 127 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/helium314/keyboard/latin/settings/ToolbarSettingsFragment.kt create mode 100644 app/src/main/res/drawable/ic_settings_toolbar.xml create mode 100644 app/src/main/res/drawable/ic_settings_toolbar_foreground.xml create mode 100644 app/src/main/res/xml/prefs_screen_toolbar.xml diff --git a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java index 9379113f2..ea2269cee 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/PreferencesSettingsFragment.java @@ -24,7 +24,6 @@ import helium314.keyboard.latin.utils.PopupKeysUtilsKt; import helium314.keyboard.latin.utils.SubtypeSettingsKt; import helium314.keyboard.latin.utils.SubtypeUtilsKt; -import helium314.keyboard.latin.utils.ToolbarUtilsKt; import kotlin.collections.ArraysKt; @@ -37,7 +36,6 @@ public void onCreate(final Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.prefs_screen_preferences); - final Resources res = getResources(); final Context context = getActivity(); // When we are called from the Settings application but we are not already running, some @@ -66,21 +64,6 @@ public void onCreate(final Bundle icicle) { PopupKeysUtilsKt.POPUP_KEYS_LABEL_DEFAULT, R.string.hint_source, (x) -> null); return true; }); - findPreference(Settings.PREF_TOOLBAR_KEYS).setOnPreferenceClickListener((pref) -> { - DialogUtilsKt.reorderDialog(requireContext(), Settings.PREF_TOOLBAR_KEYS, ToolbarUtilsKt.getDefaultToolbarPref(), - R.string.toolbar_keys, (name) -> ToolbarUtilsKt.getToolbarIconByName(name, requireContext())); - return true; - }); - findPreference(Settings.PREF_PINNED_TOOLBAR_KEYS).setOnPreferenceClickListener((pref) -> { - DialogUtilsKt.reorderDialog(requireContext(), Settings.PREF_PINNED_TOOLBAR_KEYS, ToolbarUtilsKt.getDefaultPinnedToolbarPref(), - R.string.pinned_toolbar_keys, (name) -> ToolbarUtilsKt.getToolbarIconByName(name, requireContext())); - return true; - }); - findPreference(Settings.PREF_CLIPBOARD_TOOLBAR_KEYS).setOnPreferenceClickListener((pref) -> { - DialogUtilsKt.reorderDialog(requireContext(), Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, ToolbarUtilsKt.getDefaultClipboardToolbarPref(), - R.string.clipboard_toolbar_keys, (name) -> ToolbarUtilsKt.getToolbarIconByName(name, requireContext())); - return true; - }); } @Override @@ -94,9 +77,7 @@ public void onSharedPreferenceChanged(final SharedPreferences prefs, final Strin if (key == null) return; switch (key) { case Settings.PREF_POPUP_KEYS_ORDER, Settings.PREF_SHOW_POPUP_HINTS, Settings.PREF_SHOW_NUMBER_ROW, - Settings.PREF_POPUP_KEYS_LABELS_ORDER, Settings.PREF_TOOLBAR_KEYS, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, - Settings.PREF_PINNED_TOOLBAR_KEYS, Settings.PREF_QUICK_PIN_TOOLBAR_KEYS - -> mReloadKeyboard = true; + Settings.PREF_POPUP_KEYS_LABELS_ORDER -> mReloadKeyboard = true; case Settings.PREF_LOCALIZED_NUMBER_ROW -> KeyboardLayoutSet.onSystemLocaleChanged(); case Settings.PREF_SHOW_HINTS -> findPreference(Settings.PREF_POPUP_KEYS_LABELS_ORDER).setVisible(prefs.getBoolean(Settings.PREF_SHOW_HINTS, false)); diff --git a/app/src/main/java/helium314/keyboard/latin/settings/ToolbarSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/ToolbarSettingsFragment.kt new file mode 100644 index 000000000..b2f1b4ce3 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/latin/settings/ToolbarSettingsFragment.kt @@ -0,0 +1,61 @@ +package helium314.keyboard.latin.settings + +import android.content.SharedPreferences +import android.os.Bundle +import androidx.preference.Preference +import helium314.keyboard.keyboard.KeyboardSwitcher +import helium314.keyboard.latin.R +import helium314.keyboard.latin.utils.defaultClipboardToolbarPref +import helium314.keyboard.latin.utils.defaultPinnedToolbarPref +import helium314.keyboard.latin.utils.defaultToolbarPref +import helium314.keyboard.latin.utils.getToolbarIconByName +import helium314.keyboard.latin.utils.reorderDialog + +class ToolbarSettingsFragment : SubScreenFragment() { + private var reloadKeyboard = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + addPreferencesFromResource(R.xml.prefs_screen_toolbar) + + findPreference(Settings.PREF_TOOLBAR_KEYS)?.onPreferenceClickListener = + Preference.OnPreferenceClickListener { + reorderDialog( + requireContext(), Settings.PREF_TOOLBAR_KEYS, defaultToolbarPref, + R.string.toolbar_keys + ) { getToolbarIconByName(it, requireContext()) } + true + } + findPreference(Settings.PREF_PINNED_TOOLBAR_KEYS)?.onPreferenceClickListener = + Preference.OnPreferenceClickListener { + reorderDialog( + requireContext(), Settings.PREF_PINNED_TOOLBAR_KEYS, defaultPinnedToolbarPref, + R.string.pinned_toolbar_keys + ) { getToolbarIconByName(it, requireContext()) } + true + } + findPreference(Settings.PREF_CLIPBOARD_TOOLBAR_KEYS)?.onPreferenceClickListener = + Preference.OnPreferenceClickListener { + reorderDialog( + requireContext(), Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, defaultClipboardToolbarPref, + R.string.clipboard_toolbar_keys + ) { getToolbarIconByName(it, requireContext()) } + true + } + } + + override fun onPause() { + super.onPause() + if (reloadKeyboard) + KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(requireContext()) + reloadKeyboard = false + } + + override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String?) { + if (key == null) return + when (key) { + Settings.PREF_TOOLBAR_KEYS, Settings.PREF_CLIPBOARD_TOOLBAR_KEYS, Settings.PREF_PINNED_TOOLBAR_KEYS, + Settings.PREF_QUICK_PIN_TOOLBAR_KEYS -> reloadKeyboard = true + } + } +} diff --git a/app/src/main/res/drawable/ic_settings_toolbar.xml b/app/src/main/res/drawable/ic_settings_toolbar.xml new file mode 100644 index 000000000..5db7c5eb7 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_toolbar.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_settings_toolbar_foreground.xml b/app/src/main/res/drawable/ic_settings_toolbar_foreground.xml new file mode 100644 index 000000000..3831fb738 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_toolbar_foreground.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 51132e979..4e0ec40ce 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -756,8 +756,10 @@ New dictionary: Tap the language to open settings Choose input method - - "Appearance" + + Appearance + + Toolbar Description of hidden features diff --git a/app/src/main/res/xml/prefs.xml b/app/src/main/res/xml/prefs.xml index 313c1e9de..cbe4fe762 100644 --- a/app/src/main/res/xml/prefs.xml +++ b/app/src/main/res/xml/prefs.xml @@ -25,6 +25,12 @@ android:key="screen_appearance" android:icon="@drawable/ic_settings_appearance" latin:singleLineTitle="false" /> + - - - - - - - - - - diff --git a/app/src/main/res/xml/prefs_screen_toolbar.xml b/app/src/main/res/xml/prefs_screen_toolbar.xml new file mode 100644 index 000000000..1c0d50333 --- /dev/null +++ b/app/src/main/res/xml/prefs_screen_toolbar.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + From e69a54303d104836220404d830a1d8b7a07264ab Mon Sep 17 00:00:00 2001 From: Helium314 Date: Tue, 28 May 2024 22:04:21 +0200 Subject: [PATCH 53/58] move mns.txt to the correct folder --- .../assets/{language_key_texts => locale_key_texts}/mns.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/src/main/assets/{language_key_texts => locale_key_texts}/mns.txt (89%) diff --git a/app/src/main/assets/language_key_texts/mns.txt b/app/src/main/assets/locale_key_texts/mns.txt similarity index 89% rename from app/src/main/assets/language_key_texts/mns.txt rename to app/src/main/assets/locale_key_texts/mns.txt index 04674e713..9c36d5faa 100644 --- a/app/src/main/assets/language_key_texts/mns.txt +++ b/app/src/main/assets/locale_key_texts/mns.txt @@ -17,4 +17,4 @@ ю ю̄ [labels] -alphabet: АБВ \ No newline at end of file +alphabet: АБВ From 4ea2929139ed6dfcf1072fb7188576f1ecb085c2 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Thu, 30 May 2024 21:04:51 +0200 Subject: [PATCH 54/58] update hebrew layout, fixes #735 see discussion in #738 Co-authored-by: codokie <@> --- app/src/main/assets/layouts/hebrew.json | 66 ++++++++++++++++++++----- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/app/src/main/assets/layouts/hebrew.json b/app/src/main/assets/layouts/hebrew.json index e3c8d4b87..955f60ea4 100644 --- a/app/src/main/assets/layouts/hebrew.json +++ b/app/src/main/assets/layouts/hebrew.json @@ -3,42 +3,86 @@ { "$": "variation_selector", "email": { "label": "-" }, "url": { "label": "-" }, - "default": { "label": "'", "popup": { "main": { "label": "\"" } } } + "default": { "label": "'", "popup": { "relevant": [{ "label": "׳" }, { "label": "״" }, { "label": "\"" }] } } }, { "$": "variation_selector", "email": { "label": "_" }, "url": { "label": "_" }, - "default": { "label": "-", "popup": { "main": { "label": "_" } } } + "default": { "label": "-", "popup": { "relevant": [{ "label": "־" }, { "label": "_" }] } } }, - { "label": "ק" }, - { "label": "ר" }, + { "label": "ק", "popup": { + "relevant": [ + { "label": "\u05b8" }, + { "label": "\u05b3" }, + { "label": "\u05bb" } + ] + } }, + { "label": "ר", "popup": { + "relevant": [ + { "label": "\u05bf" } + ] + } }, { "label": "א" }, { "label": "ט" }, - { "label": "ו" }, + { "label": "ו", "popup": { + "relevant": [ + { "label": "ו\u05b9" }, + { "label": "ו\u05bc" } + ] + } }, { "label": "ן" }, { "label": "ם" }, - { "label": "פ" } + { "label": "פ", "popup": { + "relevant": [ + { "label": "\u05b7" }, + { "label": "\u05b2" } + ] + } } ], [ - { "label": "ש" }, - { "label": "ד" }, + { "label": "ש", "popup": { + "relevant": [ + { "label": "\u05b0" }, + { "label": "ש\u05c2" }, + { "label": "ש\u05c1" } + ] + } }, + { "label": "ד", "popup": { + "relevant": [ + { "label": "\u05bc" } + ] + } }, { "label": "ג" }, { "label": "כ" }, { "label": "ע" }, { "label": "י" }, - { "label": "ח" }, + { "label": "ח", "popup": { + "relevant": [ + { "label": "\u05b4" }, + { "label": "\u05b9" } + ] + } }, { "label": "ל" }, { "label": "ך" }, { "label": "ף" } ], [ { "label": "ז" }, - { "label": "ס" }, + { "label": "ס", "popup": { + "relevant": [ + { "label": "\u05b6" }, + { "label": "\u05b1" } + ] + } }, { "label": "ב" }, { "label": "ה" }, { "label": "נ" }, { "label": "מ" }, - { "label": "צ" }, + { "label": "צ", "popup": { + "relevant": [ + { "label": "\u05b5" } + ] + } }, { "label": "ת" }, { "label": "ץ" } ] From d171c6b3c6b021211ef5006c4f15af82a8569885 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Fri, 31 May 2024 09:42:49 +0200 Subject: [PATCH 55/58] reduce issues when splitting layout with customized functional keys and uncommon space bar --- .../keyboard/keyboard/internal/KeyboardBuilder.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt index 73f6c01b4..f528f0117 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt @@ -175,8 +175,13 @@ open class KeyboardBuilder(protected val mContext: Context, // insert spacer before first key that starts right of the center (also consider gap) var insertIndex = row.indexOfFirst { it.xPos + it.mAbsoluteWidth / 3 > 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 } + val indexOfProperSpace = row.indexOfFirst { + // should work reasonably with customizable layouts, where space key might be completely different: + // "normal" width space keys are ignored, and the possibility of space being first in row is considered + it.mCode == Constants.CODE_SPACE && it.mWidth > row.first { !it.isSpacer && it.mCode != Constants.CODE_SPACE }.mWidth * 1.5f + } + if (indexOfProperSpace >= 0) { + val spaceLeft = row[indexOfProperSpace] reduceSymbolAndActionKeyWidth(row) insertIndex = row.indexOf(spaceLeft) + 1 val widthBeforeSpace = row.subList(0, insertIndex - 1).sumOf { it.mWidth } From af636badab7d75d8980e30e9f26206bddc809456 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Fri, 31 May 2024 09:58:35 +0200 Subject: [PATCH 56/58] add comma key popups for number layouts (for phone layouts it's on period because there is no comma key) fixes #823 --- app/src/main/assets/layouts/number.json | 8 ++++---- app/src/main/assets/layouts/phone.json | 2 +- app/src/main/assets/layouts/phone_symbols.json | 2 +- .../internal/keyboard_parser/floris/TextKeyData.kt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/assets/layouts/number.json b/app/src/main/assets/layouts/number.json index bc6bae10b..5ee29fcad 100644 --- a/app/src/main/assets/layouts/number.json +++ b/app/src/main/assets/layouts/number.json @@ -19,15 +19,15 @@ ], [ { "$": "variation_selector", - "default": { "label": ",", "type": "numeric" }, - "date": { "label": ".", "type": "numeric" }, - "time": { "label": ".", "type": "numeric", "popup": { "relevant": [ + "default": { "label": ",", "type": "numeric", "groupId": 1 }, + "date": { "label": ".", "type": "numeric", "groupId": 1 }, + "time": { "label": ".", "type": "numeric", "groupId": 1, "popup": { "relevant": [ { "label": "!fixedColumnOrder!2" }, { "label": "!hasLabels!" }, { "label": "AM" }, { "label": "PM" } ] } }, - "datetime": { "label": ".", "type": "numeric", "popup": { "relevant": [ + "datetime": { "label": ".", "type": "numeric", "groupId": 1, "popup": { "relevant": [ { "label": "!fixedColumnOrder!2" }, { "label": "!hasLabels!" }, { "label": "AM" }, diff --git a/app/src/main/assets/layouts/phone.json b/app/src/main/assets/layouts/phone.json index c3ffd6677..b7a87cc43 100644 --- a/app/src/main/assets/layouts/phone.json +++ b/app/src/main/assets/layouts/phone.json @@ -20,7 +20,7 @@ [ { "label": "*#|!code/key_switch_alpha_symbol", "type": "numeric", "labelFlags": 524432 }, { "label": "0 +|0", "type": "numeric", "popup": { "relevant": [ { "label": "!noPanelAutoPopupKey!" }, { "label": "+" } ] } }, - { "label": ".", "type": "numeric", "labelFlags": 64 }, + { "label": ".", "type": "numeric", "labelFlags": 64, "groupId": 1 }, { "label": "action" } ] ] diff --git a/app/src/main/assets/layouts/phone_symbols.json b/app/src/main/assets/layouts/phone_symbols.json index d1dd29d16..03ed55962 100644 --- a/app/src/main/assets/layouts/phone_symbols.json +++ b/app/src/main/assets/layouts/phone_symbols.json @@ -20,7 +20,7 @@ [ { "label": "123|!code/key_switch_alpha_symbol", "type": "numeric", "labelFlags": 524432 }, { "label": "+", "type": "numeric" }, - { "label": ".", "type": "numeric" }, + { "label": ".", "type": "numeric", "groupId": 1 }, { "label": "action" } ] ] diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index 7fab7d62f..ac64c5178 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -119,7 +119,7 @@ sealed interface KeyData : AbstractKeyData { keys.add("!icon/clipboard_normal_key|!code/key_clipboard") if (!params.mId.mEmojiKeyEnabled && !params.mId.isNumberLayout) keys.add("!icon/emoji_normal_key|!code/key_emoji") - if (!params.mId.mLanguageSwitchKeyEnabled) + if (!params.mId.mLanguageSwitchKeyEnabled && !params.mId.isNumberLayout) keys.add("!icon/language_switch_key|!code/key_language_switch") if (!params.mId.mOneHandedModeEnabled) keys.add("!icon/start_onehanded_mode_key|!code/key_start_onehanded") From df6af1f07eb54a81830d4c571f61a26b60d3e3f2 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Fri, 31 May 2024 12:10:46 +0200 Subject: [PATCH 57/58] fix broken popup key width in number layouts --- .../helium314/keyboard/keyboard/PopupKeysKeyboard.java | 8 ++++---- .../keyboard/keyboard/internal/KeyboardParams.java | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboard.java b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboard.java index c3c6251fb..113404375 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboard.java +++ b/app/src/main/java/helium314/keyboard/keyboard/PopupKeysKeyboard.java @@ -23,7 +23,7 @@ public final class PopupKeysKeyboard extends Keyboard { PopupKeysKeyboard(final PopupKeysKeyboardParams params) { super(params); - mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultAbsoluteKeyWidth / 2; + mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mAbsolutePopupKeyWidth / 2; } public int getDefaultCoordX() { @@ -287,8 +287,8 @@ public Builder(final Context context, final Key key, final Keyboard keyboard, final float padding = context.getResources().getDimension( R.dimen.config_popup_keys_keyboard_key_horizontal_padding) + (key.hasLabelsInPopupKeys() - ? mParams.mDefaultAbsoluteKeyWidth * LABEL_PADDING_RATIO : 0.0f); - keyWidth = getMaxKeyWidth(key, mParams.mDefaultAbsoluteKeyWidth, padding, paintToMeasure); + ? mParams.mAbsolutePopupKeyWidth * LABEL_PADDING_RATIO : 0.0f); + keyWidth = getMaxKeyWidth(key, mParams.mAbsolutePopupKeyWidth, padding, paintToMeasure); rowHeight = keyboard.mMostCommonKeyHeight; } final int dividerWidth; @@ -342,7 +342,7 @@ public PopupKeysKeyboard build() { // left of the default position. if (params.mDividerWidth > 0 && pos != 0) { final int dividerX = (pos > 0) ? x - params.mDividerWidth - : x + params.mDefaultAbsoluteKeyWidth; + : x + params.mAbsolutePopupKeyWidth; final Key divider = new PopupKeyDivider( params, dividerX, y, params.mDividerWidth, params.mDefaultAbsoluteRowHeight); params.onAddKey(divider); diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java index 9d39c79d6..7f0fbe12b 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardParams.java @@ -66,6 +66,8 @@ public class KeyboardParams { public int mPopupKeysTemplate; public int mMaxPopupKeysKeyboardColumn; + // popup key width is separate from mDefaultAbsoluteKeyWidth because it should not depend on alpha or number layout + public int mAbsolutePopupKeyWidth; public int GRID_WIDTH; public int GRID_HEIGHT; @@ -225,11 +227,11 @@ public void readAttributes(final Context context, @Nullable final AttributeSet a mBaseWidth = mOccupiedWidth - mLeftPadding - mRightPadding; final float defaultKeyWidthFactor = context.getResources().getInteger(R.integer.config_screen_metrics) > 2 ? 0.9f : 1f; - mDefaultKeyWidth = mId.isNumberLayout() - ? 0.17f - : keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth, + final float alphaSymbolKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth, 1, 1, defaultKeyWidthFactor / DEFAULT_KEYBOARD_COLUMNS); + mDefaultKeyWidth = mId.isNumberLayout() ? 0.17f : alphaSymbolKeyWidth; mDefaultAbsoluteKeyWidth = (int) (mDefaultKeyWidth * mBaseWidth); + mAbsolutePopupKeyWidth = (int) (alphaSymbolKeyWidth * mBaseWidth); // todo: maybe settings should not be accessed from here? if (Settings.getInstance().getCurrent().mNarrowKeyGaps) { From e357f84572b127288dbc7b6c0e87ec4a0f48420b Mon Sep 17 00:00:00 2001 From: codokie <151087174+codokie@users.noreply.github.com> Date: Fri, 31 May 2024 18:01:50 +0300 Subject: [PATCH 58/58] Add toast notification when copying from clipboard (#752) and use the toast for showing dictionary on double-long-press word: switch from one ugly workaround (popup menu) to another (hiding more suggestions panel) Co-authored-by: codokie <@> Co-authored-by: Helium314 --- .../keyboard/keyboard/KeyboardSwitcher.java | 50 ++++++++++++++++++- .../keyboard/latin/RichInputConnection.java | 6 +++ .../suggestions/SuggestionStripView.java | 11 ++-- app/src/main/res/anim/fade_in.xml | 5 ++ app/src/main/res/anim/fade_out.xml | 5 ++ .../main/res/drawable/toast_background.xml | 6 +++ app/src/main/res/layout/fake_toast.xml | 18 +++++++ app/src/main/res/layout/input_view.xml | 3 ++ app/src/main/res/values-night/colors.xml | 1 + app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 2 + 11 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 app/src/main/res/anim/fade_in.xml create mode 100644 app/src/main/res/anim/fade_out.xml create mode 100644 app/src/main/res/drawable/toast_background.xml create mode 100644 app/src/main/res/layout/fake_toast.xml diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java index 1adb45216..6fd68514b 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java @@ -10,14 +10,19 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import helium314.keyboard.latin.utils.Log; +import android.graphics.drawable.Drawable; +import android.os.Build; import android.view.ContextThemeWrapper; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.NonNull; @@ -36,6 +41,7 @@ import helium314.keyboard.latin.settings.SettingsValues; import helium314.keyboard.latin.utils.CapsModeUtils; import helium314.keyboard.latin.utils.LanguageOnSpacebarUtils; +import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.RecapitalizeStatus; import helium314.keyboard.latin.utils.ResourceUtils; import helium314.keyboard.latin.utils.ScriptUtils; @@ -53,6 +59,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private HorizontalScrollView mClipboardStripScrollView; private View mSuggestionStripView; private ClipboardHistoryView mClipboardHistoryView; + private TextView mFakeToastView; private LatinIME mLatinIME; private RichInputMethodManager mRichImm; private boolean mIsHardwareAcceleratedDrawingEnabled; @@ -467,6 +474,46 @@ public void switchOneHandedMode() { Settings.getInstance().writeOneHandedModeGravity(mKeyboardViewWrapper.getOneHandedGravity()); } + /** + * Displays a toast message. + * + * @param text The text to display in the toast message. + * @param briefToast If true, the toast duration will be short; otherwise, it will last longer. + */ + public void showToast(final String text, final boolean briefToast){ + // In API 32 and below, toasts can be shown without a notification permission. + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { + final int toastLength = briefToast ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG; + final Toast toast = Toast.makeText(mLatinIME, text, toastLength); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.show(); + } else { + final int toastLength = briefToast ? 2000 : 3500; + showFakeToast(text, toastLength); + } + } + + // Displays a toast-like message with the provided text for a specified duration. + public void showFakeToast(final String text, final int timeMillis) { + if (mFakeToastView.getVisibility() == View.VISIBLE) return; + + final Drawable appIcon = mFakeToastView.getCompoundDrawables()[0]; + if (appIcon != null) { + final int bound = mFakeToastView.getLineHeight(); + appIcon.setBounds(0, 0, bound, bound); + mFakeToastView.setCompoundDrawables(appIcon, null, null, null); + } + mFakeToastView.setText(text); + mFakeToastView.setVisibility(View.VISIBLE); + mFakeToastView.bringToFront(); + mFakeToastView.startAnimation(AnimationUtils.loadAnimation(mLatinIME, R.anim.fade_in)); + + mFakeToastView.postDelayed(() -> { + mFakeToastView.startAnimation(AnimationUtils.loadAnimation(mLatinIME, R.anim.fade_out)); + mFakeToastView.setVisibility(View.GONE); + }, timeMillis); + } + // Implements {@link KeyboardState.SwitchActions}. @Override public boolean isInDoubleTapShiftKeyTimeout() { @@ -562,6 +609,7 @@ public View onCreateInputView(@NonNull Context displayContext, final boolean isH mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame); mEmojiPalettesView = mCurrentInputView.findViewById(R.id.emoji_palettes_view); mClipboardHistoryView = mCurrentInputView.findViewById(R.id.clipboard_history_view); + mFakeToastView = mCurrentInputView.findViewById(R.id.fakeToast); mKeyboardViewWrapper = mCurrentInputView.findViewById(R.id.keyboard_view_wrapper); mKeyboardViewWrapper.setKeyboardActionListener(mLatinIME.mKeyboardActionListener); diff --git a/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java b/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java index 8e4dc0571..f4711f5f6 100644 --- a/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java +++ b/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java @@ -10,11 +10,14 @@ import android.content.ClipboardManager; import android.content.Context; import android.inputmethodservice.InputMethodService; +import android.os.Build; import android.os.Bundle; import android.os.SystemClock; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.CharacterStyle; + +import helium314.keyboard.keyboard.KeyboardSwitcher; import helium314.keyboard.latin.utils.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; @@ -671,6 +674,9 @@ public void copyText(final boolean getSelection) { if (text == null || text.length() == 0) return; final ClipboardManager cm = (ClipboardManager) mParent.getSystemService(Context.CLIPBOARD_SERVICE); cm.setPrimaryClip(ClipData.newPlainText("copied text", text)); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { + KeyboardSwitcher.getInstance().showToast(mParent.getString(R.string.toast_msg_clipboard_copy), true); + } } public void commitCorrection(final CorrectionInfo correctionInfo) { diff --git a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java index af6699cba..c63cd2d6f 100644 --- a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java +++ b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.java @@ -41,6 +41,7 @@ import helium314.keyboard.accessibility.AccessibilityUtils; import helium314.keyboard.keyboard.Keyboard; +import helium314.keyboard.keyboard.KeyboardSwitcher; import helium314.keyboard.keyboard.MainKeyboardView; import helium314.keyboard.keyboard.PopupKeysPanel; import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode; @@ -445,12 +446,10 @@ private void showSourceDict(final TextView wordView) { final SuggestedWordInfo info = mSuggestedWords.getInfo(index); if (!info.getWord().equals(word)) return; final String text = info.mSourceDict.mDictType + ":" + info.mSourceDict.mLocale; - // apparently toast is not working on some Android versions, probably - // Android 13 with the notification permission - // Toast.makeText(getContext(), text, Toast.LENGTH_LONG).show(); - final PopupMenu uglyWorkaround = new PopupMenu(DialogUtilsKt.getPlatformDialogThemeContext(getContext()), wordView); - uglyWorkaround.getMenu().add(Menu.NONE, 1, Menu.NONE, text); - uglyWorkaround.show(); + if (isShowingMoreSuggestionPanel()) { + mMoreSuggestionsView.dismissPopupKeysPanel(); + } + KeyboardSwitcher.getInstance().showToast(text, true); } private void removeSuggestion(TextView wordView) { diff --git a/app/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml new file mode 100644 index 000000000..875cd5f97 --- /dev/null +++ b/app/src/main/res/anim/fade_in.xml @@ -0,0 +1,5 @@ + + diff --git a/app/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml new file mode 100644 index 000000000..cf78f0d3a --- /dev/null +++ b/app/src/main/res/anim/fade_out.xml @@ -0,0 +1,5 @@ + + diff --git a/app/src/main/res/drawable/toast_background.xml b/app/src/main/res/drawable/toast_background.xml new file mode 100644 index 000000000..4e0da5a4b --- /dev/null +++ b/app/src/main/res/drawable/toast_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/layout/fake_toast.xml b/app/src/main/res/layout/fake_toast.xml new file mode 100644 index 000000000..7d41e0004 --- /dev/null +++ b/app/src/main/res/layout/fake_toast.xml @@ -0,0 +1,18 @@ + + + diff --git a/app/src/main/res/layout/input_view.xml b/app/src/main/res/layout/input_view.xml index c719d55ab..0bd874eee 100644 --- a/app/src/main/res/layout/input_view.xml +++ b/app/src/main/res/layout/input_view.xml @@ -10,6 +10,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" style="?attr/inputViewStyle"> + diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 332735c97..c9aafc8c6 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -6,6 +6,7 @@ #FFF #BBB #666 + #444444 @color/keyboard_background_dark diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 3d3bcfd53..8e1aaab24 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -68,6 +68,7 @@ #000 #555 #AAA + #666666 #1A73E8 @color/keyboard_background_light diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4e0ec40ce..21806019e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -830,4 +830,6 @@ New dictionary: Variable toolbar direction Reverse direction when a right-to-left keyboard subtype is selected + + Content copied