From 3eb2f45d9791b0e038607e1811851070fb62013c Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sun, 6 Jul 2025 04:23:40 +0300
Subject: [PATCH 01/56] Add emoji search
---
app/build.gradle.kts | 2 +-
app/src/main/AndroidManifest.xml | 5 +
.../emoji_bottom/emoji_bottom_row.json | 1 +
.../accessibility/KeyCodeDescriptionMapper.kt | 1 +
.../java/helium314/keyboard/keyboard/Key.java | 3 +-
.../keyboard/keyboard/KeyboardId.java | 2 +-
.../keyboard/keyboard/KeyboardSwitcher.java | 6 +-
.../keyboard/emoji/DynamicGridKeyboard.java | 15 +-
.../keyboard/emoji/EmojiCategory.java | 8 +-
.../keyboard/emoji/EmojiPageKeyboardView.java | 15 +-
.../keyboard/emoji/EmojiPalettesView.java | 4 +-
.../keyboard/emoji/EmojiSearchActivity.kt | 325 ++++++++++++++++++
.../keyboard/internal/KeyboardCodesSet.java | 6 +-
.../internal/keyboard_parser/EmojiParser.kt | 50 ++-
.../keyboard_parser/floris/KeyCode.kt | 4 +-
.../keyboard_parser/floris/KeyLabel.kt | 2 +
.../keyboard_parser/floris/TextKeyData.kt | 2 +-
.../helium314/keyboard/latin/LatinIME.java | 38 +-
.../latin/SingleDictionaryFacilitator.kt | 26 +-
.../keyboard/latin/common/Constants.java | 1 +
.../keyboard/latin/inputlogic/InputLogic.java | 10 +
.../latin/suggestions/SuggestionStripView.kt | 5 +-
.../latin/utils/SuggestionResults.java | 3 +-
app/src/main/res/layout/input_view.xml | 3 +-
app/src/main/res/values-land/config.xml | 2 +-
.../main/res/values-sw600dp-land/config.xml | 2 +-
app/src/main/res/values-sw600dp/config.xml | 2 +-
.../main/res/values-sw768dp-land/config.xml | 2 +-
app/src/main/res/values-sw768dp/config.xml | 2 +-
app/src/main/res/values/config.xml | 2 +-
app/src/main/res/values/strings.xml | 4 +
app/src/main/res/values/themes-common.xml | 5 +
32 files changed, 502 insertions(+), 56 deletions(-)
create mode 100644 app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 97bad787c5..7f38a5b4b9 100755
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -114,7 +114,7 @@ dependencies {
// compose
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
- implementation(platform("androidx.compose:compose-bom:2025.05.00"))
+ implementation(platform("androidx.compose:compose-bom-beta:2025.06.01"))
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a7dcab1e62..4ebab05c10 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -76,6 +76,11 @@ SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
+
+
diff --git a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json
index 2a7c8f6b17..27d048e46c 100644
--- a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json
+++ b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json
@@ -1,6 +1,7 @@
[
[
{ "label": "alpha", "width": 0.15 },
+ { "label": "search", "width": 0.15 },
{ "label": "space", "width": -1 },
{ "label": "delete", "width": 0.15 }
]
diff --git a/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt b/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt
index cd1841ce67..50060b0db4 100644
--- a/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt
+++ b/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt
@@ -35,6 +35,7 @@ internal class KeyCodeDescriptionMapper private constructor() {
put(KeyCode.ACTION_NEXT, R.string.spoken_description_action_next)
put(KeyCode.ACTION_PREVIOUS, R.string.spoken_description_action_previous)
put(KeyCode.EMOJI, R.string.spoken_description_emoji)
+ put(KeyCode.SEARCH, R.string.spoken_description_search)
// Because the upper-case and lower-case mappings of the following letters is depending on
// the locale, the upper case descriptions should be defined here. The lower case
// descriptions are handled in {@link #getSpokenLetterDescriptionId(Context,int)}.
diff --git a/app/src/main/java/helium314/keyboard/keyboard/Key.java b/app/src/main/java/helium314/keyboard/keyboard/Key.java
index 5b4c8c5d9c..9261007260 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/Key.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/Key.java
@@ -1174,7 +1174,8 @@ public KeyParams(
// fallthrough
case KeyCode.SHIFT, Constants.CODE_ENTER, KeyCode.SHIFT_ENTER, KeyCode.ALPHA, Constants.CODE_SPACE, KeyCode.NUMPAD,
KeyCode.SYMBOL, KeyCode.SYMBOL_ALPHA, KeyCode.LANGUAGE_SWITCH, KeyCode.EMOJI, KeyCode.CLIPBOARD,
- KeyCode.MOVE_START_OF_LINE, KeyCode.MOVE_END_OF_LINE, KeyCode.MOVE_START_OF_PAGE, KeyCode.MOVE_END_OF_PAGE:
+ KeyCode.MOVE_START_OF_LINE, KeyCode.MOVE_END_OF_LINE, KeyCode.MOVE_START_OF_PAGE, KeyCode.MOVE_END_OF_PAGE,
+ KeyCode.SEARCH:
actionFlags |= ACTION_FLAGS_NO_KEY_PREVIEW; // no preview even if icon!
}
if (mCode == KeyCode.SETTINGS || mCode == KeyCode.LANGUAGE_SWITCH)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java
index 62523f488b..9fe3fe385f 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java
@@ -60,7 +60,7 @@ public final class KeyboardId {
public static final int ELEMENT_EMOJI_CATEGORY13 = 23;
public static final int ELEMENT_EMOJI_CATEGORY14 = 24;
public static final int ELEMENT_EMOJI_CATEGORY15 = 25;
- public static final int ELEMENT_EMOJI_CATEGORY16 = 26;
+ public static final int ELEMENT_EMOJI_CATEGORY16 = 26; // Emoji search
public static final int ELEMENT_CLIPBOARD = 27;
public static final int ELEMENT_NUMPAD = 28;
public static final int ELEMENT_EMOJI_BOTTOM_ROW = 29;
diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java
index 34330ad48c..cf662f2c54 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java
@@ -309,7 +309,7 @@ private void setMainKeyboardFrame(
@NonNull final SettingsValues settingsValues,
@NonNull final KeyboardSwitchState toggleState) {
final int visibility = isImeSuppressedByHardwareKeyboard(settingsValues, toggleState) ? View.GONE : View.VISIBLE;
- final int stripVisibility = settingsValues.mToolbarMode == ToolbarMode.HIDDEN ? View.GONE : View.VISIBLE;
+ final int stripVisibility = settingsValues.mToolbarMode == ToolbarMode.HIDDEN || mLatinIME.isEmojiSearch()? View.GONE : View.VISIBLE;
mStripContainer.setVisibility(stripVisibility);
PointerTracker.switchTo(mKeyboardView);
mKeyboardView.setVisibility(visibility);
@@ -632,6 +632,10 @@ public boolean isShowingStripContainer() {
return mStripContainer.isShown();
}
+ public EmojiPalettesView getEmojiPalettesView() {
+ return mEmojiPalettesView;
+ }
+
public View getVisibleKeyboardView() {
if (isShowingEmojiPalettes()) {
return mEmojiPalettesView;
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 eea566a720..286174e497 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java
@@ -51,7 +51,7 @@ final class DynamicGridKeyboard extends Keyboard {
private final ArrayList mEmptyColumnIndices = new ArrayList<>(4);
public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
- final int maxKeyCount, final int categoryId, final int width) {
+ final int maxRowCount, final int categoryId, final int width) {
super(templateKeyboard);
// todo: would be better to keep them final and not require width, but how to properly set width of the template keyboard?
// an alternative would be to always create the templateKeyboard with full width
@@ -69,7 +69,7 @@ public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templat
mColumnsNum = mBaseWidth / mHorizontalStep;
if (spacerWidth > 0)
setSpacerColumns(spacerWidth);
- mMaxKeyCount = maxKeyCount;
+ mMaxKeyCount = maxRowCount * getOccupiedColumnCount();
mIsRecents = categoryId == EmojiCategory.ID_RECENTS;
mPrefs = prefs;
}
@@ -111,8 +111,8 @@ private Key getTemplateKey(final int code) {
throw new RuntimeException("Can't find template key: code=" + code);
}
- public int getDynamicOccupiedHeight() {
- final int row = (mGridKeys.size() - 1) / getOccupiedColumnCount() + 1;
+ int getOccupiedHeight() {
+ final int row = (mMaxKeyCount - 1) / getOccupiedColumnCount() + 1;
return row * mVerticalStep;
}
@@ -146,6 +146,13 @@ public void addKeyLast(final Key usedKey) {
addKey(usedKey, false);
}
+ public void removeAllKeys() {
+ synchronized (mLock) {
+ mGridKeys.clear();
+ mCachedGridKeys = null;
+ }
+ }
+
private void addKey(final Key usedKey, final boolean addFirst) {
if (usedKey == null) {
return;
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java
index 413a6b8f62..fd89e7b4d4 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java
@@ -119,7 +119,7 @@ public int getPageCount() {
private final SharedPreferences mPrefs;
private final Resources mRes;
private final Context mContext;
- private final int mMaxRecentsKeyCount;
+ private final int mMaxRecentsRowCount;
private final KeyboardLayoutSet mLayoutSet;
private final HashMap mCategoryNameToIdMap = new HashMap<>();
private final int[] mCategoryTabIconId = new int[sCategoryName.length];
@@ -133,7 +133,7 @@ public EmojiCategory(final Context ctx, final KeyboardLayoutSet layoutSet, final
mPrefs = KtxKt.prefs(ctx);
mRes = ctx.getResources();
mContext = ctx;
- mMaxRecentsKeyCount = mRes.getInteger(R.integer.config_emoji_keyboard_max_recents_key_count);
+ mMaxRecentsRowCount = mRes.getInteger(R.integer.config_emoji_keyboard_max_recents_row_count);
mLayoutSet = layoutSet;
for (int i = 0; i < sCategoryName.length; ++i) {
mCategoryNameToIdMap.put(sCategoryName[i], i);
@@ -294,7 +294,7 @@ public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
if (categoryId == EmojiCategory.ID_RECENTS) {
final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
- mMaxRecentsKeyCount, categoryId, currentWidth);
+ mMaxRecentsRowCount, categoryId, currentWidth);
mCategoryKeyboardMap.put(categoryKeyboardMapKey, kbd);
kbd.loadRecentKeys(mCategoryKeyboardMap.values());
return kbd;
@@ -307,7 +307,7 @@ public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
for (int pageId = 0; pageId < sortedKeysPages.length; ++pageId) {
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
- keyCountPerPage, categoryId, currentWidth);
+ MAX_LINE_COUNT_PER_PAGE, categoryId, currentWidth);
for (final Key emojiKey : sortedKeysPages[pageId]) {
if (emojiKey == null) {
break;
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java
index cc1e0a359d..4227263738 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPageKeyboardView.java
@@ -113,6 +113,7 @@ public EmojiPageKeyboardView(final Context context, final AttributeSet attrs,
mPopupKeysKeyboardContainer = inflater.inflate(popupKeysKeyboardLayoutId, null);
mDescriptionView = mPopupKeysKeyboardContainer.findViewById(R.id.description_view);
mPopupKeysKeyboardView = mPopupKeysKeyboardContainer.findViewById(R.id.popup_keys_keyboard_view);
+ setFitsSystemWindows(false);
}
@Override
@@ -120,7 +121,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final Keyboard keyboard = getKeyboard();
if (keyboard instanceof DynamicGridKeyboard) {
final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
- final int occupiedHeight = ((DynamicGridKeyboard) keyboard).getDynamicOccupiedHeight();
+ final int occupiedHeight = ((DynamicGridKeyboard) keyboard).getOccupiedHeight();
final int height = occupiedHeight + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(width, height);
return;
@@ -442,23 +443,25 @@ public boolean onMove(final MotionEvent e) {
final int x = (int)e.getX();
final int y = (int)e.getY();
final Key key = getKey(x, y);
- final boolean isShowingPopupKeysPanel = isShowingPopupKeysPanel();
+ final boolean isShowingPopupKeyboard = isShowingPopupKeysPanel() && mPopupKeysKeyboardView.getVisibility() == VISIBLE;
// Touched key has changed, release previous key's callbacks and
// re-register them for the new key.
- if (key != mCurrentKey && !isShowingPopupKeysPanel) {
+ if (key != mCurrentKey && !isShowingPopupKeyboard) {
releaseCurrentKey(false);
mCurrentKey = key;
+ cancelLongPress();
+ if (isShowingPopupKeysPanel()) {
+ onCancelPopupKeysPanel();
+ }
if (key == null) {
return false;
}
registerPress(key);
-
- cancelLongPress();
registerLongPress(key);
}
- if (isShowingPopupKeysPanel) {
+ if (isShowingPopupKeyboard) {
final long eventTime = e.getEventTime();
final int translatedX = mPopupKeysPanel.translateX(x);
final int translatedY = mPopupKeysPanel.translateY(y);
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 c29d9280da..36fe561f39 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
@@ -341,12 +341,12 @@ public void startEmojiPalettes(final KeyVisualAttributes keyVisualAttr,
initDictionaryFacilitator();
}
- private void addRecentKey(final Key key) {
+ public void addRecentKey(final Key key) {
if (Settings.getValues().mIncognitoModeEnabled) {
// We do not want to log recent keys while being in incognito
return;
}
- if (mEmojiCategory.isInRecentTab()) {
+ if (getVisibility() == VISIBLE && mEmojiCategory.isInRecentTab()) {
getRecentsKeyboard().addPendingKey(key);
return;
}
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
new file mode 100644
index 0000000000..3a44969972
--- /dev/null
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -0,0 +1,325 @@
+package helium314.keyboard.keyboard.emoji
+
+import android.R.string.cancel
+import android.content.Context
+import android.content.Intent
+import android.content.res.Configuration
+import android.os.Bundle
+import android.view.ContextThemeWrapper
+import android.view.ViewGroup
+import android.view.inputmethod.EditorInfo
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.exclude
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.isImeVisible
+import androidx.compose.foundation.layout.safeDrawing
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.PlatformImeOptions
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.text.style.TextDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.viewinterop.AndroidView
+import helium314.keyboard.keyboard.Key
+import helium314.keyboard.keyboard.Key.KeyParams
+import helium314.keyboard.keyboard.KeyboardId
+import helium314.keyboard.keyboard.KeyboardLayoutSet
+import helium314.keyboard.keyboard.KeyboardSwitcher
+import helium314.keyboard.keyboard.KeyboardTheme
+import helium314.keyboard.keyboard.internal.KeyboardBuilder
+import helium314.keyboard.keyboard.internal.KeyboardParams
+import helium314.keyboard.keyboard.internal.keyboard_parser.EMOJI_HINT_LABEL
+import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
+import helium314.keyboard.keyboard.internal.keyboard_parser.getCode
+import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiKeyDimensions
+import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiPopupSpec
+import helium314.keyboard.latin.Dictionary
+import helium314.keyboard.latin.DictionaryFactory
+import helium314.keyboard.latin.LatinIME
+import helium314.keyboard.latin.R
+import helium314.keyboard.latin.RichInputMethodManager
+import helium314.keyboard.latin.RichInputMethodSubtype
+import helium314.keyboard.latin.SingleDictionaryFacilitator
+import helium314.keyboard.latin.common.ColorType
+import helium314.keyboard.latin.common.StringUtils
+import helium314.keyboard.latin.common.splitOnWhitespace
+import helium314.keyboard.latin.settings.Settings
+import helium314.keyboard.latin.utils.DictionaryInfoUtils
+import helium314.keyboard.latin.utils.ResourceUtils
+import helium314.keyboard.latin.utils.prefs
+import helium314.keyboard.settings.CloseIcon
+import helium314.keyboard.settings.SearchIcon
+
+/**
+ * This activity is displayed in a gap created for it above the keyboard and below the host app, and partly obscures the host app.
+ */
+class EmojiSearchActivity : ComponentActivity() {
+ private var startup: Boolean = true
+ private var emojiPageKeyboardView: EmojiPageKeyboardView? = null
+ private var keyboardParams: KeyboardParams? = null
+ private var keyWidth: Float? = null
+ private var keyHeight: Float? = null
+ private var pressedKey: Key? = null
+ private var imeClosed: Boolean = false
+
+ @OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ init()
+ enableEdgeToEdge()
+ setContent {
+ Surface(modifier = Modifier.fillMaxSize(), color = Color(0x80000000)) {
+ val imeVisible = WindowInsets.isImeVisible
+ val localDensity = LocalDensity.current
+ var heightPx by remember {
+ mutableIntStateOf(0)
+ }
+ var heightDp by remember {
+ mutableStateOf(0.dp)
+ }
+ Column(
+ modifier = Modifier.fillMaxSize()
+ .windowInsetsPadding(WindowInsets.safeDrawing.exclude(WindowInsets(bottom = heightDp))),
+ verticalArrangement = Arrangement.Bottom
+ ) {
+ Column(modifier = Modifier.wrapContentHeight().onGloballyPositioned {
+ if (!startup && !imeVisible) {
+ imeClosed = true
+ cancel()
+ return@onGloballyPositioned
+ }
+ if (!startup && (KeyboardSwitcher.getInstance().isShowingEmojiPalettes
+ || KeyboardSwitcher.getInstance().isShowingClipboardHistory)) {
+ cancel()
+ return@onGloballyPositioned
+ }
+ heightPx = it.size.height
+ heightDp = with(localDensity) { it.size.height.toDp() }
+ }) {
+ Row(modifier = Modifier.background(Color.White).fillMaxWidth().height(30.dp)) {
+ IconButton(onClick = { cancel() }) {
+ Icon(
+ painterResource(R.drawable.ic_arrow_back),
+ stringResource(R.string.spoken_description_action_previous)
+ )
+ }
+ Text(
+ text = stringResource(R.string.emoji_search_title), fontSize = 18.sp,
+ modifier = Modifier.fillMaxWidth().align(Alignment.CenterVertically)
+ )
+ }
+ AndroidView({ emojiPageKeyboardView!! }, modifier = Modifier.wrapContentHeight().fillMaxWidth())
+ val focusRequester = remember { FocusRequester() }
+ var text by remember { mutableStateOf(TextFieldValue(searchText, selection = TextRange(searchText.length))) }
+ BasicTextField(
+ value = text,
+ modifier = Modifier.fillMaxWidth().heightIn(20.dp, 30.dp).focusRequester(focusRequester),
+ textStyle = TextStyle(textDirection = TextDirection.Content),
+ onValueChange = { it: TextFieldValue ->
+ text = it
+ search(it.text)
+ },
+ enabled = true,
+ keyboardOptions = KeyboardOptions(
+ imeAction = ImeAction.Done,
+ platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))
+ ),
+ keyboardActions = KeyboardActions(onDone = { finish() }),
+ singleLine = true,
+ ) {
+ TextFieldDefaults.DecorationBox(
+ value = text.text,
+ contentPadding = PaddingValues(0.dp),
+ visualTransformation = VisualTransformation.None,
+ innerTextField = it,
+ placeholder = { Text(stringResource(R.string.search_field_placeholder)) },
+ leadingIcon = { SearchIcon() },
+ trailingIcon = {
+ IconButton(onClick = {
+ text = TextFieldValue()
+ search("")
+ }) { CloseIcon(cancel) }
+ },
+ singleLine = true,
+ enabled = true,
+ interactionSource = MutableInteractionSource(),
+ )
+ }
+ LaunchedEffect(Unit) { focusRequester.requestFocus() }
+ }
+ }
+ }
+ }
+ }
+
+ override fun onEnterAnimationComplete() {
+ search(searchText)
+ }
+
+ override fun onStop() {
+ val intent = Intent(this, LatinIME::class.java).setAction(EMOJI_SEARCH_DONE_ACTION)
+ intent.putExtra(IME_CLOSED_KEY, imeClosed)
+ if (pressedKey != null) {
+ intent.putExtra(
+ EMOJI_KEY, if (pressedKey!!.code == KeyCode.MULTIPLE_CODE_POINTS)
+ pressedKey!!.getOutputText()
+ else
+ Character.toString(pressedKey!!.code)
+ )
+
+ KeyboardSwitcher.getInstance().emojiPalettesView.addRecentKey(pressedKey)
+ }
+ startService(intent)
+ super.onStop()
+ }
+
+ private fun init() {
+ val contextThemeWrapper = ContextThemeWrapper(this, KeyboardTheme.getKeyboardTheme(this).mStyleId)
+ val keyboardWidth = ResourceUtils.getKeyboardWidth(contextThemeWrapper, Settings.getValues())
+ val layoutSet = KeyboardLayoutSet.Builder(contextThemeWrapper, null)
+ .setSubtype(RichInputMethodSubtype.emojiSubtype)
+ .setKeyboardGeometry(keyboardWidth, EmojiLayoutParams(contextThemeWrapper.resources).emojiKeyboardHeight).build()
+
+ // Initialize popup specs
+ layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_CATEGORY2)
+
+ val keyboard = DynamicGridKeyboard(contextThemeWrapper.prefs(), layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
+ if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) 1 else 2,
+ KeyboardId.ELEMENT_EMOJI_CATEGORY16, keyboardWidth)
+ val builder = KeyboardBuilder(contextThemeWrapper, KeyboardParams())
+ builder.load(keyboard.mId)
+ keyboardParams = builder.mParams
+ val (width, height) = getEmojiKeyDimensions(keyboardParams!!, contextThemeWrapper)
+ keyWidth = width
+ keyHeight = height
+ emojiPageKeyboardView = EmojiPageKeyboardView(contextThemeWrapper, null)
+ emojiPageKeyboardView!!.setKeyboard(keyboard)
+ emojiPageKeyboardView!!.layoutParams =
+ ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ emojiPageKeyboardView!!.background = null
+ Settings.getValues().mColors.setBackground(emojiPageKeyboardView!!, ColorType.MAIN_BACKGROUND)
+ emojiPageKeyboardView!!.setPadding(0, 10, 0, 10)
+
+ emojiPageKeyboardView!!.setEmojiViewCallback(object : EmojiViewCallback {
+ override fun onPressKey(key: Key) {
+ }
+
+ override fun onReleaseKey(key: Key) {
+ pressedKey = key
+ finish()
+ }
+
+ override fun getDescription(emoji: String): String? = if (Settings.getValues().mShowEmojiDescriptions)
+ dictionaryFacilitator?.getWordProperty(emoji)?.mShortcutTargets[0]?.mWord else null
+ })
+ }
+
+ fun search(text: String) {
+ searchText = text
+ initDictionaryFacilitator(this)
+ if (dictionaryFacilitator == null) {
+ cancel()
+ return
+ }
+
+ startup = false
+ val keyboard = emojiPageKeyboardView!!.keyboard as DynamicGridKeyboard
+ keyboard.removeAllKeys()
+ pressedKey = null
+ dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { StringUtils.mightBeEmoji(it.word) }.forEach {
+ val emoji = it.word
+ val popupSpec = getEmojiPopupSpec(emoji)
+ val keyParams = KeyParams(
+ emoji, emoji.getCode(), if (popupSpec != null) EMOJI_HINT_LABEL else null, popupSpec,
+ Key.LABEL_FLAGS_FONT_NORMAL, keyboardParams
+ )
+ keyParams.mAbsoluteWidth = keyWidth!!
+ keyParams.mAbsoluteHeight = keyHeight!!
+ val key = keyParams.createKey()
+ keyboard.addKeyLast(key)
+ if (pressedKey == null && Settings.getValues().mAutoCorrectEnabled)
+ pressedKey = key
+ }
+ emojiPageKeyboardView!!.invalidate()
+ }
+
+ private fun cancel() {
+ pressedKey = null
+ finish()
+ }
+
+ @JvmRecord
+ data class PrivateImeOptions(val height: Int)
+
+ companion object {
+ const val EMOJI_SEARCH_DONE_ACTION: String = "EMOJI_SEARCH_DONE"
+ const val IME_CLOSED_KEY: String = "IME_CLOSED"
+ const val EMOJI_KEY: String = "EMOJI"
+ private const val PRIVATE_IME_OPTIONS_PREFIX: String = "helium314.keyboard.keyboard.emoji.search"
+ private var dictionaryFacilitator: SingleDictionaryFacilitator? = null
+ private var searchText: String = ""
+
+ fun isSupported(context: Context): Boolean {
+ initDictionaryFacilitator(context)
+ return dictionaryFacilitator != null
+ }
+
+ fun decodePrivateImeOptions(editorInfo: EditorInfo?): PrivateImeOptions = PrivateImeOptions(
+ editorInfo?.privateImeOptions?.takeIf { it.startsWith(PRIVATE_IME_OPTIONS_PREFIX) }
+ ?.substring(PRIVATE_IME_OPTIONS_PREFIX.length + 1)?.toInt() ?: 0)
+
+ private fun encodePrivateImeOptions(privateImeOptions: PrivateImeOptions) =
+ "$PRIVATE_IME_OPTIONS_PREFIX,${privateImeOptions.height}"
+
+ private fun initDictionaryFacilitator(context: Context) {
+ val locale = RichInputMethodManager.getInstance().currentSubtype.locale
+ if (dictionaryFacilitator?.isForLocale(locale) != true) {
+ dictionaryFacilitator?.closeDictionaries()
+ dictionaryFacilitator = DictionaryInfoUtils.getCachedDictForLocaleAndType(locale, Dictionary.TYPE_EMOJI, context)
+ ?.let { DictionaryFactory.getDictionary(it, locale) }?.let { SingleDictionaryFacilitator(it) }
+ }
+ }
+ }
+}
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 7671784e05..8b8d0a993c 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardCodesSet.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardCodesSet.java
@@ -55,7 +55,8 @@ public static int getCode(final String name) {
"key_toggle_onehanded",
"key_start_onehanded", // keep name to avoid breaking custom layouts
"key_stop_onehanded", // keep name to avoid breaking custom layouts
- "key_switch_onehanded"
+ "key_switch_onehanded",
+ "key_search"
};
private static final int[] DEFAULT = {
@@ -81,7 +82,8 @@ public static int getCode(final String name) {
KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.TOGGLE_ONE_HANDED_MODE,
- KeyCode.SWITCH_ONE_HANDED_MODE
+ KeyCode.SWITCH_ONE_HANDED_MODE,
+ KeyCode.SEARCH
};
static {
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 f58a0b5225..30b01eab85 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
@@ -17,6 +17,7 @@ import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.ResourceUtils
import helium314.keyboard.latin.utils.prefs
import java.util.Collections
+import kotlin.let
import kotlin.math.sqrt
class EmojiParser(private val params: KeyboardParams, private val context: Context) {
@@ -65,21 +66,7 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte
var currentX = params.mLeftPadding.toFloat()
val currentY = params.mTopPadding.toFloat() // no need to ever change, assignment to rows into rows is done in DynamicGridKeyboard
- // 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
- // we also ignore side padding for key width, and prefer fewer keys per row over narrower keys
- val defaultKeyWidth = ResourceUtils.getDefaultKeyboardWidth(context) * params.mDefaultKeyWidth
- var keyWidth = defaultKeyWidth * sqrt(Settings.getValues().mKeyboardHeightScale)
- val defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(context.resources, false)
- val defaultBottomPadding = context.resources.getFraction(R.fraction.config_keyboard_bottom_padding_holo, defaultKeyboardHeight, defaultKeyboardHeight)
- val emojiKeyboardHeight = defaultKeyboardHeight * 0.75f + params.mVerticalGap - defaultBottomPadding - context.resources.getDimensionPixelSize(R.dimen.config_emoji_category_page_id_height)
- var keyHeight = emojiKeyboardHeight * params.mDefaultRowHeight * Settings.getValues().mKeyboardHeightScale // still apply height scale to key
-
- if (Settings.getValues().mEmojiKeyFit) {
- keyWidth *= Settings.getValues().mFontSizeMultiplierEmoji
- keyHeight *= Settings.getValues().mFontSizeMultiplierEmoji
- }
-
+ val (keyWidth, keyHeight) = getEmojiKeyDimensions(params, context)
lines.forEach { line ->
val keyParams = parseEmojiKeyNew(line) ?: return@forEach
@@ -104,6 +91,7 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte
if (SupportedEmojis.isUnsupported(label)) return null
val popupKeysSpec = split.drop(1).filterNot { SupportedEmojis.isUnsupported(it) }
.takeIf { it.isNotEmpty() }?.joinToString(",")
+ popupKeysSpec?.let { emojiPopupSpecs[label] = popupKeysSpec }
return KeyParams(
label,
label.getCode(),
@@ -113,10 +101,36 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte
params
)
}
+}
+
+fun getEmojiKeyDimensions(params: KeyboardParams, context: Context): Pair {
+ // 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
+ // we also ignore side padding for key width, and prefer fewer keys per row over narrower keys
+ val defaultKeyWidth = ResourceUtils.getDefaultKeyboardWidth(context) * params.mDefaultKeyWidth
+ var keyWidth = defaultKeyWidth * sqrt(Settings.getValues().mKeyboardHeightScale)
+ val defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(context.resources, false)
+ val defaultBottomPadding = context.resources.getFraction(
+ R.fraction.config_keyboard_bottom_padding_holo, defaultKeyboardHeight, defaultKeyboardHeight
+ )
+ val emojiKeyboardHeight = defaultKeyboardHeight * 0.75f + params.mVerticalGap - defaultBottomPadding -
+ context.resources.getDimensionPixelSize(R.dimen.config_emoji_category_page_id_height)
+ var keyHeight =
+ emojiKeyboardHeight * params.mDefaultRowHeight * Settings.getValues().mKeyboardHeightScale // still apply height scale to key
- private fun String.getCode(): Int =
- if (StringUtils.codePointCount(this) != 1) KeyCode.MULTIPLE_CODE_POINTS
- else Character.codePointAt(this, 0)
+ if (Settings.getValues().mEmojiKeyFit) {
+ keyWidth *= Settings.getValues().mFontSizeMultiplierEmoji
+ keyHeight *= Settings.getValues().mFontSizeMultiplierEmoji
+ }
+ return Pair(keyWidth, keyHeight)
}
+fun String.getCode(): Int =
+ if (StringUtils.codePointCount(this) != 1) KeyCode.MULTIPLE_CODE_POINTS
+ else Character.codePointAt(this, 0)
+
const val EMOJI_HINT_LABEL = "◥"
+
+private val emojiPopupSpecs: MutableMap = mutableMapOf()
+
+fun getEmojiPopupSpec(emoji: String): String? = emojiPopupSpecs[emoji]
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 eccf62ebbd..b939872c31 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
@@ -53,6 +53,7 @@ object KeyCode {
const val CLIPBOARD_CLEAR_HISTORY = -36
//const val CLIPBOARD_CLEAR_FULL_HISTORY = -37
//const val CLIPBOARD_CLEAR_PRIMARY_CLIP = -38
+ const val SEARCH = -39
//const val COMPACT_LAYOUT_TO_LEFT = -111
//const val COMPACT_LAYOUT_TO_RIGHT = -112
@@ -196,7 +197,8 @@ object KeyCode {
ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, WORD_LEFT, WORD_RIGHT, PAGE_UP,
PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT,
MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK,
- TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT, SEND_INTENT_ONE, SEND_INTENT_TWO, SEND_INTENT_THREE,
+ TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT, SEND_INTENT_ONE, SEND_INTENT_TWO,
+ SEND_INTENT_THREE, SEARCH
-> 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 214a4df816..9b9777b1eb 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
@@ -39,6 +39,7 @@ object KeyLabel {
const val TAB = "tab"
const val ESCAPE = "esc"
const val TIMESTAMP = "timestamp"
+ const val SEARCH = "search"
/** to make sure a FlorisBoard label works when reading a JSON layout */
// resulting special labels should be names of FunctionalKey enum, case insensitive
@@ -107,6 +108,7 @@ object KeyLabel {
CTRL, ALT, FN, META, ESCAPE -> label.uppercase(Locale.US)
TAB -> "!icon/tab_key|!code/${KeyCode.TAB}"
TIMESTAMP -> "⌚|!code/${KeyCode.TIMESTAMP}"
+ SEARCH -> "!icon/search_key|!code/key_search"
else -> null
}
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 0f7c2f7f66..0af8e626a5 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
@@ -411,7 +411,7 @@ sealed interface KeyData : AbstractKeyData {
when (label) { // or use code?
KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL, KeyLabel.ALPHA, KeyLabel.COMMA, KeyLabel.PERIOD, KeyLabel.DELETE,
KeyLabel.COM, KeyLabel.LANGUAGE_SWITCH, KeyLabel.NUMPAD, KeyLabel.CTRL, KeyLabel.ALT,
- KeyLabel.FN, KeyLabel.META, toolbarKeyStrings[ToolbarKey.EMOJI] -> return Key.BACKGROUND_TYPE_FUNCTIONAL
+ KeyLabel.FN, KeyLabel.META, KeyLabel.SEARCH, toolbarKeyStrings[ToolbarKey.EMOJI] -> 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)
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 0d4fc4bd1b..3730d6ada0 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -45,6 +45,7 @@
import helium314.keyboard.compat.EditorInfoCompatUtils;
import helium314.keyboard.keyboard.KeyboardActionListener;
import helium314.keyboard.keyboard.KeyboardActionListenerImpl;
+import helium314.keyboard.keyboard.emoji.EmojiSearchActivity;
import helium314.keyboard.keyboard.internal.KeyboardIconsSet;
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
import helium314.keyboard.latin.common.InsetsOutlineProvider;
@@ -868,7 +869,7 @@ public void setInputView(final View view) {
mInputView = view;
mInsetsUpdater = ViewOutlineProviderUtilsKt.setInsetsOutlineProvider(view);
updateSoftInputWindowLayoutParameters();
- mSuggestionStripView = mSettings.getCurrent().mToolbarMode == ToolbarMode.HIDDEN?
+ mSuggestionStripView = mSettings.getCurrent().mToolbarMode == ToolbarMode.HIDDEN || isEmojiSearch()?
null : view.findViewById(R.id.suggestion_strip_view);
if (hasSuggestionStripView()) {
mSuggestionStripView.setListener(this, view);
@@ -1289,7 +1290,7 @@ public void onComputeInsets(final InputMethodService.Insets outInsets) {
return;
}
final int stripHeight = mKeyboardSwitcher.isShowingStripContainer() ? mKeyboardSwitcher.getStripContainer().getHeight() : 0;
- final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - stripHeight;
+ int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - stripHeight;
if (hasSuggestionStripView()) {
mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY);
@@ -1306,6 +1307,10 @@ public void onComputeInsets(final InputMethodService.Insets outInsets) {
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
outInsets.touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom);
}
+
+ // Has to be subtracted after calculating touchableRegion
+ visibleTopY -= getEmojiSearchActivityHeight();
+
outInsets.contentTopInsets = visibleTopY;
outInsets.visibleTopInsets = visibleTopY;
mInsetsUpdater.setInsets(outInsets);
@@ -1856,6 +1861,35 @@ void launchSettings() {
startActivity(intent);
}
+ public void launchEmojiSearch() {
+ startActivity(new Intent().setClass(this, EmojiSearchActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent != null && EmojiSearchActivity.EMOJI_SEARCH_DONE_ACTION.equals(intent.getAction())) {
+ if (intent.getBooleanExtra(EmojiSearchActivity.IME_CLOSED_KEY, false)) {
+ requestHideSelf(0);
+ } else {
+ KeyboardSwitcher.getInstance().setEmojiKeyboard();
+ if (intent.hasExtra(EmojiSearchActivity.EMOJI_KEY)) {
+ onTextInput(intent.getStringExtra(EmojiSearchActivity.EMOJI_KEY));
+ }
+ }
+ return START_STICKY;
+ }
+
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ public boolean isEmojiSearch() {
+ return getEmojiSearchActivityHeight() > 0;
+ }
+
+ private int getEmojiSearchActivityHeight() {
+ return EmojiSearchActivity.Companion.decodePrivateImeOptions(getCurrentInputEditorInfo()).height();
+ }
+
public void dumpDictionaryForDebug(final String dictName) {
if (!mDictionaryFacilitator.isActive()) {
resetDictionaryFacilitatorIfNecessary();
diff --git a/app/src/main/java/helium314/keyboard/latin/SingleDictionaryFacilitator.kt b/app/src/main/java/helium314/keyboard/latin/SingleDictionaryFacilitator.kt
index 4e2d62486e..18ef3f144f 100644
--- a/app/src/main/java/helium314/keyboard/latin/SingleDictionaryFacilitator.kt
+++ b/app/src/main/java/helium314/keyboard/latin/SingleDictionaryFacilitator.kt
@@ -5,6 +5,7 @@ import android.content.Context
import android.util.LruCache
import helium314.keyboard.keyboard.Keyboard
import helium314.keyboard.keyboard.KeyboardSwitcher
+import helium314.keyboard.keyboard.emoji.SupportedEmojis
import helium314.keyboard.latin.DictionaryFacilitator.DictionaryInitializationListener
import helium314.keyboard.latin.common.ComposedData
import helium314.keyboard.latin.makedict.WordProperty
@@ -17,6 +18,29 @@ import java.util.concurrent.TimeUnit
class SingleDictionaryFacilitator(private val dict: Dictionary) : DictionaryFacilitator {
var suggestionLogger: SuggestionLogger? = null
+ /**
+ * Returns combined suggestions that match any of the given words, with scores that reflect the matches against all words.
+ * The combined score is calculated as the average absolute (above [Int.MIN_VALUE]) score,
+ * where a non-match is considered an absolute zero.
+ * Other suggestion fields of combined matches are taken arbitrarily from one of them.
+ */
+ fun getSuggestions(words: List): SuggestionResults {
+ val suggestionResults = SuggestionResults(SuggestedWords.MAX_SUGGESTIONS, false, false)
+ words.flatMap { word -> getSuggestions(word).distinctBy { it.word } } // Filter out duplicate results per word
+ .groupBy { it.word }
+ .forEach { (_, results) ->
+ val info = results.maxBy { it.mScore }
+ val score = (results.sumOf { it.mScore.toLong() - Int.MIN_VALUE } / words.size + Int.MIN_VALUE).toInt()
+ suggestionResults.add(
+ SuggestedWords.SuggestedWordInfo(
+ info.word, info.mPrevWordsContext, score, info.mKindAndFlags,
+ info.mSourceDict, info.mIndexOfTouchPointOfSecondWord, info.mAutoCommitFirstWordConfidence
+ )
+ )
+ }
+ return suggestionResults
+ }
+
// this will not work from spell checker if used together with a different keyboard app
fun getSuggestions(word: String): SuggestionResults {
val suggestionResults = getSuggestionResults(
@@ -41,7 +65,7 @@ class SingleDictionaryFacilitator(private val dict: Dictionary) : DictionaryFaci
dict.getSuggestions(composedData, ngramContext, keyboard.proximityInfo.nativeProximityInfo,
settingsValuesForSuggestion, sessionId, 1f,
floatArrayOf(Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL)
- )
+ )?.filter { !SupportedEmojis.isUnsupported(it.word) }
)
suggestionLogger?.onNewSuggestions(suggestionResults, composedData, ngramContext, keyboard, inputStyle)
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 b61586bb2b..e84d740a95 100644
--- a/app/src/main/java/helium314/keyboard/latin/common/Constants.java
+++ b/app/src/main/java/helium314/keyboard/latin/common/Constants.java
@@ -233,6 +233,7 @@ public static String printableCode(final int code) {
case KeyCode.SWITCH_ONE_HANDED_MODE: return "switchOneHandedMode";
case KeyCode.SPLIT_LAYOUT: return "splitLayout";
case KeyCode.NUMPAD: return "numpad";
+ case KeyCode.SEARCH: return "search";
default:
if (code < CODE_SPACE) return String.format("\\u%02X", code);
if (code < 0x100) return String.format("%c", code);
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 e059e0b130..e578ae332f 100644
--- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
+++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
@@ -25,6 +25,7 @@
import helium314.keyboard.event.InputTransaction;
import helium314.keyboard.keyboard.Keyboard;
import helium314.keyboard.keyboard.KeyboardSwitcher;
+import helium314.keyboard.keyboard.emoji.EmojiSearchActivity;
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
import helium314.keyboard.latin.Dictionary;
import helium314.keyboard.latin.DictionaryFacilitator;
@@ -779,6 +780,15 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp
case KeyCode.TIMESTAMP:
mLatinIME.onTextInput(TimestampKt.getTimestamp(mLatinIME));
break;
+ case KeyCode.SEARCH:
+ if (EmojiSearchActivity.Companion.isSupported(mLatinIME)) {
+ commitTyped(Settings.getValues(), LastComposedWord.NOT_A_SEPARATOR);
+ mLatinIME.launchEmojiSearch();
+ } else {
+ // todo: open dictionary settings?
+ onSettingsKeyPressed();
+ }
+ break;
case KeyCode.SEND_INTENT_ONE, KeyCode.SEND_INTENT_TWO, KeyCode.SEND_INTENT_THREE:
IntentUtils.handleSendIntentKey(mLatinIME, event.getMKeyCode());
case KeyCode.IME_HIDE_UI:
diff --git a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.kt b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.kt
index ae55648c8c..e5479fa7a5 100644
--- a/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.kt
+++ b/app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.kt
@@ -59,6 +59,7 @@ import helium314.keyboard.latin.utils.removePinnedKey
import helium314.keyboard.latin.utils.setToolbarButtonsActivatedStateOnPrefChange
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.math.min
+import androidx.core.view.isGone
@SuppressLint("InflateParams")
class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int) :
@@ -136,7 +137,7 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
enabledToolKeyBackground.gradientType = GradientDrawable.RADIAL_GRADIENT
enabledToolKeyBackground.gradientRadius = resources.getDimensionPixelSize(R.dimen.config_suggestions_strip_height) / 2.1f
- val mToolbarMode = Settings.getValues().mToolbarMode
+ val mToolbarMode = if (isGone) ToolbarMode.HIDDEN else Settings.getValues().mToolbarMode
if (mToolbarMode == ToolbarMode.TOOLBAR_KEYS) {
setToolbarVisibility(true)
}
@@ -154,7 +155,7 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
toolbar.addView(button)
}
}
- if (!Settings.getValues().mSuggestionStripHiddenPerUserSettings) {
+ if (!isGone && !Settings.getValues().mSuggestionStripHiddenPerUserSettings) {
for (pinnedKey in getPinnedToolbarKeys(context.prefs())) {
val button = createToolbarKey(context, pinnedKey)
button.layoutParams = toolbarKeyLayoutParams
diff --git a/app/src/main/java/helium314/keyboard/latin/utils/SuggestionResults.java b/app/src/main/java/helium314/keyboard/latin/utils/SuggestionResults.java
index 44a594fa39..15d029b316 100644
--- a/app/src/main/java/helium314/keyboard/latin/utils/SuggestionResults.java
+++ b/app/src/main/java/helium314/keyboard/latin/utils/SuggestionResults.java
@@ -6,6 +6,7 @@
package helium314.keyboard.latin.utils;
+import androidx.annotation.Nullable;
import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo;
import helium314.keyboard.latin.define.ProductionFlags;
@@ -56,7 +57,7 @@ public boolean add(final SuggestedWordInfo e) {
}
@Override
- public boolean addAll(final Collection extends SuggestedWordInfo> e) {
+ public boolean addAll(@Nullable final Collection extends SuggestedWordInfo> e) {
if (null == e) return false;
return super.addAll(e);
}
diff --git a/app/src/main/res/layout/input_view.xml b/app/src/main/res/layout/input_view.xml
index 0bd874eeef..52faeca364 100644
--- a/app/src/main/res/layout/input_view.xml
+++ b/app/src/main/res/layout/input_view.xml
@@ -8,8 +8,7 @@
+ android:layout_height="wrap_content">
diff --git a/app/src/main/res/values-land/config.xml b/app/src/main/res/values-land/config.xml
index e1d909847c..1ffb399990 100644
--- a/app/src/main/res/values-land/config.xml
+++ b/app/src/main/res/values-land/config.xml
@@ -62,7 +62,7 @@
41%p
78%p
78%p
- 32
+ 2
3
diff --git a/app/src/main/res/values-sw600dp-land/config.xml b/app/src/main/res/values-sw600dp-land/config.xml
index 448b73c0e1..bc5a17579f 100644
--- a/app/src/main/res/values-sw600dp-land/config.xml
+++ b/app/src/main/res/values-sw600dp-land/config.xml
@@ -52,7 +52,7 @@
40%p
64%p
64%p
- 36
+ 3
4
diff --git a/app/src/main/res/values-sw600dp/config.xml b/app/src/main/res/values-sw600dp/config.xml
index 3fac28c125..be55360d1f 100644
--- a/app/src/main/res/values-sw600dp/config.xml
+++ b/app/src/main/res/values-sw600dp/config.xml
@@ -72,7 +72,7 @@
28%p
78%p
78%p
- 36
+ 3
3
diff --git a/app/src/main/res/values-sw768dp-land/config.xml b/app/src/main/res/values-sw768dp-land/config.xml
index a315051013..c0fe32b58e 100644
--- a/app/src/main/res/values-sw768dp-land/config.xml
+++ b/app/src/main/res/values-sw768dp-land/config.xml
@@ -50,5 +50,5 @@
33%p
58%p
58%p
- 39
+ 3
diff --git a/app/src/main/res/values-sw768dp/config.xml b/app/src/main/res/values-sw768dp/config.xml
index 578de4d85b..642e8bf733 100644
--- a/app/src/main/res/values-sw768dp/config.xml
+++ b/app/src/main/res/values-sw768dp/config.xml
@@ -67,5 +67,5 @@
30%p
64%p
64%p
- 39
+ 3
diff --git a/app/src/main/res/values/config.xml b/app/src/main/res/values/config.xml
index bc22125eb1..a2b97d8639 100644
--- a/app/src/main/res/values/config.xml
+++ b/app/src/main/res/values/config.xml
@@ -75,7 +75,7 @@
30%p
78%p
78%p
- 32
+ 3
2
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1ea905d049..050d4c9c75 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -905,4 +905,8 @@ New dictionary:
Custom subtype
Landscape
+
+ Search emoji
+
+ Search
diff --git a/app/src/main/res/values/themes-common.xml b/app/src/main/res/values/themes-common.xml
index ed3533b51f..88b01c3a65 100644
--- a/app/src/main/res/values/themes-common.xml
+++ b/app/src/main/res/values/themes-common.xml
@@ -115,4 +115,9 @@
- true
- none
+
From 23426c059f5dbe61905022e71d08349bd949dd1e Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Mon, 7 Jul 2025 02:06:04 +0300
Subject: [PATCH 02/56] respect _Default emoji skin tone_
---
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 11 +++++------
.../keyboard/internal/keyboard_parser/EmojiParser.kt | 3 +++
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 3a44969972..6f4534581c 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -73,6 +73,7 @@ import helium314.keyboard.keyboard.internal.KeyboardParams
import helium314.keyboard.keyboard.internal.keyboard_parser.EMOJI_HINT_LABEL
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
import helium314.keyboard.keyboard.internal.keyboard_parser.getCode
+import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiDefaultVersion
import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiKeyDimensions
import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiPopupSpec
import helium314.keyboard.latin.Dictionary
@@ -222,7 +223,7 @@ class EmojiSearchActivity : ComponentActivity() {
.setSubtype(RichInputMethodSubtype.emojiSubtype)
.setKeyboardGeometry(keyboardWidth, EmojiLayoutParams(contextThemeWrapper.resources).emojiKeyboardHeight).build()
- // Initialize popup specs
+ // Initialize default versions and popup specs
layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_CATEGORY2)
val keyboard = DynamicGridKeyboard(contextThemeWrapper.prefs(), layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
@@ -269,12 +270,10 @@ class EmojiSearchActivity : ComponentActivity() {
keyboard.removeAllKeys()
pressedKey = null
dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { StringUtils.mightBeEmoji(it.word) }.forEach {
- val emoji = it.word
+ val emoji = getEmojiDefaultVersion(it.word)
val popupSpec = getEmojiPopupSpec(emoji)
- val keyParams = KeyParams(
- emoji, emoji.getCode(), if (popupSpec != null) EMOJI_HINT_LABEL else null, popupSpec,
- Key.LABEL_FLAGS_FONT_NORMAL, keyboardParams
- )
+ val keyParams = KeyParams(emoji, emoji.getCode(), if (popupSpec != null) EMOJI_HINT_LABEL else null, popupSpec,
+ Key.LABEL_FLAGS_FONT_NORMAL, keyboardParams)
keyParams.mAbsoluteWidth = keyWidth!!
keyParams.mAbsoluteHeight = keyHeight!!
val key = keyParams.createKey()
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 30b01eab85..003a69b88d 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
@@ -52,6 +52,7 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte
// find the line containing the skin tone, and swap with first
val foundIndex = split.indexOfFirst { it.contains(defaultSkinTone) }
if (foundIndex > 0) {
+ emojiDefaultVersions[split[0]] = split[foundIndex]
Collections.swap(split, 0, foundIndex)
}
split.joinToString(" ")
@@ -131,6 +132,8 @@ fun String.getCode(): Int =
const val EMOJI_HINT_LABEL = "◥"
+private val emojiDefaultVersions: MutableMap = mutableMapOf()
private val emojiPopupSpecs: MutableMap = mutableMapOf()
+fun getEmojiDefaultVersion(emoji: String): String = emojiDefaultVersions[emoji] ?: emoji
fun getEmojiPopupSpec(emoji: String): String? = emojiPopupSpecs[emoji]
From 197b84bde66645e3942719c176cd4166de71349d Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Tue, 8 Jul 2025 02:15:12 +0300
Subject: [PATCH 03/56] Minor fix
---
.../keyboard/emoji/EmojiSearchActivity.kt | 3 +--
.../internal/keyboard_parser/EmojiParser.kt | 26 +++++++++++--------
2 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 6f4534581c..255dba53dd 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -63,7 +63,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import helium314.keyboard.keyboard.Key
-import helium314.keyboard.keyboard.Key.KeyParams
import helium314.keyboard.keyboard.KeyboardId
import helium314.keyboard.keyboard.KeyboardLayoutSet
import helium314.keyboard.keyboard.KeyboardSwitcher
@@ -272,7 +271,7 @@ class EmojiSearchActivity : ComponentActivity() {
dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { StringUtils.mightBeEmoji(it.word) }.forEach {
val emoji = getEmojiDefaultVersion(it.word)
val popupSpec = getEmojiPopupSpec(emoji)
- val keyParams = KeyParams(emoji, emoji.getCode(), if (popupSpec != null) EMOJI_HINT_LABEL else null, popupSpec,
+ val keyParams = Key.KeyParams(emoji, emoji.getCode(), if (popupSpec != null) EMOJI_HINT_LABEL else null, popupSpec,
Key.LABEL_FLAGS_FONT_NORMAL, keyboardParams)
keyParams.mAbsoluteWidth = keyWidth!!
keyParams.mAbsoluteHeight = keyHeight!!
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 003a69b88d..cecefb325f 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
@@ -45,19 +45,23 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte
context.assets.open("emoji/$emojiFileName").reader().use { it.readLines() }
}
val defaultSkinTone = context.prefs().getString(Settings.PREF_EMOJI_SKIN_TONE, Defaults.PREF_EMOJI_SKIN_TONE)!!
- if (params.mId.mElementId == KeyboardId.ELEMENT_EMOJI_CATEGORY2 && defaultSkinTone != "") {
- // adjust PEOPLE_AND_BODY if we have a non-yellow default skin tone
- val modifiedLines = emojiLines.map {
- val split = it.splitOnWhitespace().toMutableList()
- // find the line containing the skin tone, and swap with first
- val foundIndex = split.indexOfFirst { it.contains(defaultSkinTone) }
- if (foundIndex > 0) {
- emojiDefaultVersions[split[0]] = split[foundIndex]
- Collections.swap(split, 0, foundIndex)
+ if (params.mId.mElementId == KeyboardId.ELEMENT_EMOJI_CATEGORY2) {
+ emojiDefaultVersions.clear()
+ emojiPopupSpecs.clear()
+ if (defaultSkinTone != "") {
+ // adjust PEOPLE_AND_BODY if we have a non-yellow default skin tone
+ val modifiedLines = emojiLines.map {
+ val split = it.splitOnWhitespace().toMutableList()
+ // find the line containing the skin tone, and swap with first
+ val foundIndex = split.indexOfFirst { it.contains(defaultSkinTone) }
+ if (foundIndex > 0) {
+ emojiDefaultVersions[split[0]] = split[foundIndex]
+ Collections.swap(split, 0, foundIndex)
+ }
+ split.joinToString(" ")
}
- split.joinToString(" ")
+ return parseLines(modifiedLines)
}
- return parseLines(modifiedLines)
}
return parseLines(emojiLines)
}
From 408c893f1556058304937004dc90ee51cfea89c3 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Tue, 8 Jul 2025 02:28:38 +0300
Subject: [PATCH 04/56] Minor fix
---
.../helium314/keyboard/keyboard/emoji/EmojiPalettesView.java | 3 ++-
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 3 ++-
.../keyboard/keyboard/internal/keyboard_parser/EmojiParser.kt | 4 ++++
3 files changed, 8 insertions(+), 2 deletions(-)
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 36fe561f39..d69445ca0a 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
@@ -36,6 +36,7 @@
import helium314.keyboard.keyboard.PointerTracker;
import helium314.keyboard.keyboard.internal.KeyDrawParams;
import helium314.keyboard.keyboard.internal.KeyVisualAttributes;
+import helium314.keyboard.keyboard.internal.keyboard_parser.EmojiParserKt;
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
import helium314.keyboard.latin.AudioAndHapticFeedbackManager;
import helium314.keyboard.latin.Dictionary;
@@ -316,7 +317,7 @@ public String getDescription(String emoji) {
return null;
}
- var wordProperty = sDictionaryFacilitator.getWordProperty(emoji);
+ var wordProperty = sDictionaryFacilitator.getWordProperty(EmojiParserKt.getEmojiNeutralVersion(emoji));
if (wordProperty == null || ! wordProperty.mHasShortcuts) {
return null;
}
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 255dba53dd..0d051beb8d 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -74,6 +74,7 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
import helium314.keyboard.keyboard.internal.keyboard_parser.getCode
import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiDefaultVersion
import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiKeyDimensions
+import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiNeutralVersion
import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiPopupSpec
import helium314.keyboard.latin.Dictionary
import helium314.keyboard.latin.DictionaryFactory
@@ -252,7 +253,7 @@ class EmojiSearchActivity : ComponentActivity() {
}
override fun getDescription(emoji: String): String? = if (Settings.getValues().mShowEmojiDescriptions)
- dictionaryFacilitator?.getWordProperty(emoji)?.mShortcutTargets[0]?.mWord else null
+ dictionaryFacilitator?.getWordProperty(getEmojiNeutralVersion(emoji))?.mShortcutTargets[0]?.mWord else null
})
}
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 cecefb325f..cb8cb20aa6 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
@@ -47,6 +47,7 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte
val defaultSkinTone = context.prefs().getString(Settings.PREF_EMOJI_SKIN_TONE, Defaults.PREF_EMOJI_SKIN_TONE)!!
if (params.mId.mElementId == KeyboardId.ELEMENT_EMOJI_CATEGORY2) {
emojiDefaultVersions.clear()
+ emojiNeutralVersions.clear()
emojiPopupSpecs.clear()
if (defaultSkinTone != "") {
// adjust PEOPLE_AND_BODY if we have a non-yellow default skin tone
@@ -56,6 +57,7 @@ class EmojiParser(private val params: KeyboardParams, private val context: Conte
val foundIndex = split.indexOfFirst { it.contains(defaultSkinTone) }
if (foundIndex > 0) {
emojiDefaultVersions[split[0]] = split[foundIndex]
+ emojiNeutralVersions[split[foundIndex]] = split[0]
Collections.swap(split, 0, foundIndex)
}
split.joinToString(" ")
@@ -137,7 +139,9 @@ fun String.getCode(): Int =
const val EMOJI_HINT_LABEL = "◥"
private val emojiDefaultVersions: MutableMap = mutableMapOf()
+private val emojiNeutralVersions: MutableMap = mutableMapOf()
private val emojiPopupSpecs: MutableMap = mutableMapOf()
fun getEmojiDefaultVersion(emoji: String): String = emojiDefaultVersions[emoji] ?: emoji
+fun getEmojiNeutralVersion(emoji: String): String = emojiNeutralVersions[emoji] ?: emoji
fun getEmojiPopupSpec(emoji: String): String? = emojiPopupSpecs[emoji]
From 494f76bf9bfe077272a422242a5a976acb66ed84 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Wed, 9 Jul 2025 01:36:33 +0300
Subject: [PATCH 05/56] Minor fix
---
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 0d051beb8d..003e71038c 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -12,6 +12,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -120,9 +121,8 @@ class EmojiSearchActivity : ComponentActivity() {
var heightDp by remember {
mutableStateOf(0.dp)
}
- Column(
- modifier = Modifier.fillMaxSize()
- .windowInsetsPadding(WindowInsets.safeDrawing.exclude(WindowInsets(bottom = heightDp))),
+ Column(modifier = Modifier.fillMaxSize().clickable(onClick = { cancel() })
+ .windowInsetsPadding(WindowInsets.safeDrawing.exclude(WindowInsets(bottom = heightDp))),
verticalArrangement = Arrangement.Bottom
) {
Column(modifier = Modifier.wrapContentHeight().onGloballyPositioned {
From 7679d3e6520207aa5fada62bd8544e8181befcc4 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Wed, 9 Jul 2025 23:28:27 +0300
Subject: [PATCH 06/56] Styling
---
.../keyboard/emoji/EmojiSearchActivity.kt | 155 ++++++++++--------
1 file changed, 83 insertions(+), 72 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 003e71038c..d5e4b43dac 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -5,7 +5,6 @@ import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
-import android.view.ContextThemeWrapper
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.activity.ComponentActivity
@@ -32,12 +31,14 @@ import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
@@ -49,7 +50,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -98,6 +101,7 @@ import helium314.keyboard.settings.SearchIcon
* This activity is displayed in a gap created for it above the keyboard and below the host app, and partly obscures the host app.
*/
class EmojiSearchActivity : ComponentActivity() {
+ private val colors = Settings.getValues().mColors
private var startup: Boolean = true
private var emojiPageKeyboardView: EmojiPageKeyboardView? = null
private var keyboardParams: KeyboardParams? = null
@@ -112,6 +116,7 @@ class EmojiSearchActivity : ComponentActivity() {
init()
enableEdgeToEdge()
setContent {
+ LocalContext.current.setTheme(KeyboardTheme.getKeyboardTheme(this).mStyleId)
Surface(modifier = Modifier.fillMaxSize(), color = Color(0x80000000)) {
val imeVisible = WindowInsets.isImeVisible
val localDensity = LocalDensity.current
@@ -125,68 +130,78 @@ class EmojiSearchActivity : ComponentActivity() {
.windowInsetsPadding(WindowInsets.safeDrawing.exclude(WindowInsets(bottom = heightDp))),
verticalArrangement = Arrangement.Bottom
) {
- Column(modifier = Modifier.wrapContentHeight().onGloballyPositioned {
- if (!startup && !imeVisible) {
- imeClosed = true
- cancel()
- return@onGloballyPositioned
- }
- if (!startup && (KeyboardSwitcher.getInstance().isShowingEmojiPalettes
- || KeyboardSwitcher.getInstance().isShowingClipboardHistory)) {
- cancel()
- return@onGloballyPositioned
- }
- heightPx = it.size.height
- heightDp = with(localDensity) { it.size.height.toDp() }
- }) {
- Row(modifier = Modifier.background(Color.White).fillMaxWidth().height(30.dp)) {
+ Column(modifier = Modifier.wrapContentHeight()
+ .background(Color(colors.get(ColorType.MAIN_BACKGROUND))).onGloballyPositioned {
+ if (!startup && !imeVisible) {
+ imeClosed = true
+ cancel()
+ return@onGloballyPositioned
+ }
+ if (!startup && (KeyboardSwitcher.getInstance().isShowingEmojiPalettes
+ || KeyboardSwitcher.getInstance().isShowingClipboardHistory)) {
+ cancel()
+ return@onGloballyPositioned
+ }
+ heightPx = it.size.height
+ heightDp = with(localDensity) { it.size.height.toDp() }
+ }) {
+ Row(modifier = Modifier.fillMaxWidth().height(30.dp)) {
IconButton(onClick = { cancel() }) {
- Icon(
- painterResource(R.drawable.ic_arrow_back),
- stringResource(R.string.spoken_description_action_previous)
- )
+ Icon(painter = painterResource(R.drawable.ic_arrow_back),
+ stringResource(R.string.spoken_description_action_previous),
+ tint = Color(colors.get(ColorType.EMOJI_KEY_TEXT)))
}
- Text(
- text = stringResource(R.string.emoji_search_title), fontSize = 18.sp,
- modifier = Modifier.fillMaxWidth().align(Alignment.CenterVertically)
- )
+ Text(text = stringResource(R.string.emoji_search_title), fontSize = 18.sp,
+ color = Color(colors.get(ColorType.EMOJI_KEY_TEXT)),
+ modifier = Modifier.fillMaxWidth().align(Alignment.CenterVertically))
}
AndroidView({ emojiPageKeyboardView!! }, modifier = Modifier.wrapContentHeight().fillMaxWidth())
val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf(TextFieldValue(searchText, selection = TextRange(searchText.length))) }
- BasicTextField(
- value = text,
- modifier = Modifier.fillMaxWidth().heightIn(20.dp, 30.dp).focusRequester(focusRequester),
- textStyle = TextStyle(textDirection = TextDirection.Content),
- onValueChange = { it: TextFieldValue ->
- text = it
- search(it.text)
- },
- enabled = true,
- keyboardOptions = KeyboardOptions(
- imeAction = ImeAction.Done,
- platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))
- ),
- keyboardActions = KeyboardActions(onDone = { finish() }),
- singleLine = true,
- ) {
- TextFieldDefaults.DecorationBox(
- value = text.text,
- contentPadding = PaddingValues(0.dp),
- visualTransformation = VisualTransformation.None,
- innerTextField = it,
- placeholder = { Text(stringResource(R.string.search_field_placeholder)) },
- leadingIcon = { SearchIcon() },
- trailingIcon = {
- IconButton(onClick = {
- text = TextFieldValue()
- search("")
- }) { CloseIcon(cancel) }
+ val textFieldColors = TextFieldDefaults.colors()
+ .copy(unfocusedContainerColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_BACKGROUND)),
+ unfocusedTextColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
+ cursorColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
+ unfocusedLeadingIconColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
+ unfocusedTrailingIconColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
+ unfocusedPlaceholderColor = lerp(Color(colors.get(ColorType.FUNCTIONAL_KEY_BACKGROUND)),
+ Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)), 0.5f))
+ CompositionLocalProvider(LocalTextSelectionColors provides textFieldColors.textSelectionColors) {
+ BasicTextField(
+ value = text,
+ modifier = Modifier.fillMaxWidth().heightIn(20.dp, 30.dp).focusRequester(focusRequester),
+ textStyle = TextStyle(textDirection = TextDirection.Content, color = textFieldColors.unfocusedTextColor),
+ onValueChange = { it: TextFieldValue ->
+ text = it
+ search(it.text)
},
- singleLine = true,
enabled = true,
- interactionSource = MutableInteractionSource(),
- )
+ keyboardOptions = KeyboardOptions(
+ imeAction = ImeAction.Done,
+ platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))
+ ),
+ keyboardActions = KeyboardActions(onDone = { finish() }),
+ singleLine = true,
+ ) {
+ TextFieldDefaults.DecorationBox(
+ value = text.text,
+ colors = textFieldColors,
+ contentPadding = PaddingValues(2.dp),
+ visualTransformation = VisualTransformation.None,
+ innerTextField = it,
+ placeholder = { Text(stringResource(R.string.search_field_placeholder)) },
+ leadingIcon = { SearchIcon() },
+ trailingIcon = {
+ IconButton(onClick = {
+ text = TextFieldValue()
+ search("")
+ }) { CloseIcon(cancel) }
+ },
+ singleLine = true,
+ enabled = true,
+ interactionSource = MutableInteractionSource(),
+ )
+ }
}
LaunchedEffect(Unit) { focusRequester.requestFocus() }
}
@@ -201,14 +216,12 @@ class EmojiSearchActivity : ComponentActivity() {
override fun onStop() {
val intent = Intent(this, LatinIME::class.java).setAction(EMOJI_SEARCH_DONE_ACTION)
- intent.putExtra(IME_CLOSED_KEY, imeClosed)
+ .putExtra(IME_CLOSED_KEY, imeClosed)
if (pressedKey != null) {
- intent.putExtra(
- EMOJI_KEY, if (pressedKey!!.code == KeyCode.MULTIPLE_CODE_POINTS)
- pressedKey!!.getOutputText()
- else
- Character.toString(pressedKey!!.code)
- )
+ intent.putExtra(EMOJI_KEY, if (pressedKey!!.code == KeyCode.MULTIPLE_CODE_POINTS)
+ pressedKey!!.getOutputText()
+ else
+ Character.toString(pressedKey!!.code))
KeyboardSwitcher.getInstance().emojiPalettesView.addRecentKey(pressedKey)
}
@@ -217,30 +230,28 @@ class EmojiSearchActivity : ComponentActivity() {
}
private fun init() {
- val contextThemeWrapper = ContextThemeWrapper(this, KeyboardTheme.getKeyboardTheme(this).mStyleId)
- val keyboardWidth = ResourceUtils.getKeyboardWidth(contextThemeWrapper, Settings.getValues())
- val layoutSet = KeyboardLayoutSet.Builder(contextThemeWrapper, null)
- .setSubtype(RichInputMethodSubtype.emojiSubtype)
- .setKeyboardGeometry(keyboardWidth, EmojiLayoutParams(contextThemeWrapper.resources).emojiKeyboardHeight).build()
+ val keyboardWidth = ResourceUtils.getKeyboardWidth(this, Settings.getValues())
+ val layoutSet = KeyboardLayoutSet.Builder(this, null).setSubtype(RichInputMethodSubtype.emojiSubtype)
+ .setKeyboardGeometry(keyboardWidth, EmojiLayoutParams(resources).emojiKeyboardHeight).build()
// Initialize default versions and popup specs
layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_CATEGORY2)
- val keyboard = DynamicGridKeyboard(contextThemeWrapper.prefs(), layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
+ val keyboard = DynamicGridKeyboard(prefs(), layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) 1 else 2,
KeyboardId.ELEMENT_EMOJI_CATEGORY16, keyboardWidth)
- val builder = KeyboardBuilder(contextThemeWrapper, KeyboardParams())
+ val builder = KeyboardBuilder(this, KeyboardParams())
builder.load(keyboard.mId)
keyboardParams = builder.mParams
- val (width, height) = getEmojiKeyDimensions(keyboardParams!!, contextThemeWrapper)
+ val (width, height) = getEmojiKeyDimensions(keyboardParams!!, this)
keyWidth = width
keyHeight = height
- emojiPageKeyboardView = EmojiPageKeyboardView(contextThemeWrapper, null)
+ emojiPageKeyboardView = EmojiPageKeyboardView(this, null)
emojiPageKeyboardView!!.setKeyboard(keyboard)
emojiPageKeyboardView!!.layoutParams =
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
emojiPageKeyboardView!!.background = null
- Settings.getValues().mColors.setBackground(emojiPageKeyboardView!!, ColorType.MAIN_BACKGROUND)
+ colors.setBackground(emojiPageKeyboardView!!, ColorType.MAIN_BACKGROUND)
emojiPageKeyboardView!!.setPadding(0, 10, 0, 10)
emojiPageKeyboardView!!.setEmojiViewCallback(object : EmojiViewCallback {
From fcd91f49b15cbf501b9b21e864b483eeaf5cf8a1 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Thu, 10 Jul 2025 05:47:30 +0300
Subject: [PATCH 07/56] Minor fixes
---
.../keyboard/emoji/EmojiSearchActivity.kt | 22 ++++++++++++-------
.../helium314/keyboard/latin/LatinIME.java | 2 +-
2 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index d5e4b43dac..c9cc1d52d7 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -132,13 +132,16 @@ class EmojiSearchActivity : ComponentActivity() {
) {
Column(modifier = Modifier.wrapContentHeight()
.background(Color(colors.get(ColorType.MAIN_BACKGROUND))).onGloballyPositioned {
+ if (startup && imeVisible && isAlphaKeyboard()) {
+ search(searchText)
+ return@onGloballyPositioned
+ }
if (!startup && !imeVisible) {
imeClosed = true
cancel()
return@onGloballyPositioned
}
- if (!startup && (KeyboardSwitcher.getInstance().isShowingEmojiPalettes
- || KeyboardSwitcher.getInstance().isShowingClipboardHistory)) {
+ if (!startup && !isAlphaKeyboard()) {
cancel()
return@onGloballyPositioned
}
@@ -210,10 +213,6 @@ class EmojiSearchActivity : ComponentActivity() {
}
}
- override fun onEnterAnimationComplete() {
- search(searchText)
- }
-
override fun onStop() {
val intent = Intent(this, LatinIME::class.java).setAction(EMOJI_SEARCH_DONE_ACTION)
.putExtra(IME_CLOSED_KEY, imeClosed)
@@ -268,14 +267,21 @@ class EmojiSearchActivity : ComponentActivity() {
})
}
- fun search(text: String) {
- searchText = text
+ private fun isAlphaKeyboard(): Boolean = !(KeyboardSwitcher.getInstance().isShowingEmojiPalettes
+ || KeyboardSwitcher.getInstance().isShowingClipboardHistory)
+
+ private fun search(text: String) {
initDictionaryFacilitator(this)
if (dictionaryFacilitator == null) {
cancel()
return
}
+ if (!startup && text == searchText) {
+ return
+ }
+
+ searchText = text
startup = false
val keyboard = emojiPageKeyboardView!!.keyboard as DynamicGridKeyboard
keyboard.removeAllKeys()
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 3730d6ada0..7c726c2dcb 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -1871,7 +1871,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getBooleanExtra(EmojiSearchActivity.IME_CLOSED_KEY, false)) {
requestHideSelf(0);
} else {
- KeyboardSwitcher.getInstance().setEmojiKeyboard();
+ mHandler.postDelayed(() -> KeyboardSwitcher.getInstance().setEmojiKeyboard(), 100);
if (intent.hasExtra(EmojiSearchActivity.EMOJI_KEY)) {
onTextInput(intent.getStringExtra(EmojiSearchActivity.EMOJI_KEY));
}
From 2725df2c8a11fdcb7c23cce1f9bd2d132ae17761 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Fri, 11 Jul 2025 00:15:59 +0300
Subject: [PATCH 08/56] Minor fix Cosmetics
---
.../keyboard/emoji/EmojiSearchActivity.kt | 54 ++++++++++---------
1 file changed, 30 insertions(+), 24 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index c9cc1d52d7..6e0e2bb0be 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -96,19 +96,21 @@ import helium314.keyboard.latin.utils.ResourceUtils
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.CloseIcon
import helium314.keyboard.settings.SearchIcon
+import kotlin.properties.Delegates
/**
* This activity is displayed in a gap created for it above the keyboard and below the host app, and partly obscures the host app.
*/
class EmojiSearchActivity : ComponentActivity() {
private val colors = Settings.getValues().mColors
- private var startup: Boolean = true
- private var emojiPageKeyboardView: EmojiPageKeyboardView? = null
- private var keyboardParams: KeyboardParams? = null
- private var keyWidth: Float? = null
- private var keyHeight: Float? = null
+ private var enterAnimationComplete = false
+ private var startup = true
+ private lateinit var emojiPageKeyboardView: EmojiPageKeyboardView
+ private lateinit var keyboardParams: KeyboardParams
+ private var keyWidth by Delegates.notNull()
+ private var keyHeight by Delegates.notNull()
private var pressedKey: Key? = null
- private var imeClosed: Boolean = false
+ private var imeClosed = false
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
@@ -132,7 +134,7 @@ class EmojiSearchActivity : ComponentActivity() {
) {
Column(modifier = Modifier.wrapContentHeight()
.background(Color(colors.get(ColorType.MAIN_BACKGROUND))).onGloballyPositioned {
- if (startup && imeVisible && isAlphaKeyboard()) {
+ if (startup && enterAnimationComplete && imeVisible && isAlphaKeyboard()) {
search(searchText)
return@onGloballyPositioned
}
@@ -158,7 +160,7 @@ class EmojiSearchActivity : ComponentActivity() {
color = Color(colors.get(ColorType.EMOJI_KEY_TEXT)),
modifier = Modifier.fillMaxWidth().align(Alignment.CenterVertically))
}
- AndroidView({ emojiPageKeyboardView!! }, modifier = Modifier.wrapContentHeight().fillMaxWidth())
+ AndroidView({ emojiPageKeyboardView }, modifier = Modifier.wrapContentHeight().fillMaxWidth())
val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf(TextFieldValue(searchText, selection = TextRange(searchText.length))) }
val textFieldColors = TextFieldDefaults.colors()
@@ -213,16 +215,20 @@ class EmojiSearchActivity : ComponentActivity() {
}
}
+ override fun onEnterAnimationComplete() {
+ enterAnimationComplete = true
+ }
+
override fun onStop() {
val intent = Intent(this, LatinIME::class.java).setAction(EMOJI_SEARCH_DONE_ACTION)
.putExtra(IME_CLOSED_KEY, imeClosed)
- if (pressedKey != null) {
- intent.putExtra(EMOJI_KEY, if (pressedKey!!.code == KeyCode.MULTIPLE_CODE_POINTS)
- pressedKey!!.getOutputText()
+ pressedKey?.let {
+ intent.putExtra(EMOJI_KEY, if (it.code == KeyCode.MULTIPLE_CODE_POINTS)
+ it.getOutputText()
else
- Character.toString(pressedKey!!.code))
+ Character.toString(it.code))
- KeyboardSwitcher.getInstance().emojiPalettesView.addRecentKey(pressedKey)
+ KeyboardSwitcher.getInstance().emojiPalettesView.addRecentKey(it)
}
startService(intent)
super.onStop()
@@ -242,18 +248,18 @@ class EmojiSearchActivity : ComponentActivity() {
val builder = KeyboardBuilder(this, KeyboardParams())
builder.load(keyboard.mId)
keyboardParams = builder.mParams
- val (width, height) = getEmojiKeyDimensions(keyboardParams!!, this)
+ val (width, height) = getEmojiKeyDimensions(keyboardParams, this)
keyWidth = width
keyHeight = height
emojiPageKeyboardView = EmojiPageKeyboardView(this, null)
- emojiPageKeyboardView!!.setKeyboard(keyboard)
- emojiPageKeyboardView!!.layoutParams =
+ emojiPageKeyboardView.setKeyboard(keyboard)
+ emojiPageKeyboardView.layoutParams =
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
- emojiPageKeyboardView!!.background = null
- colors.setBackground(emojiPageKeyboardView!!, ColorType.MAIN_BACKGROUND)
- emojiPageKeyboardView!!.setPadding(0, 10, 0, 10)
+ emojiPageKeyboardView.background = null
+ colors.setBackground(emojiPageKeyboardView, ColorType.MAIN_BACKGROUND)
+ emojiPageKeyboardView.setPadding(0, 10, 0, 10)
- emojiPageKeyboardView!!.setEmojiViewCallback(object : EmojiViewCallback {
+ emojiPageKeyboardView.setEmojiViewCallback(object : EmojiViewCallback {
override fun onPressKey(key: Key) {
}
@@ -283,7 +289,7 @@ class EmojiSearchActivity : ComponentActivity() {
searchText = text
startup = false
- val keyboard = emojiPageKeyboardView!!.keyboard as DynamicGridKeyboard
+ val keyboard = emojiPageKeyboardView.keyboard as DynamicGridKeyboard
keyboard.removeAllKeys()
pressedKey = null
dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { StringUtils.mightBeEmoji(it.word) }.forEach {
@@ -291,14 +297,14 @@ class EmojiSearchActivity : ComponentActivity() {
val popupSpec = getEmojiPopupSpec(emoji)
val keyParams = Key.KeyParams(emoji, emoji.getCode(), if (popupSpec != null) EMOJI_HINT_LABEL else null, popupSpec,
Key.LABEL_FLAGS_FONT_NORMAL, keyboardParams)
- keyParams.mAbsoluteWidth = keyWidth!!
- keyParams.mAbsoluteHeight = keyHeight!!
+ keyParams.mAbsoluteWidth = keyWidth
+ keyParams.mAbsoluteHeight = keyHeight
val key = keyParams.createKey()
keyboard.addKeyLast(key)
if (pressedKey == null && Settings.getValues().mAutoCorrectEnabled)
pressedKey = key
}
- emojiPageKeyboardView!!.invalidate()
+ emojiPageKeyboardView.invalidate()
}
private fun cancel() {
From 4a6a51d218c0f55943c7f26faecd40f0c5c3f604 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 12 Jul 2025 02:02:53 +0300
Subject: [PATCH 09/56] Minor fix
---
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 6e0e2bb0be..243bfac3ee 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -50,6 +50,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
@@ -132,8 +133,8 @@ class EmojiSearchActivity : ComponentActivity() {
.windowInsetsPadding(WindowInsets.safeDrawing.exclude(WindowInsets(bottom = heightDp))),
verticalArrangement = Arrangement.Bottom
) {
- Column(modifier = Modifier.wrapContentHeight()
- .background(Color(colors.get(ColorType.MAIN_BACKGROUND))).onGloballyPositioned {
+ Column(modifier = Modifier.wrapContentHeight().background(Color(colors.get(ColorType.MAIN_BACKGROUND)))
+ .onGloballyPositioned {
if (startup && enterAnimationComplete && imeVisible && isAlphaKeyboard()) {
search(searchText)
return@onGloballyPositioned
@@ -187,6 +188,7 @@ class EmojiSearchActivity : ComponentActivity() {
),
keyboardActions = KeyboardActions(onDone = { finish() }),
singleLine = true,
+ cursorBrush = SolidColor(textFieldColors.cursorColor)
) {
TextFieldDefaults.DecorationBox(
value = text.text,
From e4380111d8d942c42ebff953720b1f4aeed8a593 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 12 Jul 2025 02:37:02 +0300
Subject: [PATCH 10/56] Minor fix, license
---
app/src/main/AndroidManifest.xml | 5 ++---
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 3 ++-
app/src/main/java/helium314/keyboard/latin/LatinIME.java | 3 ++-
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4ebab05c10..1695bf9c5a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -76,9 +76,8 @@ SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
-
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 243bfac3ee..9bc511f4f9 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.keyboard.emoji
import android.R.string.cancel
@@ -100,7 +101,7 @@ import helium314.keyboard.settings.SearchIcon
import kotlin.properties.Delegates
/**
- * This activity is displayed in a gap created for it above the keyboard and below the host app, and partly obscures the host app.
+ * This activity is displayed in a gap created for it above the keyboard and below the host app, and disables the host app.
*/
class EmojiSearchActivity : ComponentActivity() {
private val colors = Settings.getValues().mColors
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 7c726c2dcb..bea625d27c 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -1862,7 +1862,8 @@ void launchSettings() {
}
public void launchEmojiSearch() {
- startActivity(new Intent().setClass(this, EmojiSearchActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ startActivity(new Intent().setClass(this, EmojiSearchActivity.class)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_MULTIPLE_TASK));
}
@Override
From 251e5e8815a21d382d938aca641a44a1c060f292 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 12 Jul 2025 21:38:56 +0300
Subject: [PATCH 11/56] Minor fix
---
.../keyboard/internal/keyboard_parser/floris/KeyCode.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 b939872c31..c13d041734 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
@@ -53,7 +53,6 @@ object KeyCode {
const val CLIPBOARD_CLEAR_HISTORY = -36
//const val CLIPBOARD_CLEAR_FULL_HISTORY = -37
//const val CLIPBOARD_CLEAR_PRIMARY_CLIP = -38
- const val SEARCH = -39
//const val COMPACT_LAYOUT_TO_LEFT = -111
//const val COMPACT_LAYOUT_TO_RIGHT = -112
@@ -175,6 +174,7 @@ object KeyCode {
const val ALT_RIGHT = -10047
const val META_LEFT = -10048
const val META_RIGHT = -10049
+ const val SEARCH = -10050
// Intents
From 2445a236cc4510e346a41bef38476fa5fd702802 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Tue, 15 Jul 2025 02:02:59 +0300
Subject: [PATCH 12/56] Minor fixes
---
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 13 ++++---------
.../java/helium314/keyboard/latin/LatinIME.java | 2 +-
2 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 9bc511f4f9..253eedbf26 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -105,7 +105,6 @@ import kotlin.properties.Delegates
*/
class EmojiSearchActivity : ComponentActivity() {
private val colors = Settings.getValues().mColors
- private var enterAnimationComplete = false
private var startup = true
private lateinit var emojiPageKeyboardView: EmojiPageKeyboardView
private lateinit var keyboardParams: KeyboardParams
@@ -136,10 +135,6 @@ class EmojiSearchActivity : ComponentActivity() {
) {
Column(modifier = Modifier.wrapContentHeight().background(Color(colors.get(ColorType.MAIN_BACKGROUND)))
.onGloballyPositioned {
- if (startup && enterAnimationComplete && imeVisible && isAlphaKeyboard()) {
- search(searchText)
- return@onGloballyPositioned
- }
if (!startup && !imeVisible) {
imeClosed = true
cancel()
@@ -177,7 +172,8 @@ class EmojiSearchActivity : ComponentActivity() {
BasicTextField(
value = text,
modifier = Modifier.fillMaxWidth().heightIn(20.dp, 30.dp).focusRequester(focusRequester),
- textStyle = TextStyle(textDirection = TextDirection.Content, color = textFieldColors.unfocusedTextColor),
+ textStyle = TextStyle(textDirection = TextDirection.Content,
+ color = textFieldColors.unfocusedTextColor),
onValueChange = { it: TextFieldValue ->
text = it
search(it.text)
@@ -185,8 +181,7 @@ class EmojiSearchActivity : ComponentActivity() {
enabled = true,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
- platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))
- ),
+ platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))),
keyboardActions = KeyboardActions(onDone = { finish() }),
singleLine = true,
cursorBrush = SolidColor(textFieldColors.cursorColor)
@@ -219,7 +214,7 @@ class EmojiSearchActivity : ComponentActivity() {
}
override fun onEnterAnimationComplete() {
- enterAnimationComplete = true
+ search(searchText)
}
override fun onStop() {
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index bea625d27c..c942b088cc 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -1877,7 +1877,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
onTextInput(intent.getStringExtra(EmojiSearchActivity.EMOJI_KEY));
}
}
- return START_STICKY;
+ return START_NOT_STICKY;
}
return super.onStartCommand(intent, flags, startId);
From 7e005d3d4ba8395f8ee8190cdf956e20afb80d95 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Fri, 18 Jul 2025 05:21:38 +0300
Subject: [PATCH 13/56] Use `hintLocales`
---
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 10 +++++-----
.../keyboard/latin/inputlogic/InputLogic.java | 8 ++++----
.../keyboard/latin/utils/DictionaryInfoUtils.kt | 7 +++++++
3 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 253eedbf26..6e1dc8ae93 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -64,6 +64,8 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.PlatformImeOptions
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.text.intl.Locale
+import androidx.compose.ui.text.intl.LocaleList
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -110,6 +112,7 @@ class EmojiSearchActivity : ComponentActivity() {
private lateinit var keyboardParams: KeyboardParams
private var keyWidth by Delegates.notNull()
private var keyHeight by Delegates.notNull()
+ private lateinit var hintLocales: LocaleList
private var pressedKey: Key? = null
private var imeClosed = false
@@ -181,6 +184,7 @@ class EmojiSearchActivity : ComponentActivity() {
enabled = true,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
+ hintLocales = hintLocales,
platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))),
keyboardActions = KeyboardActions(onDone = { finish() }),
singleLine = true,
@@ -233,6 +237,7 @@ class EmojiSearchActivity : ComponentActivity() {
}
private fun init() {
+ hintLocales = LocaleList(DictionaryInfoUtils.getLocalesWithEmojiDicts(this).map { Locale(it.toLanguageTag()) })
val keyboardWidth = ResourceUtils.getKeyboardWidth(this, Settings.getValues())
val layoutSet = KeyboardLayoutSet.Builder(this, null).setSubtype(RichInputMethodSubtype.emojiSubtype)
.setKeyboardGeometry(keyboardWidth, EmojiLayoutParams(resources).emojiKeyboardHeight).build()
@@ -321,11 +326,6 @@ class EmojiSearchActivity : ComponentActivity() {
private var dictionaryFacilitator: SingleDictionaryFacilitator? = null
private var searchText: String = ""
- fun isSupported(context: Context): Boolean {
- initDictionaryFacilitator(context)
- return dictionaryFacilitator != null
- }
-
fun decodePrivateImeOptions(editorInfo: EditorInfo?): PrivateImeOptions = PrivateImeOptions(
editorInfo?.privateImeOptions?.takeIf { it.startsWith(PRIVATE_IME_OPTIONS_PREFIX) }
?.substring(PRIVATE_IME_OPTIONS_PREFIX.length + 1)?.toInt() ?: 0)
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 e578ae332f..5e9af489fd 100644
--- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
+++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
@@ -781,12 +781,12 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp
mLatinIME.onTextInput(TimestampKt.getTimestamp(mLatinIME));
break;
case KeyCode.SEARCH:
- if (EmojiSearchActivity.Companion.isSupported(mLatinIME)) {
- commitTyped(Settings.getValues(), LastComposedWord.NOT_A_SEPARATOR);
- mLatinIME.launchEmojiSearch();
- } else {
+ if (DictionaryInfoUtils.getLocalesWithEmojiDicts(mLatinIME).isEmpty()) {
// todo: open dictionary settings?
onSettingsKeyPressed();
+ } else {
+ commitTyped(Settings.getValues(), LastComposedWord.NOT_A_SEPARATOR);
+ mLatinIME.launchEmojiSearch();
}
break;
case KeyCode.SEND_INTENT_ONE, KeyCode.SEND_INTENT_TWO, KeyCode.SEND_INTENT_THREE:
diff --git a/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt
index 05417fdb31..2ffccd8e21 100644
--- a/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt
+++ b/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt
@@ -8,6 +8,8 @@ package helium314.keyboard.latin.utils
import android.content.Context
import android.text.TextUtils
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils
+import helium314.keyboard.latin.Dictionary
+import helium314.keyboard.latin.RichInputMethodManager
import helium314.keyboard.latin.common.FileUtils
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
import helium314.keyboard.latin.common.loopOverCodePoints
@@ -101,6 +103,11 @@ object DictionaryInfoUtils {
return absoluteDirectoryName
}
+ @JvmStatic
+ fun getLocalesWithEmojiDicts(context: Context): List =
+ RichInputMethodManager.Companion.getInstance().getMyEnabledInputMethodSubtypes(true)
+ .map { it.locale() }.filter { getCachedDictForLocaleAndType(it, Dictionary.TYPE_EMOJI, context) != null }
+
@JvmStatic
fun getCachedDictForLocaleAndType(locale: Locale, type: String, context: Context): File? =
getCachedDictsForLocale(locale, context).firstOrNull { it.name.substringBefore("_") == type }
From 00b196c941c9cf9818747b53be9efede299d017e Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Fri, 18 Jul 2025 05:27:08 +0300
Subject: [PATCH 14/56] fix build
---
.../java/helium314/keyboard/latin/inputlogic/InputLogic.java | 1 +
1 file changed, 1 insertion(+)
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 5e9af489fd..645fd8270e 100644
--- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
+++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
@@ -49,6 +49,7 @@
import helium314.keyboard.latin.settings.SpacingAndPunctuations;
import helium314.keyboard.latin.suggestions.SuggestionStripViewAccessor;
import helium314.keyboard.latin.utils.AsyncResultHolder;
+import helium314.keyboard.latin.utils.DictionaryInfoUtils;
import helium314.keyboard.latin.utils.InputTypeUtils;
import helium314.keyboard.latin.utils.IntentUtils;
import helium314.keyboard.latin.utils.Log;
From 763d24794ec5fcbd04ea35c4280f1ef2298a1908 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 19 Jul 2025 18:29:21 +0300
Subject: [PATCH 15/56] Cosmetics, doc
---
.../keyboard/emoji/EmojiSearchActivity.kt | 36 +++++++++----------
.../helium314/keyboard/latin/LatinIME.java | 2 +-
2 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 6e1dc8ae93..9f0e33e40b 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -108,11 +108,11 @@ import kotlin.properties.Delegates
class EmojiSearchActivity : ComponentActivity() {
private val colors = Settings.getValues().mColors
private var startup = true
+ private lateinit var hintLocales: LocaleList
private lateinit var emojiPageKeyboardView: EmojiPageKeyboardView
private lateinit var keyboardParams: KeyboardParams
private var keyWidth by Delegates.notNull()
private var keyHeight by Delegates.notNull()
- private lateinit var hintLocales: LocaleList
private var pressedKey: Key? = null
private var imeClosed = false
@@ -126,12 +126,8 @@ class EmojiSearchActivity : ComponentActivity() {
Surface(modifier = Modifier.fillMaxSize(), color = Color(0x80000000)) {
val imeVisible = WindowInsets.isImeVisible
val localDensity = LocalDensity.current
- var heightPx by remember {
- mutableIntStateOf(0)
- }
- var heightDp by remember {
- mutableStateOf(0.dp)
- }
+ var heightPx by remember { mutableIntStateOf(0) }
+ var heightDp by remember { mutableStateOf(0.dp) }
Column(modifier = Modifier.fillMaxSize().clickable(onClick = { cancel() })
.windowInsetsPadding(WindowInsets.safeDrawing.exclude(WindowInsets(bottom = heightDp))),
verticalArrangement = Arrangement.Bottom
@@ -163,21 +159,20 @@ class EmojiSearchActivity : ComponentActivity() {
AndroidView({ emojiPageKeyboardView }, modifier = Modifier.wrapContentHeight().fillMaxWidth())
val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf(TextFieldValue(searchText, selection = TextRange(searchText.length))) }
- val textFieldColors = TextFieldDefaults.colors()
- .copy(unfocusedContainerColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_BACKGROUND)),
- unfocusedTextColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
- cursorColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
- unfocusedLeadingIconColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
- unfocusedTrailingIconColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
- unfocusedPlaceholderColor = lerp(Color(colors.get(ColorType.FUNCTIONAL_KEY_BACKGROUND)),
- Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)), 0.5f))
+ val textFieldColors = TextFieldDefaults.colors().copy(
+ unfocusedContainerColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_BACKGROUND)),
+ unfocusedTextColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
+ cursorColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
+ unfocusedLeadingIconColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
+ unfocusedTrailingIconColor = Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)),
+ unfocusedPlaceholderColor = lerp(Color(colors.get(ColorType.FUNCTIONAL_KEY_BACKGROUND)),
+ Color(colors.get(ColorType.FUNCTIONAL_KEY_TEXT)), 0.5f))
CompositionLocalProvider(LocalTextSelectionColors provides textFieldColors.textSelectionColors) {
BasicTextField(
value = text,
modifier = Modifier.fillMaxWidth().heightIn(20.dp, 30.dp).focusRequester(focusRequester),
- textStyle = TextStyle(textDirection = TextDirection.Content,
- color = textFieldColors.unfocusedTextColor),
- onValueChange = { it: TextFieldValue ->
+ textStyle = TextStyle(textDirection = TextDirection.Content, color = textFieldColors.unfocusedTextColor),
+ onValueChange = {
text = it
search(it.text)
},
@@ -193,6 +188,11 @@ class EmojiSearchActivity : ComponentActivity() {
TextFieldDefaults.DecorationBox(
value = text.text,
colors = textFieldColors,
+
+ /**
+ * This is the reason for not using [androidx.compose.material3.TextField],
+ * which uses [TextFieldDefaults.contentPaddingWithoutLabel]
+ */
contentPadding = PaddingValues(2.dp),
visualTransformation = VisualTransformation.None,
innerTextField = it,
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index c942b088cc..4b44e33335 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -1868,7 +1868,7 @@ public void launchEmojiSearch() {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent != null && EmojiSearchActivity.EMOJI_SEARCH_DONE_ACTION.equals(intent.getAction())) {
+ if (intent != null && EmojiSearchActivity.EMOJI_SEARCH_DONE_ACTION.equals(intent.getAction()) && ! isEmojiSearch()) {
if (intent.getBooleanExtra(EmojiSearchActivity.IME_CLOSED_KEY, false)) {
requestHideSelf(0);
} else {
From ee1ce7cbcdf3c5c48fa4d286d128276f56e7c8b7 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Tue, 22 Jul 2025 17:18:48 +0300
Subject: [PATCH 16/56] Fix crash on IME switch
---
app/src/main/java/helium314/keyboard/latin/LatinIME.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 4b44e33335..35bae52fa9 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -1877,6 +1877,8 @@ public int onStartCommand(Intent intent, int flags, int startId) {
onTextInput(intent.getStringExtra(EmojiSearchActivity.EMOJI_KEY));
}
}
+
+ stopSelf(); // Allow the service to be destroyed on next IME switch
return START_NOT_STICKY;
}
From a855413223a599cb4d165abcd547bda65609ae00 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 26 Jul 2025 16:30:32 +0300
Subject: [PATCH 17/56] Minor fix
---
app/src/main/java/helium314/keyboard/latin/LatinIME.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 35bae52fa9..02b47e43ef 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -1878,7 +1878,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
}
}
- stopSelf(); // Allow the service to be destroyed on next IME switch
+ stopSelf(startId); // Allow the service to be destroyed when unbound
return START_NOT_STICKY;
}
From 485783a2392173b365b9f1b6f00c61908ee55a9a Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Mon, 28 Jul 2025 23:52:13 +0300
Subject: [PATCH 18/56] Add timing logs, Prepare for NO_LOCALE_PER_APP
---
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 14 ++++++++++----
.../java/helium314/keyboard/latin/LatinIME.java | 1 +
.../keyboard/latin/inputlogic/InputLogic.java | 1 -
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 9f0e33e40b..ed1528b9a0 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -96,6 +96,7 @@ import helium314.keyboard.latin.common.StringUtils
import helium314.keyboard.latin.common.splitOnWhitespace
import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.DictionaryInfoUtils
+import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.ResourceUtils
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.CloseIcon
@@ -180,7 +181,7 @@ class EmojiSearchActivity : ComponentActivity() {
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
hintLocales = hintLocales,
- platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))),
+ platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx), this@EmojiSearchActivity))),
keyboardActions = KeyboardActions(onDone = { finish() }),
singleLine = true,
cursorBrush = SolidColor(textFieldColors.cursorColor)
@@ -218,7 +219,9 @@ class EmojiSearchActivity : ComponentActivity() {
}
override fun onEnterAnimationComplete() {
+ Log.d("emoji-search", "onEnterAnimationComplete")
search(searchText)
+ Log.d("emoji-search", "initial search done")
}
override fun onStop() {
@@ -237,6 +240,7 @@ class EmojiSearchActivity : ComponentActivity() {
}
private fun init() {
+ Log.d("emoji-search", "init start")
hintLocales = LocaleList(DictionaryInfoUtils.getLocalesWithEmojiDicts(this).map { Locale(it.toLanguageTag()) })
val keyboardWidth = ResourceUtils.getKeyboardWidth(this, Settings.getValues())
val layoutSet = KeyboardLayoutSet.Builder(this, null).setSubtype(RichInputMethodSubtype.emojiSubtype)
@@ -274,6 +278,7 @@ class EmojiSearchActivity : ComponentActivity() {
override fun getDescription(emoji: String): String? = if (Settings.getValues().mShowEmojiDescriptions)
dictionaryFacilitator?.getWordProperty(getEmojiNeutralVersion(emoji))?.mShortcutTargets[0]?.mWord else null
})
+ Log.d("emoji-search", "init end")
}
private fun isAlphaKeyboard(): Boolean = !(KeyboardSwitcher.getInstance().isShowingEmojiPalettes
@@ -328,10 +333,11 @@ class EmojiSearchActivity : ComponentActivity() {
fun decodePrivateImeOptions(editorInfo: EditorInfo?): PrivateImeOptions = PrivateImeOptions(
editorInfo?.privateImeOptions?.takeIf { it.startsWith(PRIVATE_IME_OPTIONS_PREFIX) }
- ?.substring(PRIVATE_IME_OPTIONS_PREFIX.length + 1)?.toInt() ?: 0)
+ ?.let { it.substring(PRIVATE_IME_OPTIONS_PREFIX.length + 1, it.indexOf(',')) }?.toInt() ?: 0)
- private fun encodePrivateImeOptions(privateImeOptions: PrivateImeOptions) =
- "$PRIVATE_IME_OPTIONS_PREFIX,${privateImeOptions.height}"
+ private fun encodePrivateImeOptions(privateImeOptions: PrivateImeOptions, context: Context) =
+ "$PRIVATE_IME_OPTIONS_PREFIX.${privateImeOptions.height},"
+ //todo: add ${context.packageName}.${Constants.ImeOption.NO_LOCALE_PER_APP}
private fun initDictionaryFacilitator(context: Context) {
val locale = RichInputMethodManager.getInstance().currentSubtype.locale
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 02b47e43ef..516004885e 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -1862,6 +1862,7 @@ void launchSettings() {
}
public void launchEmojiSearch() {
+ Log.d("emoji-search", "before activity launch");
startActivity(new Intent().setClass(this, EmojiSearchActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_MULTIPLE_TASK));
}
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 645fd8270e..3f9980a30c 100644
--- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
+++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
@@ -25,7 +25,6 @@
import helium314.keyboard.event.InputTransaction;
import helium314.keyboard.keyboard.Keyboard;
import helium314.keyboard.keyboard.KeyboardSwitcher;
-import helium314.keyboard.keyboard.emoji.EmojiSearchActivity;
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
import helium314.keyboard.latin.Dictionary;
import helium314.keyboard.latin.DictionaryFacilitator;
From 4d381c346867ee96d32ee6c87e200f93684d66a1 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Thu, 31 Jul 2025 01:33:47 +0300
Subject: [PATCH 19/56] Fix search startup instability, support orientation
change
---
app/src/main/AndroidManifest.xml | 2 +-
.../keyboard/keyboard/KeyboardSwitcher.java | 5 +-
.../keyboard/emoji/EmojiPalettesView.java | 5 +-
.../keyboard/emoji/EmojiSearchActivity.kt | 49 ++++++++++++++-----
.../helium314/keyboard/latin/LatinIME.java | 6 ++-
5 files changed, 50 insertions(+), 17 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1695bf9c5a..6dd5eb90a4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -78,7 +78,7 @@ SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
+ android:windowSoftInputMode="adjustNothing|stateAlwaysVisible" android:configChanges="orientation|screenSize"/>
()
private lateinit var hintLocales: LocaleList
private lateinit var emojiPageKeyboardView: EmojiPageKeyboardView
private lateinit var keyboardParams: KeyboardParams
@@ -125,22 +128,25 @@ class EmojiSearchActivity : ComponentActivity() {
setContent {
LocalContext.current.setTheme(KeyboardTheme.getKeyboardTheme(this).mStyleId)
Surface(modifier = Modifier.fillMaxSize(), color = Color(0x80000000)) {
- val imeVisible = WindowInsets.isImeVisible
- val localDensity = LocalDensity.current
- var heightPx by remember { mutableIntStateOf(0) }
var heightDp by remember { mutableStateOf(0.dp) }
Column(modifier = Modifier.fillMaxSize().clickable(onClick = { cancel() })
.windowInsetsPadding(WindowInsets.safeDrawing.exclude(WindowInsets(bottom = heightDp))),
verticalArrangement = Arrangement.Bottom
) {
+ val localDensity = LocalDensity.current
+ var heightPx by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.wrapContentHeight().background(Color(colors.get(ColorType.MAIN_BACKGROUND)))
.onGloballyPositioned {
- if (!startup && !imeVisible) {
+ val bottom = it.localToScreen(Offset(0f, it.size.height.toFloat())).y.toInt()
+ val imeVisible = bottom < screenHeight - 100
+ || KeyboardSwitcher.getInstance().keyboardSwitchState != KeyboardSwitcher.KeyboardSwitchState.HIDDEN
+ if (imeVisible && isAlphaKeyboard()) imeOpened = true
+ if (imeOpened && !imeVisible) {
imeClosed = true
cancel()
return@onGloballyPositioned
}
- if (!startup && !isAlphaKeyboard()) {
+ if (imeOpened && !isAlphaKeyboard()) {
cancel()
return@onGloballyPositioned
}
@@ -157,7 +163,9 @@ class EmojiSearchActivity : ComponentActivity() {
color = Color(colors.get(ColorType.EMOJI_KEY_TEXT)),
modifier = Modifier.fillMaxWidth().align(Alignment.CenterVertically))
}
- AndroidView({ emojiPageKeyboardView }, modifier = Modifier.wrapContentHeight().fillMaxWidth())
+ key(emojiPageKeyboardView) {
+ AndroidView({ emojiPageKeyboardView }, modifier = Modifier.wrapContentHeight().fillMaxWidth())
+ }
val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf(TextFieldValue(searchText, selection = TextRange(searchText.length))) }
val textFieldColors = TextFieldDefaults.colors().copy(
@@ -181,7 +189,8 @@ class EmojiSearchActivity : ComponentActivity() {
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
hintLocales = hintLocales,
- platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx), this@EmojiSearchActivity))),
+ platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx),
+ this@EmojiSearchActivity))),
keyboardActions = KeyboardActions(onDone = { finish() }),
singleLine = true,
cursorBrush = SolidColor(textFieldColors.cursorColor)
@@ -224,6 +233,14 @@ class EmojiSearchActivity : ComponentActivity() {
Log.d("emoji-search", "initial search done")
}
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ init()
+ imeOpened = false
+ firstSearchDone = false
+ search(searchText)
+ }
+
override fun onStop() {
val intent = Intent(this, LatinIME::class.java).setAction(EMOJI_SEARCH_DONE_ACTION)
.putExtra(IME_CLOSED_KEY, imeClosed)
@@ -241,6 +258,8 @@ class EmojiSearchActivity : ComponentActivity() {
private fun init() {
Log.d("emoji-search", "init start")
+ @Suppress("DEPRECATION")
+ screenHeight = windowManager.defaultDisplay.height
hintLocales = LocaleList(DictionaryInfoUtils.getLocalesWithEmojiDicts(this).map { Locale(it.toLanguageTag()) })
val keyboardWidth = ResourceUtils.getKeyboardWidth(this, Settings.getValues())
val layoutSet = KeyboardLayoutSet.Builder(this, null).setSubtype(RichInputMethodSubtype.emojiSubtype)
@@ -281,8 +300,8 @@ class EmojiSearchActivity : ComponentActivity() {
Log.d("emoji-search", "init end")
}
- private fun isAlphaKeyboard(): Boolean = !(KeyboardSwitcher.getInstance().isShowingEmojiPalettes
- || KeyboardSwitcher.getInstance().isShowingClipboardHistory)
+ private fun isAlphaKeyboard() = KeyboardSwitcher.getInstance().keyboardSwitchState !in
+ setOf(KeyboardSwitcher.KeyboardSwitchState.EMOJI, KeyboardSwitcher.KeyboardSwitchState.CLIPBOARD)
private fun search(text: String) {
initDictionaryFacilitator(this)
@@ -291,12 +310,16 @@ class EmojiSearchActivity : ComponentActivity() {
return
}
- if (!startup && text == searchText) {
+ if (firstSearchDone && text == searchText) {
+ return
+ }
+
+ if (KeyboardSwitcher.getInstance().keyboard == null) {
return
}
searchText = text
- startup = false
+ firstSearchDone = true
val keyboard = emojiPageKeyboardView.keyboard as DynamicGridKeyboard
keyboard.removeAllKeys()
pressedKey = null
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 516004885e..d629405aa8 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -869,6 +869,10 @@ public void setInputView(final View view) {
mInputView = view;
mInsetsUpdater = ViewOutlineProviderUtilsKt.setInsetsOutlineProvider(view);
updateSoftInputWindowLayoutParameters();
+ updateSuggestionStripView(view);
+ }
+
+ public void updateSuggestionStripView(View view) {
mSuggestionStripView = mSettings.getCurrent().mToolbarMode == ToolbarMode.HIDDEN || isEmojiSearch()?
null : view.findViewById(R.id.suggestion_strip_view);
if (hasSuggestionStripView()) {
@@ -1616,7 +1620,7 @@ void showGesturePreviewAndSuggestionStrip(@NonNull final SuggestedWords suggeste
dismissGestureFloatingPreviewText /* dismissDelayed */);
}
- private boolean hasSuggestionStripView() {
+ public boolean hasSuggestionStripView() {
return null != mSuggestionStripView;
}
From 26972b01c9775005c8e8790d15efd49dbed1c837 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Thu, 31 Jul 2025 01:53:56 +0300
Subject: [PATCH 20/56] Fix failing tests
---
.../main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java
index d4d5d32de2..8a4e11ad66 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardSwitcher.java
@@ -113,7 +113,7 @@ public void updateKeyboardTheme(@NonNull Context displayContext) {
settings.loadSettings(displayContext, settings.getCurrent().mLocale, settings.getCurrent().mInputAttributes);
if (mKeyboardView != null)
mLatinIME.setInputView(onCreateInputView(displayContext, mIsHardwareAcceleratedDrawingEnabled));
- } else if (mLatinIME.hasSuggestionStripView()
+ } else if (mCurrentInputView != null && mLatinIME.hasSuggestionStripView()
== (Settings.getValues().mToolbarMode == ToolbarMode.HIDDEN || mLatinIME.isEmojiSearch())) {
mLatinIME.updateSuggestionStripView(mCurrentInputView);
}
From d1042dc21089c1674bad0dd88d5c132c4de1e7ce Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 9 Aug 2025 23:48:26 +0300
Subject: [PATCH 21/56] Minor improvements
---
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 1ae5be20b5..46ab78b3cd 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -139,8 +139,8 @@ class EmojiSearchActivity : ComponentActivity() {
.onGloballyPositioned {
val bottom = it.localToScreen(Offset(0f, it.size.height.toFloat())).y.toInt()
val imeVisible = bottom < screenHeight - 100
- || KeyboardSwitcher.getInstance().keyboardSwitchState != KeyboardSwitcher.KeyboardSwitchState.HIDDEN
- if (imeVisible && isAlphaKeyboard()) imeOpened = true
+ Log.d("emoji-search", "imeVisible: $imeVisible, imeOpened: $imeOpened, bottom: $bottom, " +
+ "keyboardState: ${KeyboardSwitcher.getInstance().keyboardSwitchState}")
if (imeOpened && !imeVisible) {
imeClosed = true
cancel()
@@ -150,6 +150,9 @@ class EmojiSearchActivity : ComponentActivity() {
cancel()
return@onGloballyPositioned
}
+ if (imeVisible && isAlphaKeyboard()) {
+ imeOpened = true
+ }
heightPx = it.size.height
heightDp = with(localDensity) { it.size.height.toDp() }
}) {
@@ -315,6 +318,7 @@ class EmojiSearchActivity : ComponentActivity() {
}
if (KeyboardSwitcher.getInstance().keyboard == null) {
+ /** Avoid crash in [SingleDictionaryFacilitator.getSuggestions] */
return
}
From d58f7a24c77b3b3be313ee11d166d4caf2c54f52 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Fri, 15 Aug 2025 04:17:25 +0300
Subject: [PATCH 22/56] Minor fix
---
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 46ab78b3cd..607dee2d9b 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -136,7 +136,7 @@ class EmojiSearchActivity : ComponentActivity() {
val localDensity = LocalDensity.current
var heightPx by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.wrapContentHeight().background(Color(colors.get(ColorType.MAIN_BACKGROUND)))
- .onGloballyPositioned {
+ .clickable(onClick = { }).onGloballyPositioned {
val bottom = it.localToScreen(Offset(0f, it.size.height.toFloat())).y.toInt()
val imeVisible = bottom < screenHeight - 100
Log.d("emoji-search", "imeVisible: $imeVisible, imeOpened: $imeOpened, bottom: $bottom, " +
From cece5166557eeb89bb8220a1d20352a947212b99 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 16 Aug 2025 03:29:55 +0300
Subject: [PATCH 23/56] Minor fix
---
app/build.gradle.kts | 2 +-
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 14 ++++++++++----
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 7f38a5b4b9..401cdab21e 100755
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -114,7 +114,7 @@ dependencies {
// compose
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
- implementation(platform("androidx.compose:compose-bom-beta:2025.06.01"))
+ implementation(platform("androidx.compose:compose-bom:2025.08.00"))
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling")
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 607dee2d9b..b00ca81b15 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
+import android.os.Handler
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.activity.ComponentActivity
@@ -118,6 +119,7 @@ class EmojiSearchActivity : ComponentActivity() {
private var keyWidth by Delegates.notNull()
private var keyHeight by Delegates.notNull()
private var pressedKey: Key? = null
+ private var imeVisible = false
private var imeClosed = false
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@@ -138,13 +140,17 @@ class EmojiSearchActivity : ComponentActivity() {
Column(modifier = Modifier.wrapContentHeight().background(Color(colors.get(ColorType.MAIN_BACKGROUND)))
.clickable(onClick = { }).onGloballyPositioned {
val bottom = it.localToScreen(Offset(0f, it.size.height.toFloat())).y.toInt()
- val imeVisible = bottom < screenHeight - 100
+ imeVisible = bottom < screenHeight - 100
Log.d("emoji-search", "imeVisible: $imeVisible, imeOpened: $imeOpened, bottom: $bottom, " +
"keyboardState: ${KeyboardSwitcher.getInstance().keyboardSwitchState}")
if (imeOpened && !imeVisible) {
- imeClosed = true
- cancel()
- return@onGloballyPositioned
+ Handler(this@EmojiSearchActivity.mainLooper).postDelayed({
+ if (!imeVisible) {
+ Log.d("emoji-search", "IME closed")
+ imeClosed = true
+ cancel()
+ }
+ }, 200)
}
if (imeOpened && !isAlphaKeyboard()) {
cancel()
From 104a33ef5091d9244d6e0b2dcfd03c71e45b911e Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 16 Aug 2025 04:03:08 +0300
Subject: [PATCH 24/56] Minor fix
---
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index b00ca81b15..66bc290bb9 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -138,7 +138,7 @@ class EmojiSearchActivity : ComponentActivity() {
val localDensity = LocalDensity.current
var heightPx by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.wrapContentHeight().background(Color(colors.get(ColorType.MAIN_BACKGROUND)))
- .clickable(onClick = { }).onGloballyPositioned {
+ .clickable(false) {}.onGloballyPositioned {
val bottom = it.localToScreen(Offset(0f, it.size.height.toFloat())).y.toInt()
imeVisible = bottom < screenHeight - 100
Log.d("emoji-search", "imeVisible: $imeVisible, imeOpened: $imeOpened, bottom: $bottom, " +
From dd37915d8cf29a86d57027ab47b4bd523166c09a Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Thu, 21 Aug 2025 03:41:26 +0300
Subject: [PATCH 25/56] Another activity closing fix, Fix dynamic night color
---
.../keyboard/emoji/EmojiSearchActivity.kt | 21 +++++++++++--------
.../helium314/keyboard/latin/common/Colors.kt | 2 +-
2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 66bc290bb9..2388ccd1f7 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -94,7 +94,7 @@ import helium314.keyboard.latin.RichInputMethodManager
import helium314.keyboard.latin.RichInputMethodSubtype
import helium314.keyboard.latin.SingleDictionaryFacilitator
import helium314.keyboard.latin.common.ColorType
-import helium314.keyboard.latin.common.StringUtils
+import helium314.keyboard.latin.common.mightBeEmoji
import helium314.keyboard.latin.common.splitOnWhitespace
import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.DictionaryInfoUtils
@@ -122,6 +122,14 @@ class EmojiSearchActivity : ComponentActivity() {
private var imeVisible = false
private var imeClosed = false
+ private val closer = Runnable {
+ if (!imeVisible) {
+ Log.d("emoji-search", "IME closed")
+ imeClosed = true
+ cancel()
+ }
+ }
+
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -144,13 +152,7 @@ class EmojiSearchActivity : ComponentActivity() {
Log.d("emoji-search", "imeVisible: $imeVisible, imeOpened: $imeOpened, bottom: $bottom, " +
"keyboardState: ${KeyboardSwitcher.getInstance().keyboardSwitchState}")
if (imeOpened && !imeVisible) {
- Handler(this@EmojiSearchActivity.mainLooper).postDelayed({
- if (!imeVisible) {
- Log.d("emoji-search", "IME closed")
- imeClosed = true
- cancel()
- }
- }, 200)
+ Handler(this@EmojiSearchActivity.mainLooper).postDelayed(closer, 200)
}
if (imeOpened && !isAlphaKeyboard()) {
cancel()
@@ -158,6 +160,7 @@ class EmojiSearchActivity : ComponentActivity() {
}
if (imeVisible && isAlphaKeyboard()) {
imeOpened = true
+ Handler(this@EmojiSearchActivity.mainLooper).removeCallbacks(closer)
}
heightPx = it.size.height
heightDp = with(localDensity) { it.size.height.toDp() }
@@ -333,7 +336,7 @@ class EmojiSearchActivity : ComponentActivity() {
val keyboard = emojiPageKeyboardView.keyboard as DynamicGridKeyboard
keyboard.removeAllKeys()
pressedKey = null
- dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { StringUtils.mightBeEmoji(it.word) }.forEach {
+ dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { mightBeEmoji(it.word) }.forEach {
val emoji = getEmojiDefaultVersion(it.word)
val popupSpec = getEmojiPopupSpec(emoji)
val keyParams = Key.KeyParams(emoji, emoji.getCode(), if (popupSpec != null) EMOJI_HINT_LABEL else null, popupSpec,
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 7081228280..26a366d0a6 100644
--- a/app/src/main/java/helium314/keyboard/latin/common/Colors.kt
+++ b/app/src/main/java/helium314/keyboard/latin/common/Colors.kt
@@ -281,7 +281,7 @@ class DynamicColors(context: Context, override val themeStyle: String, override
KEY_ICON, POPUP_KEY_ICON, ONE_HANDED_MODE_BUTTON, EMOJI_CATEGORY, TOOL_BAR_KEY, FUNCTIONAL_KEY_TEXT -> keyText
KEY_HINT_TEXT -> keyHintText
SPACE_BAR_TEXT -> spaceBarText
- FUNCTIONAL_KEY_BACKGROUND -> functionalKey
+ FUNCTIONAL_KEY_BACKGROUND -> if (!isNight) functionalKey else doubleAdjustedKeyBackground
SPACE_BAR_BACKGROUND -> spaceBar
MORE_SUGGESTIONS_WORD_BACKGROUND, MAIN_BACKGROUND -> background
KEY_BACKGROUND -> keyBackground
From 534191a6f9d384f267620380a8d663f5e15c3e0d Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 23 Aug 2025 05:14:11 +0300
Subject: [PATCH 26/56] Another activity closing fix
---
.../keyboard/emoji/EmojiSearchActivity.kt | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 2388ccd1f7..4a8a1483b5 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -149,8 +149,8 @@ class EmojiSearchActivity : ComponentActivity() {
.clickable(false) {}.onGloballyPositioned {
val bottom = it.localToScreen(Offset(0f, it.size.height.toFloat())).y.toInt()
imeVisible = bottom < screenHeight - 100
- Log.d("emoji-search", "imeVisible: $imeVisible, imeOpened: $imeOpened, bottom: $bottom, " +
- "keyboardState: ${KeyboardSwitcher.getInstance().keyboardSwitchState}")
+ Log.d("emoji-search", "imeVisible: $imeVisible, firstSearchDone: $firstSearchDone, imeOpened: $imeOpened, " +
+ "bottom: $bottom, keyboardState: ${KeyboardSwitcher.getInstance().keyboardSwitchState}")
if (imeOpened && !imeVisible) {
Handler(this@EmojiSearchActivity.mainLooper).postDelayed(closer, 200)
}
@@ -158,7 +158,8 @@ class EmojiSearchActivity : ComponentActivity() {
cancel()
return@onGloballyPositioned
}
- if (imeVisible && isAlphaKeyboard()) {
+ if (imeVisible && firstSearchDone && isAlphaKeyboard()) {
+ Log.d("emoji-search", "IME opened in onGloballyPositioned")
imeOpened = true
Handler(this@EmojiSearchActivity.mainLooper).removeCallbacks(closer)
}
@@ -248,6 +249,7 @@ class EmojiSearchActivity : ComponentActivity() {
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
init()
+ imeVisible = false
imeOpened = false
firstSearchDone = false
search(searchText)
@@ -272,6 +274,7 @@ class EmojiSearchActivity : ComponentActivity() {
Log.d("emoji-search", "init start")
@Suppress("DEPRECATION")
screenHeight = windowManager.defaultDisplay.height
+ Log.d("emoji-search", "screenHeight: $screenHeight")
hintLocales = LocaleList(DictionaryInfoUtils.getLocalesWithEmojiDicts(this).map { Locale(it.toLanguageTag()) })
val keyboardWidth = ResourceUtils.getKeyboardWidth(this, Settings.getValues())
val layoutSet = KeyboardLayoutSet.Builder(this, null).setSubtype(RichInputMethodSubtype.emojiSubtype)
@@ -331,8 +334,6 @@ class EmojiSearchActivity : ComponentActivity() {
return
}
- searchText = text
- firstSearchDone = true
val keyboard = emojiPageKeyboardView.keyboard as DynamicGridKeyboard
keyboard.removeAllKeys()
pressedKey = null
@@ -349,6 +350,13 @@ class EmojiSearchActivity : ComponentActivity() {
pressedKey = key
}
emojiPageKeyboardView.invalidate()
+
+ searchText = text
+ firstSearchDone = true
+ if (imeVisible && !imeOpened) {
+ Log.d("emoji-search", "IME opened in search")
+ imeOpened = true
+ }
}
private fun cancel() {
From 885f45b83a161ed3d8c222c5784c463c94fdf47c Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Wed, 24 Sep 2025 04:40:47 +0300
Subject: [PATCH 27/56] Use `isEmoji`
---
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 4a8a1483b5..de08209a08 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -94,7 +94,7 @@ import helium314.keyboard.latin.RichInputMethodManager
import helium314.keyboard.latin.RichInputMethodSubtype
import helium314.keyboard.latin.SingleDictionaryFacilitator
import helium314.keyboard.latin.common.ColorType
-import helium314.keyboard.latin.common.mightBeEmoji
+import helium314.keyboard.latin.common.isEmoji
import helium314.keyboard.latin.common.splitOnWhitespace
import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.DictionaryInfoUtils
@@ -337,7 +337,7 @@ class EmojiSearchActivity : ComponentActivity() {
val keyboard = emojiPageKeyboardView.keyboard as DynamicGridKeyboard
keyboard.removeAllKeys()
pressedKey = null
- dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { mightBeEmoji(it.word) }.forEach {
+ dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { isEmoji(it.word) }.forEach {
val emoji = getEmojiDefaultVersion(it.word)
val popupSpec = getEmojiPopupSpec(emoji)
val keyParams = Key.KeyParams(emoji, emoji.getCode(), if (popupSpec != null) EMOJI_HINT_LABEL else null, popupSpec,
From f7ec68f279bc9662b66f68fd73172323afd5a3fc Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 27 Sep 2025 20:07:14 +0300
Subject: [PATCH 28/56] Add todo
---
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index de08209a08..5d4f6b307f 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -337,6 +337,7 @@ class EmojiSearchActivity : ComponentActivity() {
val keyboard = emojiPageKeyboardView.keyboard as DynamicGridKeyboard
keyboard.removeAllKeys()
pressedKey = null
+ //todo: change to suggestion.isEmoji()
dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { isEmoji(it.word) }.forEach {
val emoji = getEmojiDefaultVersion(it.word)
val popupSpec = getEmojiPopupSpec(emoji)
From 876c5e8fbbaad321cbd5e73c68624cf17d2dd547 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 1 Nov 2025 02:37:08 +0200
Subject: [PATCH 29/56] Cleanup
---
.../keyboard/keyboard/emoji/EmojiPalettesView.java | 6 ++----
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 6 ++----
2 files changed, 4 insertions(+), 8 deletions(-)
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 cc7281e208..a952f8839c 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
@@ -342,7 +342,7 @@ public void startEmojiPalettes(final KeyVisualAttributes keyVisualAttr,
initDictionaryFacilitator();
}
- public void addRecentKey(final Key key) {
+ void addRecentKey(final Key key) {
if (Settings.getValues().mIncognitoModeEnabled) {
// We do not want to log recent keys while being in incognito
return;
@@ -353,9 +353,7 @@ public void addRecentKey(final Key key) {
}
getRecentsKeyboard().addKeyFirst(key);
- if (mPager != null) {
- mPager.getAdapter().notifyItemChanged(mEmojiCategory.getRecentTabId());
- }
+ mPager.getAdapter().notifyItemChanged(mEmojiCategory.getRecentTabId());
}
private void setupBottomRowKeyboard(final EditorInfo editorInfo, final KeyboardActionListener keyboardActionListener) {
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 5d4f6b307f..144c8eeb0d 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -202,8 +202,7 @@ class EmojiSearchActivity : ComponentActivity() {
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
hintLocales = hintLocales,
- platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx),
- this@EmojiSearchActivity))),
+ platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))),
keyboardActions = KeyboardActions(onDone = { finish() }),
singleLine = true,
cursorBrush = SolidColor(textFieldColors.cursorColor)
@@ -380,9 +379,8 @@ class EmojiSearchActivity : ComponentActivity() {
editorInfo?.privateImeOptions?.takeIf { it.startsWith(PRIVATE_IME_OPTIONS_PREFIX) }
?.let { it.substring(PRIVATE_IME_OPTIONS_PREFIX.length + 1, it.indexOf(',')) }?.toInt() ?: 0)
- private fun encodePrivateImeOptions(privateImeOptions: PrivateImeOptions, context: Context) =
+ private fun encodePrivateImeOptions(privateImeOptions: PrivateImeOptions) =
"$PRIVATE_IME_OPTIONS_PREFIX.${privateImeOptions.height},"
- //todo: add ${context.packageName}.${Constants.ImeOption.NO_LOCALE_PER_APP}
private fun initDictionaryFacilitator(context: Context) {
val locale = RichInputMethodManager.getInstance().currentSubtype.locale
From b90fed331913e4d8808804855be205d595a3a987 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Wed, 5 Nov 2025 01:01:58 +0200
Subject: [PATCH 30/56] Fix main merge
---
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 144c8eeb0d..78b37d7ead 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -86,8 +86,8 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiDefaultVersi
import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiKeyDimensions
import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiNeutralVersion
import helium314.keyboard.keyboard.internal.keyboard_parser.getEmojiPopupSpec
-import helium314.keyboard.latin.Dictionary
-import helium314.keyboard.latin.DictionaryFactory
+import helium314.keyboard.latin.dictionary.Dictionary
+import helium314.keyboard.latin.dictionary.DictionaryFactory
import helium314.keyboard.latin.LatinIME
import helium314.keyboard.latin.R
import helium314.keyboard.latin.RichInputMethodManager
From 3f55c095b9555a7a36994460d1050e541544fbed Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Wed, 5 Nov 2025 01:06:29 +0200
Subject: [PATCH 31/56] Fix main merge
---
.../java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt
index 13dbc87fa2..618bba11a6 100644
--- a/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt
+++ b/app/src/main/java/helium314/keyboard/latin/utils/DictionaryInfoUtils.kt
@@ -8,7 +8,7 @@ package helium314.keyboard.latin.utils
import android.content.Context
import android.text.TextUtils
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils
-import helium314.keyboard.latin.Dictionary
+import helium314.keyboard.latin.dictionary.Dictionary
import helium314.keyboard.latin.RichInputMethodManager
import helium314.keyboard.latin.common.FileUtils
import helium314.keyboard.latin.common.LocaleUtils.constructLocale
From 76f768cb1ccf436b9045e82fa4fc7407aa0732eb Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Wed, 5 Nov 2025 01:34:13 +0200
Subject: [PATCH 32/56] Switch to abc keyboard
---
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 78b37d7ead..80b83859ec 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -63,6 +63,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PlatformImeOptions
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
@@ -199,8 +200,7 @@ class EmojiSearchActivity : ComponentActivity() {
search(it.text)
},
enabled = true,
- keyboardOptions = KeyboardOptions(
- imeAction = ImeAction.Done,
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done,
hintLocales = hintLocales,
platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))),
keyboardActions = KeyboardActions(onDone = { finish() }),
@@ -311,6 +311,7 @@ class EmojiSearchActivity : ComponentActivity() {
override fun getDescription(emoji: String): String? = if (Settings.getValues().mShowEmojiDescriptions)
dictionaryFacilitator?.getWordProperty(getEmojiNeutralVersion(emoji))?.mShortcutTargets[0]?.mWord else null
})
+ KeyboardSwitcher.getInstance().setAlphabetKeyboard()
Log.d("emoji-search", "init end")
}
From 8876038747dffa031da0528febcdefe30dede2f6 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Wed, 5 Nov 2025 20:35:46 +0200
Subject: [PATCH 33/56] Fix autocorrect
---
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 80b83859ec..d9bc4055ce 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -119,6 +119,7 @@ class EmojiSearchActivity : ComponentActivity() {
private lateinit var keyboardParams: KeyboardParams
private var keyWidth by Delegates.notNull()
private var keyHeight by Delegates.notNull()
+ private var firstKey: Key? = null
private var pressedKey: Key? = null
private var imeVisible = false
private var imeClosed = false
@@ -203,7 +204,10 @@ class EmojiSearchActivity : ComponentActivity() {
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done,
hintLocales = hintLocales,
platformImeOptions = PlatformImeOptions(encodePrivateImeOptions(PrivateImeOptions(heightPx)))),
- keyboardActions = KeyboardActions(onDone = { finish() }),
+ keyboardActions = KeyboardActions(onDone = {
+ if (Settings.getValues().mAutoCorrectEnabled) pressedKey = firstKey
+ finish()
+ }),
singleLine = true,
cursorBrush = SolidColor(textFieldColors.cursorColor)
) {
@@ -336,6 +340,7 @@ class EmojiSearchActivity : ComponentActivity() {
val keyboard = emojiPageKeyboardView.keyboard as DynamicGridKeyboard
keyboard.removeAllKeys()
+ firstKey = null
pressedKey = null
//todo: change to suggestion.isEmoji()
dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { isEmoji(it.word) }.forEach {
@@ -347,8 +352,7 @@ class EmojiSearchActivity : ComponentActivity() {
keyParams.mAbsoluteHeight = keyHeight
val key = keyParams.createKey()
keyboard.addKeyLast(key)
- if (pressedKey == null && Settings.getValues().mAutoCorrectEnabled)
- pressedKey = key
+ if (firstKey == null) firstKey = key
}
emojiPageKeyboardView.invalidate()
@@ -361,7 +365,6 @@ class EmojiSearchActivity : ComponentActivity() {
}
private fun cancel() {
- pressedKey = null
finish()
}
From 7bc00c8cdb82874372ee74c2b405022f973abd70 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 15 Nov 2025 00:01:17 +0200
Subject: [PATCH 34/56] Disable inline emoji search if in full emoji search
mode
---
.../java/helium314/keyboard/latin/inputlogic/InputLogic.java | 3 +--
1 file changed, 1 insertion(+), 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 268caeba9e..ee27be5012 100644
--- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
+++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
@@ -2708,8 +2708,7 @@ private static boolean isValidInlineEmojiSearchPreviousChar(int charBeforeBefore
}
public void updateEmojiDictionary(Locale locale) {
- //todo: disable if in full emoji search mode
- if (Settings.getValues().mInlineEmojiSearch && Settings.getValues().needsToLookupSuggestions()) {
+ if (Settings.getValues().mInlineEmojiSearch && Settings.getValues().needsToLookupSuggestions() && ! mLatinIME.isEmojiSearch()) {
if (mEmojiDictionaryFacilitator == null || ! mEmojiDictionaryFacilitator.isForLocale(locale)) {
closeEmojiDictionary();
var dictFile = DictionaryInfoUtils.getCachedDictForLocaleAndType(locale, "emoji", mLatinIME);
From 7ced3191f135b6f42429d44581791f809d6ece81 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Sat, 15 Nov 2025 19:51:35 +0200
Subject: [PATCH 35/56] Fix main merge
---
.../layouts/emoji_bottom/emoji_bottom_row_with_action.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json
index 4827b365d4..0f506a2bed 100644
--- a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json
+++ b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json
@@ -1,6 +1,7 @@
[
[
{ "label": "alpha", "width": 0.15 },
+ { "label": "search", "width": 0.15 },
{ "label": "space", "width": -1 },
{ "label": "delete", "width": 0.15 },
{ "label": "action", "width": 0.15 }
From 00461dfeb9e2651e41b4139d2f9c738f683451cf Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Mon, 17 Nov 2025 01:44:43 +0200
Subject: [PATCH 36/56] Cleanup
---
.../helium314/keyboard/keyboard/emoji/EmojiPalettesView.java | 1 -
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 3 +--
2 files changed, 1 insertion(+), 3 deletions(-)
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 e501e0dd4b..d77f2fe93a 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
@@ -354,7 +354,6 @@ void addRecentKey(final Key key) {
return;
}
getRecentsKeyboard().addKeyFirst(key);
-
mPager.getAdapter().notifyItemChanged(mEmojiCategory.getRecentTabId());
}
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index d9bc4055ce..d9ac20ece7 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -342,8 +342,7 @@ class EmojiSearchActivity : ComponentActivity() {
keyboard.removeAllKeys()
firstKey = null
pressedKey = null
- //todo: change to suggestion.isEmoji()
- dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { isEmoji(it.word) }.forEach {
+ dictionaryFacilitator!!.getSuggestions(text.splitOnWhitespace()).filter { it.isEmoji }.forEach {
val emoji = getEmojiDefaultVersion(it.word)
val popupSpec = getEmojiPopupSpec(emoji)
val keyParams = Key.KeyParams(emoji, emoji.getCode(), if (popupSpec != null) EMOJI_HINT_LABEL else null, popupSpec,
From d108ef9a8554758102807e190baa5a639953784b Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Wed, 19 Nov 2025 01:49:41 +0200
Subject: [PATCH 37/56] Close dictionary on dictionary changes
---
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 9 ++++++++-
app/src/main/java/helium314/keyboard/latin/LatinIME.java | 1 +
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index d9ac20ece7..9d371bc041 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -313,7 +313,9 @@ class EmojiSearchActivity : ComponentActivity() {
}
override fun getDescription(emoji: String): String? = if (Settings.getValues().mShowEmojiDescriptions)
- dictionaryFacilitator?.getWordProperty(getEmojiNeutralVersion(emoji))?.mShortcutTargets[0]?.mWord else null
+ dictionaryFacilitator?.getWordProperty(getEmojiNeutralVersion(emoji))?.let {
+ if (it.mHasShortcuts) it.mShortcutTargets[0]?.mWord else null
+ } else null
})
KeyboardSwitcher.getInstance().setAlphabetKeyboard()
Log.d("emoji-search", "init end")
@@ -382,6 +384,11 @@ class EmojiSearchActivity : ComponentActivity() {
editorInfo?.privateImeOptions?.takeIf { it.startsWith(PRIVATE_IME_OPTIONS_PREFIX) }
?.let { it.substring(PRIVATE_IME_OPTIONS_PREFIX.length + 1, it.indexOf(',')) }?.toInt() ?: 0)
+ fun closeDictionaryFacilitator() {
+ dictionaryFacilitator?.closeDictionaries()
+ dictionaryFacilitator = null
+ }
+
private fun encodePrivateImeOptions(privateImeOptions: PrivateImeOptions) =
"$PRIVATE_IME_OPTIONS_PREFIX.${privateImeOptions.height},"
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 986dc0a939..ff63451a1c 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -667,6 +667,7 @@ private void resetDictionaryFacilitator(@NonNull final Locale locale) {
mDictionaryFacilitator.resetDictionaries(this, mDictionaryFacilitator.getMainLocale(),
settingsValues.mUseContactsDictionary, settingsValues.mUseAppsDictionary,
settingsValues.mUsePersonalizedDicts, true, "", this);
+ EmojiSearchActivity.Companion.closeDictionaryFacilitator();
}
// used for debug
From c9cb50b2a367b83fd73cd35f4430753b94d022bd Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Tue, 25 Nov 2025 01:23:25 +0200
Subject: [PATCH 38/56] Fix for API < 30
---
app/src/main/AndroidManifest.xml | 2 +-
.../keyboard/emoji/EmojiSearchActivity.kt | 29 ++++++++++---------
2 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 322202c2e7..8dee1a1520 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -86,7 +86,7 @@ SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
+ android:windowSoftInputMode="adjustResize|stateAlwaysVisible" android:configChanges="orientation|screenSize"/>
Date: Wed, 26 Nov 2025 03:19:52 +0200
Subject: [PATCH 39/56] Fix landscape
---
.../helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 772dd5e31c..7fa487197f 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -21,11 +21,11 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.exclude
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentHeight
@@ -143,7 +143,7 @@ class EmojiSearchActivity : ComponentActivity() {
Surface(modifier = Modifier.fillMaxSize(), color = Color(0x80000000)) {
var heightDp by remember { mutableStateOf(0.dp) }
Column(modifier = Modifier.fillMaxSize().clickable(onClick = { cancel() })
- .windowInsetsPadding(WindowInsets.safeDrawing).offset(0.dp, heightDp),
+ .windowInsetsPadding(WindowInsets.safeDrawing.exclude(WindowInsets(bottom = heightDp))),
verticalArrangement = Arrangement.Bottom
) {
val localDensity = LocalDensity.current
From d8dcff00b7f7e301d216e79b6a7243fd2922aea4 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Wed, 26 Nov 2025 23:54:20 +0200
Subject: [PATCH 40/56] Revert `config_emoji_keyboard_max_recents_key_count`
-> `config_emoji_keyboard_max_recents_row_count` change, adding two factory
methods to `DynamicGridKeyboard`
---
.../keyboard/emoji/DynamicGridKeyboard.java | 14 ++++++++++++--
.../keyboard/keyboard/emoji/EmojiCategory.java | 12 ++++++------
.../keyboard/keyboard/emoji/EmojiSearchActivity.kt | 2 +-
app/src/main/res/values-land/config.xml | 2 +-
app/src/main/res/values-sw600dp-land/config.xml | 2 +-
app/src/main/res/values-sw600dp/config.xml | 2 +-
app/src/main/res/values-sw768dp-land/config.xml | 2 +-
app/src/main/res/values-sw768dp/config.xml | 2 +-
app/src/main/res/values/config.xml | 2 +-
9 files changed, 25 insertions(+), 15 deletions(-)
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 286174e497..7f4875a323 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java
@@ -50,8 +50,18 @@ final class DynamicGridKeyboard extends Keyboard {
private List mCachedGridKeys;
private final ArrayList mEmptyColumnIndices = new ArrayList<>(4);
- public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
+ public static DynamicGridKeyboard ofKeyCount(final SharedPreferences prefs, final Keyboard templateKeyboard,
+ final int maxKeyCount, final int categoryId, final int width) {
+ return new DynamicGridKeyboard(prefs, templateKeyboard, maxKeyCount, categoryId, width, false);
+ }
+
+ public static DynamicGridKeyboard ofRowCount(final SharedPreferences prefs, final Keyboard templateKeyboard,
final int maxRowCount, final int categoryId, final int width) {
+ return new DynamicGridKeyboard(prefs, templateKeyboard, maxRowCount, categoryId, width, true);
+ }
+
+ private DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
+ final int maxCount, final int categoryId, final int width, boolean rowCount) {
super(templateKeyboard);
// todo: would be better to keep them final and not require width, but how to properly set width of the template keyboard?
// an alternative would be to always create the templateKeyboard with full width
@@ -69,7 +79,7 @@ public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templat
mColumnsNum = mBaseWidth / mHorizontalStep;
if (spacerWidth > 0)
setSpacerColumns(spacerWidth);
- mMaxKeyCount = maxRowCount * getOccupiedColumnCount();
+ mMaxKeyCount = rowCount? maxCount * getOccupiedColumnCount() : maxCount;
mIsRecents = categoryId == EmojiCategory.ID_RECENTS;
mPrefs = prefs;
}
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java
index fd89e7b4d4..81327644b8 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java
@@ -119,7 +119,7 @@ public int getPageCount() {
private final SharedPreferences mPrefs;
private final Resources mRes;
private final Context mContext;
- private final int mMaxRecentsRowCount;
+ private final int mMaxRecentsKeyCount;
private final KeyboardLayoutSet mLayoutSet;
private final HashMap mCategoryNameToIdMap = new HashMap<>();
private final int[] mCategoryTabIconId = new int[sCategoryName.length];
@@ -133,7 +133,7 @@ public EmojiCategory(final Context ctx, final KeyboardLayoutSet layoutSet, final
mPrefs = KtxKt.prefs(ctx);
mRes = ctx.getResources();
mContext = ctx;
- mMaxRecentsRowCount = mRes.getInteger(R.integer.config_emoji_keyboard_max_recents_row_count);
+ mMaxRecentsKeyCount = mRes.getInteger(R.integer.config_emoji_keyboard_max_recents_key_count);
mLayoutSet = layoutSet;
for (int i = 0; i < sCategoryName.length; ++i) {
mCategoryNameToIdMap.put(sCategoryName[i], i);
@@ -292,9 +292,9 @@ public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
final int currentWidth = ResourceUtils.getKeyboardWidth(mContext, Settings.getValues());
if (categoryId == EmojiCategory.ID_RECENTS) {
- final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs,
+ final DynamicGridKeyboard kbd = DynamicGridKeyboard.ofKeyCount(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
- mMaxRecentsRowCount, categoryId, currentWidth);
+ mMaxRecentsKeyCount, categoryId, currentWidth);
mCategoryKeyboardMap.put(categoryKeyboardMapKey, kbd);
kbd.loadRecentKeys(mCategoryKeyboardMap.values());
return kbd;
@@ -305,7 +305,7 @@ public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
final Key[][] sortedKeysPages = sortKeysGrouped(
keyboard.getSortedKeys(), keyCountPerPage);
for (int pageId = 0; pageId < sortedKeysPages.length; ++pageId) {
- final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
+ final DynamicGridKeyboard tempKeyboard = DynamicGridKeyboard.ofRowCount(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
MAX_LINE_COUNT_PER_PAGE, categoryId, currentWidth);
for (final Key emojiKey : sortedKeysPages[pageId]) {
@@ -321,7 +321,7 @@ public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
}
private int computeMaxKeyCountPerPage() {
- final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
+ final DynamicGridKeyboard tempKeyboard = DynamicGridKeyboard.ofKeyCount(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
0, 0, ResourceUtils.getKeyboardWidth(mContext, Settings.getValues()));
return MAX_LINE_COUNT_PER_PAGE * tempKeyboard.getOccupiedColumnCount();
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
index 7fa487197f..ddb8046e02 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiSearchActivity.kt
@@ -287,7 +287,7 @@ class EmojiSearchActivity : ComponentActivity() {
// Initialize default versions and popup specs
layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_CATEGORY2)
- val keyboard = DynamicGridKeyboard(prefs(), layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
+ val keyboard = DynamicGridKeyboard.ofRowCount(prefs(), layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) 1 else 2,
KeyboardId.ELEMENT_EMOJI_CATEGORY16, keyboardWidth)
val builder = KeyboardBuilder(this, KeyboardParams())
diff --git a/app/src/main/res/values-land/config.xml b/app/src/main/res/values-land/config.xml
index 1ffb399990..e1d909847c 100644
--- a/app/src/main/res/values-land/config.xml
+++ b/app/src/main/res/values-land/config.xml
@@ -62,7 +62,7 @@
41%p
78%p
78%p
- 2
+ 32
3
diff --git a/app/src/main/res/values-sw600dp-land/config.xml b/app/src/main/res/values-sw600dp-land/config.xml
index bc5a17579f..448b73c0e1 100644
--- a/app/src/main/res/values-sw600dp-land/config.xml
+++ b/app/src/main/res/values-sw600dp-land/config.xml
@@ -52,7 +52,7 @@
40%p
64%p
64%p
- 3
+ 36
4
diff --git a/app/src/main/res/values-sw600dp/config.xml b/app/src/main/res/values-sw600dp/config.xml
index be55360d1f..3fac28c125 100644
--- a/app/src/main/res/values-sw600dp/config.xml
+++ b/app/src/main/res/values-sw600dp/config.xml
@@ -72,7 +72,7 @@
28%p
78%p
78%p
- 3
+ 36
3
diff --git a/app/src/main/res/values-sw768dp-land/config.xml b/app/src/main/res/values-sw768dp-land/config.xml
index c0fe32b58e..a315051013 100644
--- a/app/src/main/res/values-sw768dp-land/config.xml
+++ b/app/src/main/res/values-sw768dp-land/config.xml
@@ -50,5 +50,5 @@
33%p
58%p
58%p
- 3
+ 39
diff --git a/app/src/main/res/values-sw768dp/config.xml b/app/src/main/res/values-sw768dp/config.xml
index 642e8bf733..578de4d85b 100644
--- a/app/src/main/res/values-sw768dp/config.xml
+++ b/app/src/main/res/values-sw768dp/config.xml
@@ -67,5 +67,5 @@
30%p
64%p
64%p
- 3
+ 39
diff --git a/app/src/main/res/values/config.xml b/app/src/main/res/values/config.xml
index a2b97d8639..bc22125eb1 100644
--- a/app/src/main/res/values/config.xml
+++ b/app/src/main/res/values/config.xml
@@ -75,7 +75,7 @@
30%p
78%p
78%p
- 3
+ 32
2
From 93c46ad2d36d076dabfc8268128f2a63ebfbadfc Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Thu, 27 Nov 2025 00:43:43 +0200
Subject: [PATCH 41/56] Hide search button if no emoji dictionary
---
.../keyboard/keyboard/emoji/EmojiPalettesView.java | 2 ++
.../helium314/keyboard/latin/inputlogic/InputLogic.java | 9 ++-------
2 files changed, 4 insertions(+), 7 deletions(-)
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 83ba1d2384..f7d0b15f11 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
@@ -363,6 +363,8 @@ private void setupBottomRowKeyboard(final EditorInfo editorInfo, final KeyboardA
PointerTracker.switchTo(keyboardView);
final KeyboardLayoutSet kls = KeyboardLayoutSet.Builder.buildEmojiClipBottomRow(getContext(), editorInfo);
final Keyboard keyboard = kls.getKeyboard(KeyboardId.ELEMENT_EMOJI_BOTTOM_ROW);
+ var searchKey = keyboard.getKey(KeyCode.SEARCH);
+ if (searchKey != null) searchKey.setEnabled(! DictionaryInfoUtils.getLocalesWithEmojiDicts(getContext()).isEmpty());
keyboardView.setKeyboard(keyboard);
}
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 572bb5b29a..af358daaa1 100644
--- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
+++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
@@ -797,13 +797,8 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp
mLatinIME.onTextInput(TimestampKt.getTimestamp(mLatinIME));
break;
case KeyCode.SEARCH:
- if (DictionaryInfoUtils.getLocalesWithEmojiDicts(mLatinIME).isEmpty()) {
- // todo: open dictionary settings?
- onSettingsKeyPressed();
- } else {
- commitTyped(Settings.getValues(), LastComposedWord.NOT_A_SEPARATOR);
- mLatinIME.launchEmojiSearch();
- }
+ commitTyped(Settings.getValues(), LastComposedWord.NOT_A_SEPARATOR);
+ mLatinIME.launchEmojiSearch();
break;
case KeyCode.SEND_INTENT_ONE, KeyCode.SEND_INTENT_TWO, KeyCode.SEND_INTENT_THREE:
IntentUtils.handleSendIntentKey(mLatinIME, event.getKeyCode());
From ff13e47708918e9f09d91e0db0bb19febd45bc29 Mon Sep 17 00:00:00 2001
From: eranl <1707552+eranl@users.noreply.github.com>
Date: Mon, 1 Dec 2025 03:21:32 +0200
Subject: [PATCH 42/56] Use `keyboard_state_selector`
---
.../layouts/emoji_bottom/emoji_bottom_row.json | 2 +-
.../emoji_bottom/emoji_bottom_row_with_action.json | 2 +-
.../java/helium314/keyboard/keyboard/KeyboardId.java | 8 ++++++--
.../keyboard/keyboard/KeyboardLayoutSet.java | 3 +++
.../keyboard/keyboard/emoji/EmojiPalettesView.java | 2 --
.../internal/keyboard_parser/floris/KeyData.kt | 3 +++
.../main/java/helium314/keyboard/latin/LatinIME.java | 1 +
.../keyboard/settings/dialogs/DictionaryDialog.kt | 12 +++++++++---
8 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json
index 27d048e46c..190fb19e63 100644
--- a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json
+++ b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json
@@ -1,7 +1,7 @@
[
[
{ "label": "alpha", "width": 0.15 },
- { "label": "search", "width": 0.15 },
+ { "$": "keyboard_state_selector", "emojiSearchAvailable": { "label": "search", "width": 0.15 }},
{ "label": "space", "width": -1 },
{ "label": "delete", "width": 0.15 }
]
diff --git a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json
index 0f506a2bed..b199e1b3cf 100644
--- a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json
+++ b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json
@@ -1,7 +1,7 @@
[
[
{ "label": "alpha", "width": 0.15 },
- { "label": "search", "width": 0.15 },
+ { "$": "keyboard_state_selector", "emojiSearchAvailable": { "label": "search", "width": 0.15 }},
{ "label": "space", "width": -1 },
{ "label": "delete", "width": 0.15 },
{ "label": "action", "width": 0.15 }
diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java
index 232ad66d51..9455cc09f0 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardId.java
@@ -83,6 +83,7 @@ public final class KeyboardId {
public final boolean mIsSplitLayout;
public final boolean mOneHandedModeEnabled;
public final KeyboardLayoutSet.InternalAction mInternalAction;
+ public final boolean mEmojiSearchAvailable;
private final int mHashCode;
@@ -103,6 +104,7 @@ public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) {
mIsSplitLayout = params.mIsSplitLayoutEnabled;
mOneHandedModeEnabled = params.mOneHandedModeEnabled;
mInternalAction = params.mInternalAction;
+ mEmojiSearchAvailable = params.mEmojiSearchAvailable;
mHashCode = computeHashCode(this);
}
@@ -228,7 +230,7 @@ public int hashCode() {
@Override
public String toString() {
- return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s%s%s]",
+ return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s%s%s%s%s]",
elementIdToName(mElementId),
mSubtype.getLocale(),
mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
@@ -244,7 +246,9 @@ public String toString() {
(mLanguageSwitchKeyEnabled ? " languageSwitchKeyEnabled" : ""),
(mEmojiKeyEnabled ? " emojiKeyEnabled" : ""),
(isMultiLine() ? " isMultiLine" : ""),
- (mIsSplitLayout ? " isSplitLayout" : "")
+ (mIsSplitLayout ? " isSplitLayout" : ""),
+ (mInternalAction != null ? " internalAction=" + mInternalAction : ""),
+ (mEmojiSearchAvailable ? " emojiSearchAvailable" : "")
);
}
diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java
index 0a71574e59..6c0f5a5cf2 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java
@@ -21,6 +21,7 @@
import helium314.keyboard.latin.RichInputMethodManager;
import helium314.keyboard.latin.RichInputMethodSubtype;
import helium314.keyboard.latin.settings.Settings;
+import helium314.keyboard.latin.utils.DictionaryInfoUtils;
import helium314.keyboard.latin.utils.InputTypeUtils;
import helium314.keyboard.latin.utils.Log;
import helium314.keyboard.latin.utils.ResourceUtils;
@@ -99,6 +100,7 @@ public static final class Params {
// and the required ProductionFlags are enabled.
boolean mIsSplitLayoutEnabled;
InternalAction mInternalAction;
+ boolean mEmojiSearchAvailable;
}
public static void onSystemLocaleChanged() {
@@ -222,6 +224,7 @@ public Builder(final Context context, @Nullable final EditorInfo ei) {
public static KeyboardLayoutSet buildEmojiClipBottomRow(final Context context, @Nullable final EditorInfo ei) {
final Builder builder = new Builder(context, ei);
builder.mParams.mMode = KeyboardId.MODE_TEXT;
+ builder.mParams.mEmojiSearchAvailable = ! DictionaryInfoUtils.getLocalesWithEmojiDicts(context).isEmpty();
final int width = ResourceUtils.getKeyboardWidth(context, Settings.getValues());
// actually the keyboard does not have full height, but at this point we use it to get correct key heights
final int height = ResourceUtils.getKeyboardHeight(context.getResources(), Settings.getValues());
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 f7d0b15f11..83ba1d2384 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
@@ -363,8 +363,6 @@ private void setupBottomRowKeyboard(final EditorInfo editorInfo, final KeyboardA
PointerTracker.switchTo(keyboardView);
final KeyboardLayoutSet kls = KeyboardLayoutSet.Builder.buildEmojiClipBottomRow(getContext(), editorInfo);
final Keyboard keyboard = kls.getKeyboard(KeyboardId.ELEMENT_EMOJI_BOTTOM_ROW);
- var searchKey = keyboard.getKey(KeyCode.SEARCH);
- if (searchKey != null) searchKey.setEnabled(! DictionaryInfoUtils.getLocalesWithEmojiDicts(getContext()).isEmpty());
keyboardView.setKeyboard(keyboard);
}
diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyData.kt
index 7c8b0736d4..4e73ab6e0c 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyData.kt
+++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyData.kt
@@ -210,6 +210,7 @@ class KeyboardStateSelector(
val moreSymbols: AbstractKeyData? = null,
val alphabet: AbstractKeyData? = null,
val default: AbstractKeyData? = null,
+ val emojiSearchAvailable: AbstractKeyData? = null,
) : AbstractKeyData {
override fun compute(params: KeyboardParams): KeyData? {
if (params.mId.mEmojiKeyEnabled)
@@ -222,6 +223,8 @@ class KeyboardStateSelector(
moreSymbols?.compute(params)?.let { return it }
if (params.mId.isAlphabetKeyboard)
alphabet?.compute(params)?.let { return it }
+ if (params.mId.mEmojiSearchAvailable)
+ emojiSearchAvailable?.compute(params)?.let { return it }
return default?.compute(params)
}
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 8a70ec199b..11a73f8d7d 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -668,6 +668,7 @@ private void resetDictionaryFacilitator(@NonNull final Locale locale) {
mDictionaryFacilitator.resetDictionaries(this, mDictionaryFacilitator.getMainLocale(),
settingsValues.mUseContactsDictionary, settingsValues.mUseAppsDictionary,
settingsValues.mUsePersonalizedDicts, true, "", this);
+ mKeyboardSwitcher.setThemeNeedsReload();
EmojiPalettesView.closeDictionaryFacilitator();
EmojiSearchActivity.Companion.closeDictionaryFacilitator();
}
diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt
index b4ff2e55b6..b2c771df9b 100644
--- a/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt
+++ b/app/src/main/java/helium314/keyboard/settings/dialogs/DictionaryDialog.kt
@@ -44,6 +44,7 @@ import java.io.File
import java.util.Locale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalResources
+import helium314.keyboard.dictionarypack.DictionaryPackConstants
@Composable
fun DictionaryDialog(
@@ -132,13 +133,18 @@ private fun DictionaryDetails(dict: File) {
modifier = Modifier.padding(start = 10.dp, top = 0.dp, end = 10.dp, bottom = 12.dp)
)
}
- if (showDeleteDialog)
+ if (showDeleteDialog) {
+ val context = LocalContext.current
ConfirmationDialog(
onDismissRequest = { showDeleteDialog = false },
confirmButtonText = stringResource(R.string.remove),
- onConfirmed = { dict.delete() },
- content = { Text(stringResource(R.string.remove_dictionary_message, type))}
+ onConfirmed = {
+ dict.delete()
+ context.sendBroadcast(Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION))
+ },
+ content = { Text(stringResource(R.string.remove_dictionary_message, type)) }
)
+ }
}
@Preview
From c7e9d46984d49c78c4572c0e0839beef3d0e7700 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 20:31:33 +0100
Subject: [PATCH 43/56] Avoid potential NPE when inserting emoji from search
---
.../helium314/keyboard/keyboard/emoji/EmojiPalettesView.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
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 83ba1d2384..67d29c0733 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiPalettesView.java
@@ -354,7 +354,8 @@ void addRecentKey(final Key key) {
return;
}
getRecentsKeyboard().addKeyFirst(key);
- mPager.getAdapter().notifyItemChanged(mEmojiCategory.getRecentTabId());
+ if (initialized)
+ mPager.getAdapter().notifyItemChanged(mEmojiCategory.getRecentTabId());
}
private void setupBottomRowKeyboard(final EditorInfo editorInfo, final KeyboardActionListener keyboardActionListener) {
From 31e5afc783025a7b068355c9cd1f97ecdcc35df9 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 20:56:21 +0100
Subject: [PATCH 44/56] switch 3-line emoji keyboard to key count...
---
.../java/helium314/keyboard/keyboard/emoji/EmojiCategory.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java
index 81327644b8..06ed06b93b 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/EmojiCategory.java
@@ -305,9 +305,9 @@ public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
final Key[][] sortedKeysPages = sortKeysGrouped(
keyboard.getSortedKeys(), keyCountPerPage);
for (int pageId = 0; pageId < sortedKeysPages.length; ++pageId) {
- final DynamicGridKeyboard tempKeyboard = DynamicGridKeyboard.ofRowCount(mPrefs,
+ final DynamicGridKeyboard tempKeyboard = DynamicGridKeyboard.ofKeyCount(mPrefs,
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
- MAX_LINE_COUNT_PER_PAGE, categoryId, currentWidth);
+ keyCountPerPage, categoryId, currentWidth);
for (final Key emojiKey : sortedKeysPages[pageId]) {
if (emojiKey == null) {
break;
From 2acf559ee82ec49d2c4412900d2d711f1d3b62ff Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 20:57:56 +0100
Subject: [PATCH 45/56] ... and make OccupiedHeight dynamic if we use key count
prevents empty rows at the bottom of "normal" emoji view
---
.../keyboard/keyboard/emoji/DynamicGridKeyboard.java | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
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 7f4875a323..4ec57359f9 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/emoji/DynamicGridKeyboard.java
@@ -43,6 +43,7 @@ final class DynamicGridKeyboard extends Keyboard {
private final int mVerticalStep;
private final int mColumnsNum;
private final int mMaxKeyCount;
+ private final boolean mFixedRowCount;
private final boolean mIsRecents;
private final ArrayDeque mGridKeys = new ArrayDeque<>();
private final ArrayDeque mPendingKeys = new ArrayDeque<>();
@@ -61,7 +62,7 @@ public static DynamicGridKeyboard ofRowCount(final SharedPreferences prefs, fina
}
private DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
- final int maxCount, final int categoryId, final int width, boolean rowCount) {
+ final int maxCount, final int categoryId, final int width, boolean fixedRowCount) {
super(templateKeyboard);
// todo: would be better to keep them final and not require width, but how to properly set width of the template keyboard?
// an alternative would be to always create the templateKeyboard with full width
@@ -79,7 +80,8 @@ private DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templa
mColumnsNum = mBaseWidth / mHorizontalStep;
if (spacerWidth > 0)
setSpacerColumns(spacerWidth);
- mMaxKeyCount = rowCount? maxCount * getOccupiedColumnCount() : maxCount;
+ mMaxKeyCount = fixedRowCount? maxCount * getOccupiedColumnCount() : maxCount;
+ mFixedRowCount = fixedRowCount;
mIsRecents = categoryId == EmojiCategory.ID_RECENTS;
mPrefs = prefs;
}
@@ -121,8 +123,10 @@ private Key getTemplateKey(final int code) {
throw new RuntimeException("Can't find template key: code=" + code);
}
+ // height is dynamic if we don't have a fixed row count
int getOccupiedHeight() {
- final int row = (mMaxKeyCount - 1) / getOccupiedColumnCount() + 1;
+ final int count = mFixedRowCount ? mMaxKeyCount : mGridKeys.size();
+ final int row = (count - 1) / getOccupiedColumnCount() + 1;
return row * mVerticalStep;
}
From 413bf801489a8918afc892842d35406a7943c012 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:24:00 +0100
Subject: [PATCH 46/56] add comment
---
app/src/main/java/helium314/keyboard/latin/LatinIME.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
index 262fe7fe2f..051371b759 100644
--- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java
+++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java
@@ -671,7 +671,7 @@ private void resetDictionaryFacilitator(@NonNull final Locale locale) {
mDictionaryFacilitator.resetDictionaries(this, mDictionaryFacilitator.getMainLocale(),
settingsValues.mUseContactsDictionary, settingsValues.mUseAppsDictionary,
settingsValues.mUsePersonalizedDicts, true, "", this);
- mKeyboardSwitcher.setThemeNeedsReload();
+ mKeyboardSwitcher.setThemeNeedsReload(); // necessary for emoji search
EmojiPalettesView.closeDictionaryFacilitator();
EmojiSearchActivity.Companion.closeDictionaryFacilitator();
}
From 54038dcf5db7a50367a1f529d7828fcee11d5209 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:34:05 +0100
Subject: [PATCH 47/56] rename search key
---
.../keyboard/internal/keyboard_parser/floris/KeyCode.kt | 4 ++--
1 file changed, 2 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 de93965052..a47ac62c3b 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
@@ -176,7 +176,7 @@ object KeyCode {
const val ALT_RIGHT = -10047
const val META_LEFT = -10048
const val META_RIGHT = -10049
- const val SEARCH = -10050
+ const val EMOJI_SEARCH = -10050
const val INLINE_EMOJI_SEARCH_DONE = -10051
@@ -201,7 +201,7 @@ object KeyCode {
PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT,
MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK,
TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT, SEND_INTENT_ONE, SEND_INTENT_TWO,
- SEND_INTENT_THREE, SEARCH, INLINE_EMOJI_SEARCH_DONE, META_LOCK
+ SEND_INTENT_THREE, EMOJI_SEARCH, INLINE_EMOJI_SEARCH_DONE, META_LOCK
-> this
// conversion
From 418d1d78031f78c1f22fc7f9fc796ab0a512f8d7 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:36:32 +0100
Subject: [PATCH 48/56] rename key
---
.../main/java/helium314/keyboard/latin/common/Constants.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 e84d740a95..cb4eb408b7 100644
--- a/app/src/main/java/helium314/keyboard/latin/common/Constants.java
+++ b/app/src/main/java/helium314/keyboard/latin/common/Constants.java
@@ -233,7 +233,7 @@ public static String printableCode(final int code) {
case KeyCode.SWITCH_ONE_HANDED_MODE: return "switchOneHandedMode";
case KeyCode.SPLIT_LAYOUT: return "splitLayout";
case KeyCode.NUMPAD: return "numpad";
- case KeyCode.SEARCH: return "search";
+ case KeyCode.EMOJI_SEARCH: return "emojiSearch";
default:
if (code < CODE_SPACE) return String.format("\\u%02X", code);
if (code < 0x100) return String.format("%c", code);
From 3e962511ee3ca7345fe9a378c1ee8b710a3758eb Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:37:20 +0100
Subject: [PATCH 49/56] rename key
---
.../keyboard/keyboard/internal/KeyboardCodesSet.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 8b8d0a993c..9ce3265a0b 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardCodesSet.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardCodesSet.java
@@ -56,7 +56,7 @@ public static int getCode(final String name) {
"key_start_onehanded", // keep name to avoid breaking custom layouts
"key_stop_onehanded", // keep name to avoid breaking custom layouts
"key_switch_onehanded",
- "key_search"
+ "key_emoji_search"
};
private static final int[] DEFAULT = {
@@ -83,7 +83,7 @@ public static int getCode(final String name) {
KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.SWITCH_ONE_HANDED_MODE,
- KeyCode.SEARCH
+ KeyCode.EMOJI_SEARCH
};
static {
From f832bb48706420d8dc04ed6f5f072f4e2613ddf3 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:37:59 +0100
Subject: [PATCH 50/56] rename key
---
app/src/main/java/helium314/keyboard/keyboard/Key.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/helium314/keyboard/keyboard/Key.java b/app/src/main/java/helium314/keyboard/keyboard/Key.java
index 1f6733e5ce..2585d50418 100644
--- a/app/src/main/java/helium314/keyboard/keyboard/Key.java
+++ b/app/src/main/java/helium314/keyboard/keyboard/Key.java
@@ -1168,7 +1168,7 @@ public KeyParams(
case KeyCode.SHIFT, Constants.CODE_ENTER, KeyCode.SHIFT_ENTER, KeyCode.ALPHA, Constants.CODE_SPACE, KeyCode.NUMPAD,
KeyCode.SYMBOL, KeyCode.SYMBOL_ALPHA, KeyCode.LANGUAGE_SWITCH, KeyCode.EMOJI, KeyCode.CLIPBOARD,
KeyCode.MOVE_START_OF_LINE, KeyCode.MOVE_END_OF_LINE, KeyCode.MOVE_START_OF_PAGE, KeyCode.MOVE_END_OF_PAGE,
- KeyCode.SEARCH:
+ KeyCode.EMOJI_SEARCH:
actionFlags |= ACTION_FLAGS_NO_KEY_PREVIEW; // no preview even if icon!
}
if (mCode == KeyCode.SETTINGS || mCode == KeyCode.LANGUAGE_SWITCH)
From 96af52510be5eb093fbd3a758867f6b2fa9b74c9 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:40:57 +0100
Subject: [PATCH 51/56] rename key
---
.../keyboard/internal/keyboard_parser/floris/KeyLabel.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 03b821f80d..d31d9cc092 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
@@ -41,7 +41,7 @@ object KeyLabel {
const val TAB = "tab"
const val ESCAPE = "esc"
const val TIMESTAMP = "timestamp"
- const val SEARCH = "search"
+ const val EMOJI_SEARCH = "emoji_search"
/** to make sure a FlorisBoard label works when reading a JSON layout */
// resulting special labels should be names of FunctionalKey enum, case insensitive
@@ -111,7 +111,7 @@ object KeyLabel {
CTRL, ALT, FN, META, ESCAPE -> label.uppercase(Locale.US)
TAB -> "!icon/tab_key|!code/${KeyCode.TAB}"
TIMESTAMP -> "⌚"
- SEARCH -> "!icon/search_key|!code/key_search"
+ EMOJI_SEARCH -> "!icon/search_key|!code/key_emoji_search"
else -> if (label in toolbarKeyStrings.values)
"!icon/$label|!code/${getCodeForToolbarKey(ToolbarKey.valueOf(label.uppercase(Locale.US)))}"
else label
From f0ec086ed6ba99bcff422c3bed2ed81a1ad92661 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:41:20 +0100
Subject: [PATCH 52/56] rename key
---
app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json
index 190fb19e63..cde4e61b4c 100644
--- a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json
+++ b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row.json
@@ -1,7 +1,7 @@
[
[
{ "label": "alpha", "width": 0.15 },
- { "$": "keyboard_state_selector", "emojiSearchAvailable": { "label": "search", "width": 0.15 }},
+ { "$": "keyboard_state_selector", "emojiSearchAvailable": { "label": "emoji_search", "width": 0.15 }},
{ "label": "space", "width": -1 },
{ "label": "delete", "width": 0.15 }
]
From c0a5fc70e89f81e5695b46bd50be4bafc5669dc1 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:41:41 +0100
Subject: [PATCH 53/56] rename key
---
.../layouts/emoji_bottom/emoji_bottom_row_with_action.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json
index b199e1b3cf..831a5b84cd 100644
--- a/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json
+++ b/app/src/main/assets/layouts/emoji_bottom/emoji_bottom_row_with_action.json
@@ -1,7 +1,7 @@
[
[
{ "label": "alpha", "width": 0.15 },
- { "$": "keyboard_state_selector", "emojiSearchAvailable": { "label": "search", "width": 0.15 }},
+ { "$": "keyboard_state_selector", "emojiSearchAvailable": { "label": "emoji_search", "width": 0.15 }},
{ "label": "space", "width": -1 },
{ "label": "delete", "width": 0.15 },
{ "label": "action", "width": 0.15 }
From 2fa6e3d6ab632382c2b41e46ccafae300a154f72 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:42:50 +0100
Subject: [PATCH 54/56] rename key
---
.../keyboard/accessibility/KeyCodeDescriptionMapper.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt b/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt
index 50060b0db4..64311e9313 100644
--- a/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt
+++ b/app/src/main/java/helium314/keyboard/accessibility/KeyCodeDescriptionMapper.kt
@@ -35,7 +35,7 @@ internal class KeyCodeDescriptionMapper private constructor() {
put(KeyCode.ACTION_NEXT, R.string.spoken_description_action_next)
put(KeyCode.ACTION_PREVIOUS, R.string.spoken_description_action_previous)
put(KeyCode.EMOJI, R.string.spoken_description_emoji)
- put(KeyCode.SEARCH, R.string.spoken_description_search)
+ put(KeyCode.EMOJI_SEARCH, R.string.spoken_description_search)
// Because the upper-case and lower-case mappings of the following letters is depending on
// the locale, the upper case descriptions should be defined here. The lower case
// descriptions are handled in {@link #getSpokenLetterDescriptionId(Context,int)}.
From 180e18d81b9f901b64c46ee18fe8ed3d59d15160 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:46:39 +0100
Subject: [PATCH 55/56] rename key
---
.../java/helium314/keyboard/latin/inputlogic/InputLogic.java | 2 +-
1 file changed, 1 insertion(+), 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 09ad1e41e3..d155cbbe51 100644
--- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
+++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java
@@ -804,7 +804,7 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp
case KeyCode.TIMESTAMP:
mLatinIME.onTextInput(TimestampKt.getTimestamp(mLatinIME));
break;
- case KeyCode.SEARCH:
+ case KeyCode.EMOJI_SEARCH:
commitTyped(Settings.getValues(), LastComposedWord.NOT_A_SEPARATOR);
mLatinIME.launchEmojiSearch();
break;
From ab67778eb18b2be446b07b7922a39634ef9a2c01 Mon Sep 17 00:00:00 2001
From: Helium314
Date: Sat, 21 Feb 2026 21:49:42 +0100
Subject: [PATCH 56/56] rename key
---
.../keyboard/internal/keyboard_parser/floris/TextKeyData.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 5c5360fda6..2eab73d699 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
@@ -405,7 +405,7 @@ sealed interface KeyData : AbstractKeyData {
when (label) { // or use code?
KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL, KeyLabel.ALPHA, KeyLabel.COMMA, KeyLabel.PERIOD, KeyLabel.DELETE,
KeyLabel.COM, KeyLabel.LANGUAGE_SWITCH, KeyLabel.NUMPAD, KeyLabel.CTRL, KeyLabel.ALT,
- KeyLabel.FN, KeyLabel.META, KeyLabel.SEARCH, toolbarKeyStrings[ToolbarKey.EMOJI] -> return Key.BACKGROUND_TYPE_FUNCTIONAL
+ KeyLabel.FN, KeyLabel.META, KeyLabel.EMOJI_SEARCH, toolbarKeyStrings[ToolbarKey.EMOJI] -> return Key.BACKGROUND_TYPE_FUNCTIONAL
KeyLabel.SPACE, KeyLabel.ZWNJ -> return Key.BACKGROUND_TYPE_SPACEBAR
KeyLabel.ACTION -> return Key.BACKGROUND_TYPE_ACTION
KeyLabel.SHIFT -> return Key.BACKGROUND_TYPE_FUNCTIONAL