add new parser for emoji arrays

This commit is contained in:
Helium314 2023-12-10 10:49:05 +01:00
parent 4406f1b224
commit 615fde1a7b
4 changed files with 168 additions and 17 deletions

View file

@ -1326,6 +1326,47 @@ public class Key implements Comparable<Key> {
mEnabled = true;
}
/** constructor for emoji parser */ // essentially the same as the GridRows constructor, but without coordinates and outputText
public KeyParams(@Nullable final String label, final int code, @Nullable final String hintLabel,
@Nullable final String moreKeySpecs, final int labelFlags, final KeyboardParams params) {
mKeyboardParams = params;
mHintLabel = hintLabel;
mLabelFlags = labelFlags;
mBackgroundType = BACKGROUND_TYPE_EMPTY;
if (moreKeySpecs != null) {
String[] moreKeys = MoreKeySpec.splitKeySpecs(moreKeySpecs);
mMoreKeysColumnAndFlags = getMoreKeysColumnAndFlagsAndSetNullInArray(params, moreKeys);
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, null);
int actionFlags = 0;
if (moreKeys != null) {
actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
mMoreKeys = new MoreKeySpec[moreKeys.length];
for (int i = 0; i < moreKeys.length; i++) {
mMoreKeys[i] = new MoreKeySpec(moreKeys[i], false, Locale.getDefault());
}
} else {
mMoreKeys = null;
}
mActionFlags = actionFlags;
} else {
// TODO: Pass keyActionFlags as an argument.
mActionFlags = ACTION_FLAGS_NO_KEY_PREVIEW;
mMoreKeys = null;
mMoreKeysColumnAndFlags = 0;
}
mLabel = label;
mOptionalAttributes = code == Constants.CODE_OUTPUT_TEXT
? OptionalAttributes.newInstance(label, CODE_UNSPECIFIED, ICON_UNDEFINED, 0, 0)
: null;
mCode = code;
mEnabled = (code != CODE_UNSPECIFIED);
mIconId = KeyboardIconsSet.ICON_UNDEFINED;
mKeyVisualAttributes = null;
}
/** constructor for <GridRows/> */
public KeyParams(@Nullable final String label, final int code, @Nullable final String outputText,
@Nullable final String hintLabel, @Nullable final String moreKeySpecs,

View file

@ -6,6 +6,8 @@
package org.dslul.openboard.inputmethod.keyboard.emoji;
import static org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.EmojiParserKt.EMOJI_HINT_LABEL;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Log;
@ -115,7 +117,7 @@ final class DynamicGridKeyboard extends Keyboard {
// if key comes from another keyboard (ie. a {@link MoreKeysKeyboard}).
final boolean dropMoreKeys = mIsRecents;
// Check if hint was a more emoji indicator and prevent its copy if more keys aren't copied
final boolean dropHintLabel = dropMoreKeys && "\u25E5".equals(usedKey.getHintLabel());
final boolean dropHintLabel = dropMoreKeys && EMOJI_HINT_LABEL.equals(usedKey.getHintLabel());
final GridKey key = new GridKey(usedKey,
dropMoreKeys ? null : usedKey.getMoreKeys(),
dropHintLabel ? null : usedKey.getHintLabel(),

View file

@ -16,7 +16,7 @@ import org.dslul.openboard.inputmethod.keyboard.Key
import org.dslul.openboard.inputmethod.keyboard.Key.KeyParams
import org.dslul.openboard.inputmethod.keyboard.Keyboard
import org.dslul.openboard.inputmethod.keyboard.KeyboardId
import org.dslul.openboard.inputmethod.keyboard.MoreKeysKeyboard
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.EmojiParser
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.KeyboardParser
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.XmlKeyboardParser
import org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams
@ -25,7 +25,6 @@ import org.dslul.openboard.inputmethod.latin.R
import org.dslul.openboard.inputmethod.latin.common.Constants
import org.dslul.openboard.inputmethod.latin.define.DebugFlags
import org.dslul.openboard.inputmethod.latin.settings.Settings
import org.dslul.openboard.inputmethod.latin.suggestions.MoreSuggestions
import org.dslul.openboard.inputmethod.latin.utils.sumOf
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
@ -68,18 +67,6 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
return this
// todo: further plan
// 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
// -> add the option, but disable it by default for all layouts
// migrate emoji layouts to this style
// emojis are defined in that string array, should be simple to handle
// parsing could be done into a single row, which is then split as needed
// this might help with split layout (no change in key size, but in number of rows)
// 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?)
@ -165,6 +152,12 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
readAttributes(xmlId)
return this
}
if (id.mElementId >= KeyboardId.ELEMENT_EMOJI_RECENTS && id.mElementId <= KeyboardId.ELEMENT_EMOJI_CATEGORY16) {
mParams.mId = id
readAttributes(R.xml.kbd_emoji_category1) // all the same anyway, gridRows are ignored
keysInRows = EmojiParser(mParams, mContext).parse(Settings.getInstance().current.mIsSplitKeyboardEnabled)
return this
}
if (loadFromAssets(id) != null) {
if (!DebugFlags.DEBUG_ENABLED)
return this
@ -242,8 +235,7 @@ open class KeyboardBuilder<KP : KeyboardParams>(protected val mContext: Context,
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()
Toast.makeText(mContext, "using old parser for $id", Toast.LENGTH_LONG).show()
}
}
mParams.mId = id

View file

@ -0,0 +1,116 @@
package org.dslul.openboard.inputmethod.keyboard.internal.keyboard_parser
import android.content.Context
import android.os.Build
import org.dslul.openboard.inputmethod.keyboard.Key
import org.dslul.openboard.inputmethod.keyboard.Key.KeyParams
import org.dslul.openboard.inputmethod.keyboard.KeyboardId
import org.dslul.openboard.inputmethod.keyboard.internal.KeyboardParams
import org.dslul.openboard.inputmethod.latin.R
import org.dslul.openboard.inputmethod.latin.common.Constants
import org.dslul.openboard.inputmethod.latin.common.StringUtils
class EmojiParser(private val params: KeyboardParams, private val context: Context) {
fun parse(splitKeyboard: Boolean): ArrayList<ArrayList<KeyParams>> { // todo: split should be read from params, but currently this is disabled, right?
val emojiArrayId = when (params.mId.mElementId) {
KeyboardId.ELEMENT_EMOJI_RECENTS -> R.array.emoji_recents
KeyboardId.ELEMENT_EMOJI_CATEGORY1 -> R.array.emoji_smileys_emotion
KeyboardId.ELEMENT_EMOJI_CATEGORY2 -> R.array.emoji_people_body
KeyboardId.ELEMENT_EMOJI_CATEGORY3 -> R.array.emoji_animals_nature
KeyboardId.ELEMENT_EMOJI_CATEGORY4 -> R.array.emoji_food_drink
KeyboardId.ELEMENT_EMOJI_CATEGORY5 -> R.array.emoji_travel_places
KeyboardId.ELEMENT_EMOJI_CATEGORY6 -> R.array.emoji_activities
KeyboardId.ELEMENT_EMOJI_CATEGORY7 -> R.array.emoji_objects
KeyboardId.ELEMENT_EMOJI_CATEGORY8 -> R.array.emoji_symbols
KeyboardId.ELEMENT_EMOJI_CATEGORY9 -> R.array.emoji_flags
KeyboardId.ELEMENT_EMOJI_CATEGORY10 -> R.array.emoji_emoticons
else -> throw(IllegalStateException("can only parse emoji categories where an array exists"))
}
val emojiArray = context.resources.getStringArray(emojiArrayId)
val moreEmojisArray = if (params.mId.mElementId == KeyboardId.ELEMENT_EMOJI_CATEGORY2)
context.resources.getStringArray(R.array.emoji_people_body_more)
else null
if (moreEmojisArray != null && emojiArray.size != moreEmojisArray.size)
throw(IllegalStateException("Inconsistent array size between codesArray and moreKeysArray"))
// now we have the params in one long list -> split into lines and maybe add spacer
// todo: disabled, because it doesn't work properly... spacer keys get added to the end every 3 rows
// the sorting and sizing seems to be done in DynamicGridKeyboard
// only the template keys there are relevant for dimensions, resizing keys here doesn't have any effect
// -> this is really weird and unexpected, and should be changed (might also help with the text emojis...)
/* val numColumns = (1 / params.mDefaultRelativeKeyWidth).toInt()
val spacerNumKeys: Int
val spacerWidth: Float
if (splitKeyboard) {
val spacerRelativeWidth = Settings.getInstance().current.mSpacerRelativeWidth
// adjust gaps for the whole keyboard, so it's the same for all rows
params.mRelativeHorizontalGap *= 1f / (1f + spacerRelativeWidth)
params.mHorizontalGap = (params.mRelativeHorizontalGap * params.mId.mWidth).toInt()
// round the spacer width, so it's a number of keys, and number should be even if emoji count is even, odd otherwise
spacerNumKeys = (spacerRelativeWidth / params.mDefaultRelativeKeyWidth).roundTo(numColumns % 2 == 0)
spacerWidth = spacerNumKeys * params.mDefaultRelativeKeyWidth
} else {
spacerNumKeys = 0
spacerWidth = 0f
}
val spacerIndex = if (spacerNumKeys > 0) (numColumns - spacerNumKeys) / 2 else -1
*/
val row = ArrayList<KeyParams>(emojiArray.size)
var currentX = params.mLeftPadding.toFloat()
val currentY = params.mTopPadding.toFloat()
emojiArray.forEachIndexed { i, codeArraySpec ->
val keyParams = parseEmojiKey(codeArraySpec, moreEmojisArray?.get(i)?.takeIf { it.isNotEmpty() }) ?: return@forEachIndexed
keyParams.setDimensionsFromRelativeSize(currentX, currentY)
currentX += keyParams.mFullWidth // exact value seems to be not really relevant, but keeping 0 doesn't work
row.add(keyParams)
// if (row.size % numColumns == spacerIndex) { // also removed for now (would be missing setting the size and updating x
// repeat(spacerNumKeys) { row.add(KeyParams.newSpacer(params, params.mDefaultRelativeKeyWidth)) }
// }
}
return arrayListOf(row)
}
// private fun Float.roundTo(even: Boolean) = if (toInt() % 2 == if (even) 0 else 1) toInt() else toInt() + 1
private fun getLabelAndCode(spec: String): Pair<String, Int>? {
val specAndSdk = spec.split("||")
if (specAndSdk.getOrNull(1)?.toIntOrNull()?.let { it > Build.VERSION.SDK_INT } == true) return null
if ("," !in specAndSdk.first()) {
val code = specAndSdk.first().toIntOrNull(16) ?: return specAndSdk.first() to Constants.CODE_OUTPUT_TEXT // text emojis
val label = StringUtils.newSingleCodePointString(code)
return label to code
}
val labelBuilder = StringBuilder()
for (codePointString in specAndSdk.first().split(",")) {
val cp = codePointString.toInt(16)
labelBuilder.appendCodePoint(cp)
}
return labelBuilder.toString() to Constants.CODE_OUTPUT_TEXT
}
private fun parseEmojiKey(spec: String, moreKeysString: String? = null): Key.KeyParams? {
val (label, code) = getLabelAndCode(spec) ?: return null
val sb = StringBuilder()
moreKeysString?.split(";")?.let { moreKeys ->
moreKeys.forEach {
val (mkLabel, _) = getLabelAndCode(it) ?: return@forEach
sb.append(mkLabel).append(",")
}
}
val moreKeysSpec = if (sb.isNotEmpty()) {
sb.deleteCharAt(sb.length - 1)
sb.toString()
} else null
return KeyParams(
label,
code,
if (moreKeysSpec != null) EMOJI_HINT_LABEL else null,
moreKeysSpec,
Key.LABEL_FLAGS_FONT_NORMAL,
params
)
}
}
const val EMOJI_HINT_LABEL = ""