diff --git a/app/src/main/assets/layouts/number.json b/app/src/main/assets/layouts/number.json new file mode 100644 index 000000000..33c37a01e --- /dev/null +++ b/app/src/main/assets/layouts/number.json @@ -0,0 +1,49 @@ +[ + [ + { "label": "1", "type": "numeric" }, + { "label": "2", "type": "numeric" }, + { "label": "3", "type": "numeric" }, + { "label": "-", "popup": { "main": { "label": "+" } }, "labelFlags": 1073742336 } + ], + [ + { "label": "4", "type": "numeric" }, + { "label": "5", "type": "numeric" }, + { "label": "6", "type": "numeric" }, + { "label": "space" } + ], + [ + { "label": "7", "type": "numeric" }, + { "label": "8", "type": "numeric" }, + { "label": "9", "type": "numeric" }, + { "label": "delete" } + ], + [ + { "$": "variation_selector", + "default": { "label": "," }, + "date": { "label": "." }, + "time": { "time": ".", "popup": { "relevant": [ + { "label": "!fixedColumnOrder!2" }, + { "label": "!hasLabels!" }, + { "label": "AM" }, + { "label": "PM" } + ] } }, + "datetime": { "time": ".", "popup": { "relevant": [ + { "label": "!fixedColumnOrder!2" }, + { "label": "!hasLabels!" }, + { "label": "AM" }, + { "label": "PM" } + ] } } + }, + { "label": "0", "type": "numeric" }, + { "$": "variation_selector", + "default": { "label": "." }, + "date": { "label": "/" }, + "time": { "time": ":" }, + "datetime": { "time": "/ :|/", "popup": { "relevant": [ + { "label": "!noPanelAutoMoreKey!" }, + { "label": "," } + ] } } + }, + { "label": "enter"} + ] +] diff --git a/app/src/main/assets/layouts/numpad.json b/app/src/main/assets/layouts/numpad.json new file mode 100644 index 000000000..1ef9038ee --- /dev/null +++ b/app/src/main/assets/layouts/numpad.json @@ -0,0 +1,50 @@ +[ + [ + { "label": "+", "popup": { + "relevant": [ + { "label": "(" }, + { "label": "<" }, + { "label": "±" } + ] + }, "labelFlags": 512 }, + { "label": "1", "type": "numeric" }, + { "label": "2", "type": "numeric" }, + { "label": "3", "type": "numeric" }, + { "label": "%", "popup": { "main": { "label": "$$$"} }, "labelFlags": 512 } + ], + [ + { "label": "-", "popup": { + "relevant": [ + { "label": ")" }, + { "label": ">" }, + { "label": "~" } + ] + }, "labelFlags": 512 }, + { "label": "4", "type": "numeric" }, + { "label": "5", "type": "numeric" }, + { "label": "6", "type": "numeric" }, + { "label": "space" } + ], + [ + { "label": "*", "popup": { + "relevant": [ + { "label": "/" }, + { "label": "×" }, + { "label": "÷" } + ] + }, "labelFlags": 512 }, + { "label": "7", "type": "numeric" }, + { "label": "8", "type": "numeric" }, + { "label": "9", "type": "numeric" }, + { "label": "delete" } + ], + [ + { "label": "alpha" }, + { "label": "comma" }, + { "label": "symbol" }, + { "label": "0", "type": "numeric" }, + { "label": "=", "popup": { "relevant": [ { "label": "≠"}, { "label": "≈"} ] } }, + { "label": "period" }, + { "label": "enter" } + ] +] diff --git a/app/src/main/assets/layouts/phone.json b/app/src/main/assets/layouts/phone.json new file mode 100644 index 000000000..1480f8ac2 --- /dev/null +++ b/app/src/main/assets/layouts/phone.json @@ -0,0 +1,26 @@ +[ + [ + { "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": "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": "7", "type": "numeric", "popup": { "main": { "label": "PQRS" } } }, + { "label": "8", "type": "numeric", "popup": { "main": { "label": "TUV" } } }, + { "label": "9", "type": "numeric", "popup": { "main": { "label": "WXYZ" } } }, + { "label": "delete" } + ], + [ + { "label": "*#|!code/key_switch_alpha_symbol", "labelFlags": 524432 }, + { "label": "0 +|0", "type": "numeric", "popup": { "relevant": [ { "label": "!noPanelAutoMoreKey!" }, { "label": "+" } ] } }, + { "label": "." }, + { "label": "action" } + ] +] diff --git a/app/src/main/assets/layouts/phone_symbols.json b/app/src/main/assets/layouts/phone_symbols.json new file mode 100644 index 000000000..140bd4741 --- /dev/null +++ b/app/src/main/assets/layouts/phone_symbols.json @@ -0,0 +1,26 @@ +[ + [ + { "label": "(", "type": "numeric" }, + { "label": "/", "type": "numeric" }, + { "label": ")", "type": "numeric" }, + { "label": "-", "popup": { "main": { "label": "+" } } } + ], + [ + { "label": "N", "type": "numeric" }, + { "label": "!string/label_pause_key|,", "type": "numeric" }, + { "label": ",", "type": "numeric" }, + { "label": "space" } + ], + [ + { "label": "*|*", "type": "numeric" }, + { "label": "!string/label_wait_key|;", "type": "numeric" }, + { "label": "\\#", "type": "numeric" }, + { "label": "delete" } + ], + [ + { "label": "123|!code/key_switch_alpha_symbol", "labelFlags": 524432 }, + { "label": "+", "type": "numeric" }, + { "label": "." }, + { "label": "action" } + ] +] diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java index 9ed7521d0..ca74c52c5 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/Key.java @@ -958,13 +958,13 @@ public class Key implements Comparable { // params that remains constant public final int mCode; - @Nullable public final String mLabel; + @Nullable public String mLabel; @Nullable public final String mHintLabel; public final int mLabelFlags; public final int mIconId; - @Nullable public final MoreKeySpec[] mMoreKeys; + @Nullable public MoreKeySpec[] mMoreKeys; public final int mMoreKeysColumnAndFlags; - public final int mBackgroundType; + public int mBackgroundType; public final int mActionFlags; @Nullable public final KeyVisualAttributes mKeyVisualAttributes; @Nullable public OptionalAttributes mOptionalAttributes; @@ -1206,6 +1206,8 @@ public class Key implements Comparable { final boolean needsToUpcase = needsToUpcase(mLabelFlags, params.mId.mElementId); final Locale localeForUpcasing = params.mId.getLocale(); int actionFlags = 0; + if (params.mId.isNumberLayout()) + actionFlags = ACTION_FLAGS_NO_KEY_PREVIEW; final String[] languageMoreKeys = params.mLocaleKeyTexts.getMoreKeys(keySpec); if (languageMoreKeys != null && layoutMoreKeys != null && languageMoreKeys[0].startsWith("!fixedColumnOrder!")) diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardId.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardId.java index f6a5973d0..2f2ef5aa1 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardId.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardId.java @@ -178,6 +178,11 @@ public final class KeyboardId { || mElementId == ELEMENT_ALPHABET_AUTOMATIC_SHIFTED || mElementId == ELEMENT_ALPHABET_MANUAL_SHIFTED; } + public boolean isNumberLayout() { + return mElementId == ELEMENT_NUMBER || mElementId == ELEMENT_NUMPAD + || mElementId == ELEMENT_PHONE || mElementId == ELEMENT_PHONE_SYMBOLS; + } + public int imeAction() { return InputTypeUtils.getImeOptionsActionIdFromEditorInfo(mEditorInfo); } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt index c47882307..19c7076d3 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardBuilder.kt @@ -68,8 +68,6 @@ open class KeyboardBuilder(protected val mContext: Context, return this // todo: further plan - // migrate keypad layouts to this style - // will need more configurable layout definition -> another parser, or do it with compatible jsons // make the remove duplicate moreKey thing an option? // why is it on for serbian (latin), but not for german (german)? // only nordic and serbian_qwertz layouts have it disabled, default is enabled @@ -81,6 +79,10 @@ open class KeyboardBuilder(protected val mContext: Context, // write another parser, it should already consider split // add a setting to display all emojis (and use emojiv2 or emojicompat or whatever is necessary) // mention in subtitle that they may not be displayed properly, depending on the app you're writing in + // uncomment the toast below + // number layouts missing details + // landscape: numpad layout has some extra keys + // tablet: number and phone layout have some extra keys (unify with numpad, so that extra keys show both in land and sw600? or only land?) // now all layouts should be using the new parser -> throw an error instead of falling back to old parser // more settings for localized number row, so it can be different in shift or symbols // migrate moreKeys and moreSuggestions to this style? @@ -194,19 +196,19 @@ open class KeyboardBuilder(protected val mContext: Context, Log.w(TAG, "moreKeys not null for ${keyParams.mLabel} / ${keyParams.mCode}, but xml null") else if (xmlParams.mMoreKeys == null || keyParams.mMoreKeys == null || keyParams.mMoreKeys.contentEquals(xmlParams.mMoreKeys)) Unit - else if (keyParams.mMoreKeys.size < xmlParams.mMoreKeys.size) { - if (keyParams.mMoreKeys.size - xmlParams.mMoreKeys.size == -1 && keyParams.mCode.toChar().lowercase() == "s") + else if (keyParams.mMoreKeys!!.size < xmlParams.mMoreKeys!!.size) { + if (keyParams.mMoreKeys!!.size - xmlParams.mMoreKeys!!.size == -1 && keyParams.mCode.toChar().lowercase() == "s") Log.i(TAG, "missing moreKeys for ${keyParams.mLabel} / ${keyParams.mCode}") else Log.w(TAG, "missing moreKeys for ${keyParams.mLabel} / ${keyParams.mCode}") - } else if (keyParams.mMoreKeys.size > xmlParams.mMoreKeys.size) { - if (keyParams.mMoreKeys.toList().containsAll(xmlParams.mMoreKeys.toList())) - Log.i(TAG, "more moreKeys for ${keyParams.mLabel} / ${keyParams.mCode}, first same: ${keyParams.mMoreKeys.firstOrNull() == xmlParams.mMoreKeys.firstOrNull() }" + + } else if (keyParams.mMoreKeys!!.size > xmlParams.mMoreKeys!!.size) { + if (keyParams.mMoreKeys!!.toList().containsAll(xmlParams.mMoreKeys!!.toList())) + Log.i(TAG, "more moreKeys for ${keyParams.mLabel} / ${keyParams.mCode}, first same: ${keyParams.mMoreKeys?.firstOrNull() == xmlParams.mMoreKeys?.firstOrNull() }" + ", contains all original: true") // not really an issue i would say else - Log.w(TAG, "more moreKeys for ${keyParams.mLabel} / ${keyParams.mCode}, first same: ${keyParams.mMoreKeys.firstOrNull() == xmlParams.mMoreKeys.firstOrNull() }" + + Log.w(TAG, "more moreKeys for ${keyParams.mLabel} / ${keyParams.mCode}, first same: ${keyParams.mMoreKeys?.firstOrNull() == xmlParams.mMoreKeys?.firstOrNull() }" + ", contains all original: false") - } else if (!keyParams.mMoreKeys.toList().containsAll(xmlParams.mMoreKeys.toList())) + } else if (!keyParams.mMoreKeys!!.toList().containsAll(xmlParams.mMoreKeys!!.toList())) Log.w(TAG, "same size but missing moreKeys for ${keyParams.mLabel} / ${keyParams.mCode}") if (keyParams.mCode != xmlParams.mCode) Log.w(TAG, "code different: ${keyParams.mCode} vs ${xmlParams.mCode}") @@ -237,6 +239,12 @@ open class KeyboardBuilder(protected val mContext: Context, } return this } + if (DebugFlags.DEBUG_ENABLED) { + // looks like only emoji keyboards are still using the old parser, which is expected + Log.w(TAG, "falling back to old parser for $id") + if (mParams.mId.mElementId < KeyboardId.ELEMENT_EMOJI_RECENTS || mParams.mId.mElementId > KeyboardId.ELEMENT_EMOJI_CATEGORY16) + Toast.makeText(mContext, "using old parser for $id", Toast.LENGTH_LONG).show() + } } mParams.mId = id // loading a keyboard should set default params like mParams.readAttributes(mContext, attrs); @@ -298,7 +306,7 @@ open class KeyboardBuilder(protected val mContext: Context, for (row in keysInRows) { if (row.isEmpty()) continue fillGapsWithSpacers(row) - var currentX = 0f + var currentX = mParams.mLeftPadding.toFloat() row.forEach { it.setDimensionsFromRelativeSize(currentX, currentY) if (DebugFlags.DEBUG_ENABLED) diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardTextsSet.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardTextsSet.java index f2a158e27..24468cc0c 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -9,6 +9,7 @@ package org.dslul.openboard.inputmethod.keyboard.internal; import android.content.Context; import android.content.res.Resources; import android.text.TextUtils; +import android.util.Log; import org.dslul.openboard.inputmethod.annotations.UsedForTesting; import org.dslul.openboard.inputmethod.latin.RichInputMethodManager; @@ -18,7 +19,6 @@ import org.dslul.openboard.inputmethod.latin.utils.RunInLocale; import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Locale; @@ -69,6 +69,7 @@ public final class KeyboardTextsSet { } public String getText(final String name) { + Log.w(getClass().getSimpleName(), "still used for resolving "+name); return getTextInternal(name, 0); // only used for emoji and clipboard keyboards } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/KeyboardParser.kt index e405c5351..f60717511 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -1,6 +1,7 @@ package org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser import android.content.Context +import android.content.res.Configuration import android.content.res.Resources import android.util.Log import android.view.inputmethod.EditorInfo @@ -13,6 +14,7 @@ import org.dslul.openboard.inputmethod.keyboard.KeyboardTheme import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardIconsSet import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardParams import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.floris.KeyData +import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.floris.KeyType import org.dslul.openboard.inputmethod.latin.R import org.dslul.openboard.inputmethod.latin.common.Constants import org.dslul.openboard.inputmethod.latin.common.splitOnWhitespace @@ -32,12 +34,10 @@ import java.util.Locale * keys in symbol layouts. * By default, all normal keys have the same width and flags, which may cause issues with the * requirements of certain non-latin languages. todo: add labelFlags to Json parser, or determine automatically? - * - * Currently the number, phone and numpad layouts are not compatible with this parser. */ abstract class KeyboardParser(private val params: KeyboardParams, private val context: Context) { private val infos = layoutInfos(params) - private val defaultLabelFlags = infos.defaultLabelFlags or if (!params.mId.isAlphabetKeyboard) + private val defaultLabelFlags = infos.defaultLabelFlags or 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 @@ -48,6 +48,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co fun parseLayoutFromAssets(layoutName: String): ArrayList> = parseLayoutString(getLayoutFromAssets(layoutName)) + // this thing does too much... make it more understandable after everything is implemented fun parseLayoutString(layoutContent: String): ArrayList> { params.readAttributes(context, null) if (infos.touchPositionCorrectionData == null) // need to set correctly, as it's not properly done in readAttributes with attr = null @@ -55,17 +56,43 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co else params.mTouchPositionCorrection.load(context.resources.getStringArray(infos.touchPositionCorrectionData)) - val keysInRows = ArrayList>() val baseKeys: MutableList> = parseCoreLayout(layoutContent) + + val keysInRows: ArrayList> + if (params.mId.mElementId <= KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { + keysInRows = createAlphaSymbolRows(baseKeys) + } else if (params.mId.isNumberLayout) { + keysInRows = createNumericRows(baseKeys) + } else { + throw(UnsupportedOperationException("creating KeyboardId ${params.mId.mElementId} not supported")) + } + // rescale height if we have more than 4 rows (todo: there is some default row count in params that could be used) + val heightRescale = if (keysInRows.size > 4) 4f / keysInRows.size else 1f + if (params.mId.mNumberRowEnabled && params.mId.mElementId <= KeyboardId.ELEMENT_SYMBOLS_SHIFTED) + keysInRows.add(0, getNumberRow()) // todo: maybe this should be in createAlphaSymbolRows, but first need to decide height stuff below + if (heightRescale != 1f) { + // rescale all keys, so number row doesn't look weird (this is done like in current parsing) + // todo: in symbols view, number row is not rescaled + // so the symbols keyboard is higher than the normal one + // not a new issue, but should be solved in this migration + // how? possibly scale all keyboards to height of main alphabet? (consider suggestion strip) + keysInRows.forEach { row -> row.forEach { it.mRelativeHeight *= heightRescale } } + } + + return keysInRows + } + + private fun createAlphaSymbolRows(baseKeys: MutableList>): ArrayList> { + // number row related modifications of baseKeys if (!params.mId.mNumberRowEnabled && params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) { // replace first symbols row with number row baseKeys[0] = params.mLocaleKeyTexts.getNumberRow() } else if (!params.mId.mNumberRowEnabled && params.mId.isAlphabetKeyboard - && params.mId.locale.language != "ko" - && params.mId.locale.language != "th" - && params.mId.locale.language != "lo" - && params.mId.mSubtype.keyboardLayoutSetName != "pcqwerty" - ) { + && params.mId.locale.language != "ko" + && params.mId.locale.language != "th" + && params.mId.locale.language != "lo" + && params.mId.mSubtype.keyboardLayoutSetName != "pcqwerty" + ) { // add number to the first 10 keys in first row // setting the correct moreKeys is handled in PopupSet // not for korean/lao/thai layouts, todo: should be decided in the layout, not in the parser @@ -76,13 +103,14 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co Toast.makeText(context, message, Toast.LENGTH_LONG).show() } } + + val keysInRows = ArrayList>() val functionalKeysReversed = parseFunctionalKeys(R.string.key_def_functional).reversed() val functionalKeysTop = parseFunctionalKeys(R.string.key_def_functional_top_row) // keyboard parsed bottom-up because the number of rows is not fixed, but the functional keys // are always added to the rows near the bottom keysInRows.add(getBottomRowAndAdjustBaseKeys(baseKeys)) - baseKeys.reversed().forEachIndexed { i, it -> val row: List = if (i == 0 && isTablet()) { // add bottom row extra keys @@ -163,19 +191,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co keysInRows.add(0, paramsRow) // we're doing it backwards, so add on top } resizeLastNormalRowIfNecessaryForAlignment(keysInRows) - // rescale height if we have more than 4 rows - val heightRescale = if (keysInRows.size > 4) 4f / keysInRows.size else 1f - if (params.mId.mNumberRowEnabled) - keysInRows.add(0, getNumberRow()) - if (heightRescale != 1f) { - // rescale all keys, so number row doesn't look weird (this is done like in current parsing) - // todo: in symbols view, number row is not rescaled - // so the symbols keyboard is higher than the normal one - // not a new issue, but should be solved in this migration - // how? possibly scale all keyboards to height of main alphabet? (consider suggestion strip) - keysInRows.forEach { row -> row.forEach { it.mRelativeHeight *= heightRescale } } - } - return keysInRows } @@ -205,6 +220,91 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co lastNormalRow.add(lastNormalRow.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) { + // 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, converting names from florisBoard to what is used here (should be unified) + // note that this is ignoring code on those keys, if any + val functionalKeyName = when (key.label) { + "view_characters" -> "alpha" + "view_symbols" -> "symbol" + "enter" -> "action" + // todo (later): maybe add special moreKeys for phone and number layouts? + "." -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) "period" else "." + "," -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) "comma" else "," + else -> key.label + } + if (functionalKeyName.length > 1 && key.type != KeyType.NUMERIC) { + try { + keyParams = getFunctionalKeyParams(functionalKeyName) + } 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.compute(params).toKeyParams(params, 0.17f, 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) + else + key.compute(params).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?.getLabel(params)?.length?.let { it > 1 } == true) { + keyParams.mMoreKeys = null // the ABC and stuff labels should not create moreKeys + } + 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)?.mRelativeWidth = paramsRow[n].mRelativeWidth + paramsRow.getOrNull(n + 1)?.mRelativeWidth = paramsRow[n].mRelativeWidth + } 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 + } + } + } + val widthSum = paramsRow.sumOf { it.mRelativeWidth } + paramsRow.forEach { it.mRelativeWidth /= 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 @@ -332,7 +432,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co FunctionalKey.PERIOD -> KeyParams( // special period moreKey only in alphabet layout, except for ar and fa // todo: here is not the place to decide this, put it somewhere else (labelPeriod and labelPeriodSymbols?) - label ?: if (params.mId.isAlphabetKeyboard || params.mId.locale.language in listOf("ar", "fa")) params.mLocaleKeyTexts.labelPeriod else ".", + label ?: getPeriodLabel(), params, width, Key.LABEL_FLAGS_HAS_POPUP_HINT @@ -344,10 +444,10 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co moreKeys?.let { getPunctuationMoreKeys() + it } ?: getPunctuationMoreKeys() ) FunctionalKey.SPACE -> KeyParams( - "!icon/space_key|!code/key_space", // !icon/space_key_for_number_layout in number layout, but not on tablet + getSpaceLabel(), params, width, // will not be used for normal space (only in number layouts) - 0, // todo (later): alignIconToBottom for non-tablet number layout -> check what it does / whether it's necessary + if (params.mId.isNumberLayout) Key.LABEL_FLAGS_ALIGN_ICON_TO_BOTTOM else 0, Key.BACKGROUND_TYPE_SPACEBAR, null ) @@ -474,7 +574,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co else -> null } // could change definition of numbers to query a range, or have a pre-defined list, but not that crucial - params.mId.mMode in listOf(KeyboardId.MODE_URL, KeyboardId.MODE_EMAIL, KeyboardId.ELEMENT_PHONE, KeyboardId.ELEMENT_NUMBER, KeyboardId.MODE_DATE, KeyboardId.MODE_TIME, KeyboardId.MODE_DATETIME) -> when { + params.mId.isNumberLayout || params.mId.mMode in listOf(KeyboardId.MODE_URL, KeyboardId.MODE_EMAIL, KeyboardId.MODE_DATE, KeyboardId.MODE_TIME, KeyboardId.MODE_DATETIME) -> when { action == EditorInfo.IME_ACTION_NEXT && navigatePrev -> createMoreKeysArray(MORE_KEYS_NAVIGATE_PREVIOUS) action == EditorInfo.IME_ACTION_NEXT -> null action == EditorInfo.IME_ACTION_PREVIOUS && navigateNext -> createMoreKeysArray(MORE_KEYS_NAVIGATE_NEXT) @@ -536,6 +636,10 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co Toast.makeText(context, message, Toast.LENGTH_LONG).show() return this } + return getInLocale(id) + } + + private fun getInLocale(@StringRes id: Int): String { val ril = object : RunInLocale() { // todo (later): simpler way of doing this in a single line? override fun job(res: Resources) = res.getString(id) } @@ -570,11 +674,20 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co 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")) + return params.mLocaleKeyTexts.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.mLocaleKeyTexts.labelComma } @@ -582,7 +695,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co val keys = mutableListOf() if (!params.mId.mDeviceLocked) keys.add("!icon/clipboard_normal_key|!code/key_clipboard") - if (!params.mId.mEmojiKeyEnabled) + 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") @@ -596,6 +709,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co private fun getPunctuationMoreKeys(): Array { if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS || params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) return arrayOf("…") + if (params.mId.isNumberLayout) + return arrayOf(":", "…", ";", "∞", "π", "√", "°", "^") val moreKeys = params.mLocaleKeyTexts.getMoreKeys("punctuation")!! if (params.mId.mSubtype.isRtlSubtype) { for (i in moreKeys.indices) @@ -613,6 +728,11 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co return moreKeys } + private fun getSpaceLabel(): String = + if (params.mId.mElementId <= KeyboardId.ELEMENT_SYMBOLS_SHIFTED || isTablet()) + "!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 { @@ -628,6 +748,10 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co id.mElementId == KeyboardId.ELEMENT_SYMBOLS -> SimpleKeyboardParser(params, context).parseLayoutFromAssets("symbols") id.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> SimpleKeyboardParser(params, context).parseLayoutFromAssets("symbols_shifted") + id.mElementId == KeyboardId.ELEMENT_NUMPAD -> JsonKeyboardParser(params, context).parseLayoutFromAssets("numpad") + id.mElementId == KeyboardId.ELEMENT_NUMBER -> JsonKeyboardParser(params, context).parseLayoutFromAssets("number") + id.mElementId == KeyboardId.ELEMENT_PHONE -> JsonKeyboardParser(params, context).parseLayoutFromAssets("phone") + id.mElementId == KeyboardId.ELEMENT_PHONE_SYMBOLS -> JsonKeyboardParser(params, context).parseLayoutFromAssets("phone_symbols") !id.isAlphabetKeyboard -> null layoutFileNames.contains("$layoutName.json") -> JsonKeyboardParser(params, context).parseLayoutFromAssets(layoutName) layoutFileNames.contains("${getSimpleLayoutName(layoutName, params)}.txt") @@ -701,7 +825,7 @@ data class LayoutInfos( ) fun String.rtlLabel(params: KeyboardParams): String { - if (!params.mId.mSubtype.isRtlSubtype) return this + if (!params.mId.mSubtype.isRtlSubtype || params.mId.isNumberLayout) return this return when (this) { "{" -> "{|}" "}" -> "}|{" diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/floris/KeyData.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/floris/KeyData.kt index 3c98754f1..0fa824cfb 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/floris/KeyData.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/floris/KeyData.kt @@ -22,6 +22,7 @@ import org.dslul.openboard.inputmethod.latin.common.StringUtils // char_width_selector and kana_selector throw an error (not yet supported) // added labelFlags to keyDate // added manualOrLocked for shift_state_selector +// added date, time and datetime to VariationSelector /** * Basic interface for a key data object. Base for all key data objects across the IME, such as text, emojis and * selectors. The implementation is as abstract as possible, as different features require different implementations. @@ -111,9 +112,12 @@ interface KeyData : AbstractKeyData { } fun toKeyParams(params: KeyboardParams, width: Float = params.mDefaultRelativeKeyWidth, additionalLabelFlags: Int = 0): KeyParams { - require(type == KeyType.CHARACTER) { "currently only KeyType.CHARACTER is supported" } - require(groupId == GROUP_DEFAULT) { "currently only KeyData.GROUP_DEFAULT is supported" } - require(code >= 0) { "functional codes ($code) not (yet) supported" } + // 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" } + // allow some negative codes so original florisboard number layouts can be used, those codes are actually ignored + require(code >= 0 || code == -7 || code == -201 || code == -202) { "functional code $code not (yet) supported" } require(code != KeyCode.UNSPECIFIED || label.isNotEmpty()) { "key has no code and no label" } return if (code == KeyCode.UNSPECIFIED || code == KeyCode.MULTIPLE_CODE_POINTS) { @@ -258,6 +262,9 @@ data class VariationSelector( val uri: AbstractKeyData? = null, val normal: AbstractKeyData? = null, val password: AbstractKeyData? = null, + val date: AbstractKeyData? = null, + val time: AbstractKeyData? = null, + val datetime: AbstractKeyData? = null, ) : AbstractKeyData { override fun compute(params: KeyboardParams): KeyData? { return when { @@ -267,6 +274,9 @@ data class VariationSelector( params.mId.passwordInput() -> password ?: default params.mId.mMode == KeyboardId.MODE_EMAIL -> email ?: default params.mId.mMode == KeyboardId.MODE_URL -> uri ?: default + params.mId.mMode == KeyboardId.MODE_DATE -> date ?: default + params.mId.mMode == KeyboardId.MODE_TIME -> time ?: default + params.mId.mMode == KeyboardId.MODE_DATETIME -> datetime ?: default else -> default }?.compute(params) }