From 2232bc3848bacb79377e2e2999f642e4bfb597de Mon Sep 17 00:00:00 2001 From: Helium314 Date: Mon, 4 Dec 2023 12:11:53 +0100 Subject: [PATCH] improvements for tablet layouts with new parser --- README.md | 7 ++- app/src/main/assets/language_key_texts/ar.txt | 1 + app/src/main/assets/language_key_texts/fa.txt | 1 + app/src/main/assets/language_key_texts/ur.txt | 1 + .../main/assets/layouts/symbols_arabic.txt | 2 +- .../openboard/inputmethod/keyboard/Key.java | 1 + .../keyboard/internal/KeyboardBuilder.kt | 43 +++++------------ .../keyboard_parser/KeyboardParser.kt | 46 ++++++++++++------- .../keyboard_parser/LocaleKeyTexts.kt | 25 ++++++++-- .../res/values-sw600dp/functional-keys.xml | 4 +- app/src/main/res/values/functional-keys.xml | 6 ++- 11 files changed, 78 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 384f7c03b..8f8bf2b39 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Features that may go unnoticed * Reduce space between keys, with option to use old values, https://github.com/Helium314/openboard/pull/8 * Fix number row not split in split keyboard view, https://github.com/Helium314/openboard/pull/27 * Fix issue with spell checker incorrectly flagging words before a period as wrong on newer Android versions, https://github.com/openboard-team/openboard/pull/679 - * maybe not properly fixed, this causes some other issues + * maybe not properly fixed, this causes some other issues, https://github.com/Helium314/openboard/issues/55 * Fix always-dark settings on some Android versions, https://github.com/Helium314/openboard/pull/69 * Fix bug with space before word being deleted in some apps / input fields, https://github.com/Helium314/openboard/commit/ce0bf06545c4547d3fc5791cc769508db0a89e87 * Allow using auto theme on some devices with Android 9 @@ -82,11 +82,14 @@ Features that may go unnoticed * Updated translations * Open dictionary files with the app * Add more options to the language switch key +* New keyboard parser (no need to use `tools:make-keyboard-text:makeText` any more) + * Can use simple text files or JSON files as used by [FlorisBoard](https://github.com/florisboard/florisboard/tree/master/app/src/main/assets/ime/keyboard/org.florisboard.layouts/layouts) ## The rough plan/todo before "full" release * Finish keyboard parsing upgrades - * In the end, layouts should be defined in either simple text files, or json files, ideally in same format as used by [FlorisBoard](https://github.com/florisboard/florisboard/tree/master/app/src/main/assets/ime/keyboard/org.florisboard.layouts/layouts) * Users should be allowed to add their own layouts + * Determine symbol popup keys from symbols layout instead of hardcoding them to the layout (still allow layout to override or add popup keys) + * Allow users more control over popup keys (which hint label to show, which popup keys (language, layout, symbols) to show first or at all) * Overhaul the view system * Have a fixed height common to all views (keyboard, emoji, clipboard) * Should allow for more flexible one-handed mode (though the actual one-handed mode changes may be implemented later) diff --git a/app/src/main/assets/language_key_texts/ar.txt b/app/src/main/assets/language_key_texts/ar.txt index 9c83072af..827c39b6a 100644 --- a/app/src/main/assets/language_key_texts/ar.txt +++ b/app/src/main/assets/language_key_texts/ar.txt @@ -18,6 +18,7 @@ punctuation !fixedColumnOrder!7 ٕ|ٕ ٔ|ٔ ْ|ْ ٍ|ٍ ٌ|ٌ ً|ً ّ|ّ alphabet: أ‌ب‌ج symbol: ٣٢١؟ comma: ، +question: ؟ [number_row] ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ٠ diff --git a/app/src/main/assets/language_key_texts/fa.txt b/app/src/main/assets/language_key_texts/fa.txt index 5e0d05c26..268b9015b 100644 --- a/app/src/main/assets/language_key_texts/fa.txt +++ b/app/src/main/assets/language_key_texts/fa.txt @@ -11,6 +11,7 @@ punctuation !fixedColumnOrder!7 ٕ|ٕ ْ|ْ ّ|ّ ٌ|ٌ ٍ|ٍ ً|ً ٔ|ٔ alphabet: ا‌ب‌پ symbol: ۳۲۱؟ comma: ، +question: ؟ [number_row] ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰ diff --git a/app/src/main/assets/language_key_texts/ur.txt b/app/src/main/assets/language_key_texts/ur.txt index 2060bf88e..4c1546087 100644 --- a/app/src/main/assets/language_key_texts/ur.txt +++ b/app/src/main/assets/language_key_texts/ur.txt @@ -25,6 +25,7 @@ alphabet: اب‌پ comma: ، symbol: ۳۲۱؟ period: ۔ +question: ؟ [number_row] ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰ diff --git a/app/src/main/assets/layouts/symbols_arabic.txt b/app/src/main/assets/layouts/symbols_arabic.txt index d4ded1af8..398e4d925 100644 --- a/app/src/main/assets/layouts/symbols_arabic.txt +++ b/app/src/main/assets/layouts/symbols_arabic.txt @@ -25,4 +25,4 @@ $$$ : ؛ ; ! -؟ ? ¿ +؟ 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 e1ea35651..7640411e1 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 @@ -978,6 +978,7 @@ public class Key implements Comparable { public static KeyParams newSpacer(final KeyboardParams params, final float relativeWidth) { final KeyParams spacer = new KeyParams(params); spacer.mRelativeWidth = relativeWidth; + spacer.mRelativeHeight = params.mDefaultRelativeRowHeight; return spacer; } 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 5fa2ea07a..5c5a9e128 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 @@ -63,30 +63,14 @@ open class KeyboardBuilder(protected val mContext: Context, return this // todo: further plan - // migrate other languages/layouts to this style - // test whether the layouts really are the same - // comparing params with both parsers looks good, see list of detected differences below - // still need to check moreKeys, there will be many more differences that might just be minor - // write down a list of differences, ask users to open issues if they don't like them - // some keyboard_layout_set have supportedScript that is enum synced with script id in ScriptUtils - // that's one more reason for using language tags... - // but currently it's still read from xml outside the keyboard parser, so that's fine for now - // issues: - // armenian bottom row (and some more): functional keys could be narrower - // rtl parentheses hint label (urdu and more) - // urdu and others: no labels because the moreKeys are languageMoreKeys -> need the moreKeys setting soon (at least setting to show first language moreKey if no symbol) - // setting: symbol morekey(s): layout default, take from symbols, layout default but fill from symbols if empty - // setting: hint label: from symbol morekey only, symbol but language if none, first choice on long press - // (later): setting which moreKeys to prefer (default: symbol or important language, always symbol, always language) - // (later): setting whether to show duplicate moreKeys (describe properly what it actually does) - // and have some setting to enable configuring this per locale (in language settings -> potentially should not be a dialog any more but a fragment?) - // label flags are horrible to "decompile" when viewing a layout // migrate pcqwerty to this style // this will be more complicated... // linked shift keys might be easy // distance below number row with high row, but reduced key height? // consider settings key needs to disappear if device is locked - // ignore number row setting? + // ignore number row setting to avoid duplicate number row? + // and test tablet layout! + // do the moreKeys thing (only now, because pc layout may change stuff) // 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? @@ -100,9 +84,16 @@ 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 + // 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? // at least they should not make use of the KeyTextsSet/Table (and of the XmlKeyboardParser?) + // setting which moreKeys to prefer (default: symbol or important language, always symbol, always language) + // setting whether to show duplicate moreKeys (describe properly what it actually does) + // some keyboard_layout_set have supportedScript that is enum synced with script id in ScriptUtils + // that's one more reason for using language tags... + // currently it's still read from xml outside the keyboard parser, but should still go to some other place + // maybe use scriptUtils to determine, just make sure it's correct (also for hindi and serbian!) // remove the old parser // then finally the spanish/german/swiss/nordic layouts can be removed and replaced by some hasExtraKeys parameter // also the eo check could then be removed @@ -155,7 +146,7 @@ open class KeyboardBuilder(protected val mContext: Context, // autoXScale: com key, action keys, some on phone layout, some non-latin languages // autoScale: only one single letter in khmer layout (includes autoXScale) // preserveCase: action key + more keys, com key, shift keys - // shiftedLetterActivated: period and some keys on pcqwerty, tablet only + // shiftedLetterActivated: period and some keys on pcqwerty, tablet only (wtf, when enabled can't open moreKeys -> remove? or what would be the use?) // fromCustomActionLabel: action key with customLabelActionKeyStyle -> check parser where to get this info // followFunctionalTextColor: number mode keys, action key // keepBackgroundAspectRatio: lxx and rounded action more keys, lxx no-border action and emoji, moreKeys keyboard view @@ -189,18 +180,6 @@ open class KeyboardBuilder(protected val mContext: Context, return@forEachIndexed } xmlRow2.forEachIndexed { index1, xmlParams -> - // todo: compare tablet layouts (how to best force it for both parsers?) - // just rename the sw600 folders to sw 360 - // -> - // to shift symbols label should be ~ [ < - // last symbols row should be \ = * " ' : ; ! ? (but is * " ' : ; ! ? ! ?) - // last shift symbols row should have inverted ! and ? - // some different label flags - // ar: last symbols row should be \ = * " ' : ; ! ؟ (but is * « » : ; ! ؟ ! ?) - // ar: layout should not have ! and ? added (just empty space here...) - // ru, sr (both), others don't have a right shift key (come on...) - // but bulgarian (default) has -> not even per language - // armenian (and probably other 4 row layouts) messed up (delete key should be in first for, not x from bottom) val keyParams = row[index1] if (keyParams.mLabel != xmlParams.mLabel) // currency keys (shift symbol) arranged differently 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 a76657ba7..5fe748fed 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 @@ -5,6 +5,7 @@ import android.content.res.Resources import android.util.Log import android.view.inputmethod.EditorInfo import android.widget.Toast +import androidx.annotation.StringRes import org.dslul.openboard.inputmethod.keyboard.Key import org.dslul.openboard.inputmethod.keyboard.Key.KeyParams import org.dslul.openboard.inputmethod.keyboard.KeyboardId @@ -12,7 +13,6 @@ 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.toTextKey import org.dslul.openboard.inputmethod.latin.R import org.dslul.openboard.inputmethod.latin.common.Constants import org.dslul.openboard.inputmethod.latin.common.splitOnWhitespace @@ -75,28 +75,29 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co Toast.makeText(context, message, Toast.LENGTH_LONG).show() } } - val functionalKeysReversed = parseFunctionalKeys().reversed() + 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) { + val row: List = if (i == 0 && isTablet()) { // add bottom row extra keys - // todo: question mark might be different -> get it from localeKeyTexts - // also, maybe check device dimension int instead of getting this from resources, then using language labels is easier - // and in shift symbols it should be inverted question/exclamation marks - it + context.getString(R.string.key_def_extra_bottom_right) - .split(",").mapNotNull { if (it.isBlank()) null else it.trim().toTextKey(labelFlags = Key.LABEL_FLAGS_FONT_DEFAULT) } + val tabletExtraKeys = params.mLocaleKeyTexts.getTabletExtraKeys(params.mId.mElementId) + tabletExtraKeys.first + it + tabletExtraKeys.second } else { it } // parse functional keys for this row (if any) + val outerFunctionalKeyDefs = if (i == baseKeys.lastIndex && functionalKeysTop.isNotEmpty()) functionalKeysTop.first() + else emptyList() to emptyList() val functionalKeysDefs = if (i < functionalKeysReversed.size) functionalKeysReversed[i] else emptyList() to emptyList() - val functionalKeysLeft = functionalKeysDefs.first.map { getFunctionalKeyParams(it) } - val functionalKeysRight = functionalKeysDefs.second.map { getFunctionalKeyParams(it) } + // 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 @@ -152,7 +153,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co // 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 { key -> key.forEach { it.mRelativeHeight *= heightRescale } } + keysInRows.forEach { row -> row.forEach { it.mRelativeHeight *= heightRescale } } } return keysInRows @@ -184,8 +185,8 @@ 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 parseFunctionalKeys(): List, List>> = - context.getString(R.string.key_def_functional).split("\n").mapNotNull { line -> + 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()) @@ -491,6 +492,17 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co moreKeys.add("$replacementText|${iconPrefixRemoved.substringAfter("|")}") } } + // 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() && moreKeys.remove("!icon/emoji_action_key|!code/key_emoji")) { + val i = moreKeys.indexOfFirst { it.startsWith("!fixedColumnOrder") } + if (i > -1) { + val n = moreKeys[i].substringAfter("!fixedColumnOrder!").toIntOrNull() + if (n != null) + moreKeys[i] = moreKeys[i].replace(n.toString(), (n - 1).toString()) + } + // remove emoji on enter, because tablet layout has a separate emoji key + } return moreKeys.toTypedArray() } @@ -531,7 +543,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co if (elementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) return params.mLocaleKeyTexts.labelSymbol if (elementId == KeyboardId.ELEMENT_SYMBOLS) - return params.mLocaleKeyTexts.labelShiftSymbol + return params.mLocaleKeyTexts.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" @@ -569,8 +581,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co for (i in moreKeys.indices) moreKeys[i] = moreKeys[i].rtlLabel(params) // for parentheses } - if (context.resources.getInteger(R.integer.config_screen_metrics) >= 3 && moreKeys.contains("!") && moreKeys.contains("?")) { - // we have a tablet, remove ! and ? keys and reduce number in autoColumnOrder + if (isTablet() && moreKeys.contains("!") && moreKeys.contains("?")) { + // remove ! and ? keys and reduce number in autoColumnOrder // this makes use of removal of empty moreKeys in MoreKeySpec.insertAdditionalMoreKeys moreKeys[moreKeys.indexOf("!")] = "" moreKeys[moreKeys.indexOf("?")] = "" @@ -581,6 +593,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co return moreKeys } + private fun isTablet() = context.resources.getInteger(R.integer.config_screen_metrics) >= 3 + companion object { private val TAG = KeyboardParser::class.simpleName diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/LocaleKeyTexts.kt b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/LocaleKeyTexts.kt index 88a45bb17..59a04247a 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/LocaleKeyTexts.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/internal/keyboard_parser/LocaleKeyTexts.kt @@ -3,6 +3,7 @@ package org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser import android.content.Context import org.dslul.openboard.inputmethod.keyboard.Key +import org.dslul.openboard.inputmethod.keyboard.KeyboardId 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.toTextKey @@ -20,12 +21,13 @@ class LocaleKeyTexts(dataStream: InputStream?, locale: Locale) { private set var labelAlphabet = "ABC" private set - var labelShiftSymbol = "= \\\\ <" - private set + private var labelShiftSymbol = "= \\\\ <" + private var labelShiftSymbolTablet = "~ [ <" var labelComma = "," private set var labelPeriod = "." private set + private var labelQuestion = "?" val currencyKey = getCurrencyKey(locale) private var numberKeys = ((1..9) + 0).map { it.toString() } private val numbersMoreKeys = arrayOf( @@ -51,8 +53,8 @@ class LocaleKeyTexts(dataStream: InputStream?, locale: Locale) { moreKeys["\""] = arrayOf("!fixedColumnOrder!5", "„", "“", "”", "«", "»") if ("!" !in moreKeys) moreKeys["!"] = arrayOf("¡") - if ("?" !in moreKeys) - moreKeys["?"] = arrayOf("¿") + if (labelQuestion !in moreKeys) + moreKeys[labelQuestion] = if (labelQuestion == "?") arrayOf("¿") else arrayOf("?", "¿") if ("punctuation" !in moreKeys) moreKeys["punctuation"] = arrayOf("${Key.MORE_KEYS_AUTO_COLUMN_ORDER}8", "\\,", "?", "!", "#", ")", "(", "/", ";", "'", "@", ":", "-", "\"", "+", "\\%", "&") } @@ -80,6 +82,19 @@ class LocaleKeyTexts(dataStream: InputStream?, locale: Locale) { } } + /** Pair(extraKeysLeft, extraKeysRight) */ + // todo: they should be optional, or will unexpectedly appear on custom layouts + fun getTabletExtraKeys(elementId: Int): Pair, List> { + val flags = Key.LABEL_FLAGS_FONT_DEFAULT + return when (elementId) { + KeyboardId.ELEMENT_SYMBOLS -> listOf("\\".toTextKey(labelFlags = flags), "=".toTextKey(labelFlags = flags)) to emptyList() + KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> emptyList() to listOf("¡".toTextKey(labelFlags = flags), "¿".toTextKey(labelFlags = flags)) + else -> emptyList() to listOf("!".toTextKey(labelFlags = flags), labelQuestion.toTextKey(labelFlags = flags)) // assume alphabet + } + } + + fun getShiftSymbolLabel(isTablet: Boolean) = if (isTablet) labelShiftSymbolTablet else labelShiftSymbol + // need tp provide a copy because some functions like MoreKeySpec.insertAdditionalMoreKeys may modify the array fun getMoreKeys(label: String): Array? = moreKeys[label]?.copyOf() @@ -125,8 +140,10 @@ class LocaleKeyTexts(dataStream: InputStream?, locale: Locale) { "symbol" -> labelSymbol = split.last() "alphabet" -> labelAlphabet = split.last() "shift_symbol" -> labelShiftSymbol = split.last() // never used, but could be... + "shift_symbol_tablet" -> labelShiftSymbolTablet = split.last() // never used, but could be... "comma" -> labelComma = split.last() "period" -> labelPeriod = split.last() + "question" -> labelQuestion = split.last() } } diff --git a/app/src/main/res/values-sw600dp/functional-keys.xml b/app/src/main/res/values-sw600dp/functional-keys.xml index 90ba398d8..f44de015f 100644 --- a/app/src/main/res/values-sw600dp/functional-keys.xml +++ b/app/src/main/res/values-sw600dp/functional-keys.xml @@ -1,8 +1,8 @@ - ";delete 10% + " ;action 10% shift 10%; shift" - "!, ?" + ";delete 10%" "symbol, 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 index 5037e5b8b..285039d6b 100644 --- a/app/src/main/res/values/functional-keys.xml +++ b/app/src/main/res/values/functional-keys.xml @@ -5,12 +5,14 @@ the left side, keys after are on the right side. Use comma to separate keys on the same side. Valid key names are at the bottom of SimpleLayoutParser (todo: location may change) Width (in percent of keyboard width) can be appended to the key name, defaults to default key width + Applied from bottom --> "shift 15%; delete 15%" - "" + ""