diff --git a/java/res/drawable/arrow_down.xml b/java/res/drawable/arrow_down.xml
new file mode 100644
index 000000000..9c8ef5e6c
--- /dev/null
+++ b/java/res/drawable/arrow_down.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/arrow_left.xml b/java/res/drawable/arrow_left.xml
index f53ed4d37..51868088b 100644
--- a/java/res/drawable/arrow_left.xml
+++ b/java/res/drawable/arrow_left.xml
@@ -1,20 +1,20 @@
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
diff --git a/java/res/drawable/arrow_left_26.xml b/java/res/drawable/arrow_left_26.xml
new file mode 100644
index 000000000..f53ed4d37
--- /dev/null
+++ b/java/res/drawable/arrow_left_26.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/arrow_right.xml b/java/res/drawable/arrow_right.xml
new file mode 100644
index 000000000..5109cd87b
--- /dev/null
+++ b/java/res/drawable/arrow_right.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/arrow_up.xml b/java/res/drawable/arrow_up.xml
new file mode 100644
index 000000000..8d338651a
--- /dev/null
+++ b/java/res/drawable/arrow_up.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/chevron_down.xml b/java/res/drawable/chevron_down.xml
new file mode 100644
index 000000000..746f6c06d
--- /dev/null
+++ b/java/res/drawable/chevron_down.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/java/res/drawable/clipboard.xml b/java/res/drawable/clipboard.xml
new file mode 100644
index 000000000..e4cf2e78d
--- /dev/null
+++ b/java/res/drawable/clipboard.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/close.xml b/java/res/drawable/close.xml
new file mode 100644
index 000000000..991e1ca0d
--- /dev/null
+++ b/java/res/drawable/close.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/copy.xml b/java/res/drawable/copy.xml
new file mode 100644
index 000000000..be18c41bb
--- /dev/null
+++ b/java/res/drawable/copy.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/ctrl.xml b/java/res/drawable/ctrl.xml
new file mode 100644
index 000000000..9c0a06d18
--- /dev/null
+++ b/java/res/drawable/ctrl.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/java/res/drawable/cut.xml b/java/res/drawable/cut.xml
new file mode 100644
index 000000000..3a734cc16
--- /dev/null
+++ b/java/res/drawable/cut.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
diff --git a/java/res/drawable/edit_text.xml b/java/res/drawable/edit_text.xml
new file mode 100644
index 000000000..f2d53814c
--- /dev/null
+++ b/java/res/drawable/edit_text.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/redo.xml b/java/res/drawable/redo.xml
new file mode 100644
index 000000000..290274cdb
--- /dev/null
+++ b/java/res/drawable/redo.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/drawable/undo.xml b/java/res/drawable/undo.xml
new file mode 100644
index 000000000..8ad21710b
--- /dev/null
+++ b/java/res/drawable/undo.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 38bb14f92..68c620e88 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -421,6 +421,7 @@
+
diff --git a/java/res/values/strings-uix.xml b/java/res/values/strings-uix.xml
index 579c94cef..ae8682877 100644
--- a/java/res/values/strings-uix.xml
+++ b/java/res/values/strings-uix.xml
@@ -2,6 +2,11 @@
Voice Input
Theme Switcher
+ Emojis
+ Paste from Clipboard
+ Undo
+ Redo
+ Text Editor
AMOLED Dark Purple
AOSP Material Dark
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index d3a7f826c..e7b51e048 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -573,5 +573,4 @@ Tip: You can download and remove dictionaries by going to <b>Languages &
This resource is copied from packages/apps/Settings/res/values/strings.xml -->
\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ
- Emojis
diff --git a/java/res/values/themes-lxx-dark.xml b/java/res/values/themes-lxx-dark.xml
index 2b53e747f..126d24aea 100644
--- a/java/res/values/themes-lxx-dark.xml
+++ b/java/res/values/themes-lxx-dark.xml
@@ -48,6 +48,7 @@
- @drawable/btn_keyboard_spacebar_lxx_dark
- @color/key_text_color_lxx_dark
- @color/key_functional_text_color_lxx_dark
+ - @color/key_text_color_lxx_dark
- @color/key_text_color_lxx_dark
- @color/key_hint_letter_color_lxx_dark
- @color/key_text_inactive_color_lxx_dark
diff --git a/java/res/values/themes-lxx-light.xml b/java/res/values/themes-lxx-light.xml
index aac14fa41..f11e45ff7 100644
--- a/java/res/values/themes-lxx-light.xml
+++ b/java/res/values/themes-lxx-light.xml
@@ -48,6 +48,7 @@
- @drawable/btn_keyboard_spacebar_lxx_light
- @color/key_text_color_lxx_light
- @color/key_text_inactive_color_lxx_light
+ - @color/key_text_color_lxx_light
- @color/key_functional_text_color_lxx_light
- @color/key_hint_letter_color_lxx_light
- @color/key_text_inactive_color_lxx_light
diff --git a/java/res/xml/rowkeys_qwerty2_left5.xml b/java/res/xml/rowkeys_qwerty2_left5.xml
index 540b29b49..cfd1a500b 100644
--- a/java/res/xml/rowkeys_qwerty2_left5.xml
+++ b/java/res/xml/rowkeys_qwerty2_left5.xml
@@ -23,16 +23,26 @@
>
+ latin:keySpec="f"
+ latin:keyHintLabel="%"
+ latin:additionalMoreKeys="%" />
diff --git a/java/res/xml/rowkeys_qwerty2_right4.xml b/java/res/xml/rowkeys_qwerty2_right4.xml
index 685c5b81d..555b9e344 100644
--- a/java/res/xml/rowkeys_qwerty2_right4.xml
+++ b/java/res/xml/rowkeys_qwerty2_right4.xml
@@ -23,14 +23,22 @@
>
diff --git a/java/res/xml/rowkeys_qwerty3_left4.xml b/java/res/xml/rowkeys_qwerty3_left4.xml
index 09de5a7e5..d263eeb2a 100644
--- a/java/res/xml/rowkeys_qwerty3_left4.xml
+++ b/java/res/xml/rowkeys_qwerty3_left4.xml
@@ -23,14 +23,22 @@
>
diff --git a/java/res/xml/rowkeys_qwerty3_right3.xml b/java/res/xml/rowkeys_qwerty3_right3.xml
index ccacb2cf2..dcf1f4ad4 100644
--- a/java/res/xml/rowkeys_qwerty3_right3.xml
+++ b/java/res/xml/rowkeys_qwerty3_right3.xml
@@ -22,10 +22,16 @@
xmlns:latin="http://schemas.android.com/apk/res/org.futo.inputmethod.latin"
>
+ latin:keySpec="b"
+ latin:keyHintLabel=";"
+ latin:additionalMoreKeys=";" />
+ latin:keySpec="m"
+ latin:keyHintLabel="?"
+ latin:additionalMoreKeys="?,/" />
diff --git a/java/src/org/futo/inputmethod/keyboard/Key.java b/java/src/org/futo/inputmethod/keyboard/Key.java
index 683e60245..9d19098b3 100644
--- a/java/src/org/futo/inputmethod/keyboard/Key.java
+++ b/java/src/org/futo/inputmethod/keyboard/Key.java
@@ -665,6 +665,10 @@ public class Key implements Comparable {
if ((mLabelFlags & LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR) != 0) {
return params.mFunctionalTextColor;
}
+ if (mPressed) {
+ return params.mPressedTextColor;
+ }
+
return isShiftedLetterActivated() ? params.mTextInactivatedColor : params.mTextColor;
}
diff --git a/java/src/org/futo/inputmethod/keyboard/KeyboardActionListener.java b/java/src/org/futo/inputmethod/keyboard/KeyboardActionListener.java
index a8e75de45..1bb2ed695 100644
--- a/java/src/org/futo/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/org/futo/inputmethod/keyboard/KeyboardActionListener.java
@@ -101,6 +101,11 @@ public interface KeyboardActionListener {
*/
public boolean onCustomRequest(int requestCode);
+ public void onMovePointer(int steps);
+ public void onMoveDeletePointer(int steps);
+ public void onUpWithDeletePointerActive();
+ public void onUpWithPointerActive();
+
public static final KeyboardActionListener EMPTY_LISTENER = new Adapter();
public static class Adapter implements KeyboardActionListener {
@@ -125,8 +130,14 @@ public interface KeyboardActionListener {
@Override
public void onFinishSlidingInput() {}
@Override
- public boolean onCustomRequest(int requestCode) {
- return false;
- }
+ public boolean onCustomRequest(int requestCode) { return false; }
+ @Override
+ public void onMovePointer(int steps) {}
+ @Override
+ public void onMoveDeletePointer(int steps) {}
+ @Override
+ public void onUpWithDeletePointerActive() {}
+ @Override
+ public void onUpWithPointerActive() {}
}
}
diff --git a/java/src/org/futo/inputmethod/keyboard/KeyboardView.java b/java/src/org/futo/inputmethod/keyboard/KeyboardView.java
index e95878ec5..8418830e6 100644
--- a/java/src/org/futo/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/org/futo/inputmethod/keyboard/KeyboardView.java
@@ -176,6 +176,11 @@ public class KeyboardView extends View {
R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView);
mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0);
mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr, mDrawableProvider);
+
+ if(isMoreKeys && mKeyVisualAttributes != null) {
+ mKeyVisualAttributes.mTextColor = mDrawableProvider.getMoreKeysTextColor();
+ }
+
keyAttr.recycle();
mPaint.setAntiAlias(true);
diff --git a/java/src/org/futo/inputmethod/keyboard/PointerTracker.java b/java/src/org/futo/inputmethod/keyboard/PointerTracker.java
index d19d91ad0..4e59e44ba 100644
--- a/java/src/org/futo/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/org/futo/inputmethod/keyboard/PointerTracker.java
@@ -85,6 +85,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
// Parameters for pointer handling.
private static PointerTrackerParams sParams;
+ private static final int sPointerStep = (int)(16.0 * Resources.getSystem().getDisplayMetrics().density);
+
private static GestureStrokeRecognitionParams sGestureStrokeRecognitionParams;
private static GestureStrokeDrawingParams sGestureStrokeDrawingParams;
private static boolean sNeedsPhantomSuddenMoveEventHack;
@@ -128,6 +130,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
private int mLastX;
private int mLastY;
+ private int mStartX;
+ private int mStartY;
+ private long mStartTime;
+ private boolean mCursorMoved = false;
+
// true if keyboard layout has been changed.
private boolean mKeyboardLayoutHasBeenChanged;
@@ -691,6 +698,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
startRepeatKey(key);
startLongPressTimer(key);
setPressedKeyGraphics(key, eventTime);
+
+ mStartX = x;
+ mStartY = y;
+ mStartTime = System.currentTimeMillis();
}
}
@@ -892,6 +903,29 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
final int lastX = mLastX;
final int lastY = mLastY;
final Key oldKey = mCurrentKey;
+
+ if (oldKey != null && oldKey.getCode() == Constants.CODE_SPACE) {
+ int steps = (x - mStartX) / sPointerStep;
+ final int swipeIgnoreTime = Settings.getInstance().getCurrent().mKeyLongpressTimeout / MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
+ if (steps != 0 && mStartTime + swipeIgnoreTime < System.currentTimeMillis()) {
+ mCursorMoved = true;
+ mStartX += steps * sPointerStep;
+ sListener.onMovePointer(steps);
+ }
+ return;
+ }
+
+ if (oldKey != null && oldKey.getCode() == Constants.CODE_DELETE) {
+ int steps = (x - mStartX) / sPointerStep;
+ if (steps != 0) {
+ sTimerProxy.cancelKeyTimersOf(this);
+ mCursorMoved = true;
+ mStartX += steps * sPointerStep;
+ sListener.onMoveDeletePointer(steps);
+ }
+ return;
+ }
+
final Key newKey = onMoveKey(x, y);
if (sGestureEnabler.shouldHandleGesture()) {
@@ -966,6 +1000,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
// Release the last pressed key.
setReleasedKeyGraphics(currentKey, true /* withAnimation */);
+ if(mCursorMoved && currentKey.getCode() == Constants.CODE_DELETE) {
+ sListener.onUpWithDeletePointerActive();
+ }
+
+ if(mCursorMoved) {
+ sListener.onUpWithPointerActive();
+ }
+
if (isShowingMoreKeysPanel()) {
if (!mIsTrackingForActionDisabled) {
final int translatedX = mMoreKeysPanel.translateX(x);
@@ -988,6 +1030,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
return;
}
+ if (mCursorMoved) {
+ mCursorMoved = false;
+ return;
+ }
if (mIsTrackingForActionDisabled) {
return;
}
@@ -1018,6 +1064,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
if (isShowingMoreKeysPanel()) {
return;
}
+ if (mCursorMoved) {
+ return;
+ }
final Key key = getKey();
if (key == null) {
return;
diff --git a/java/src/org/futo/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/org/futo/inputmethod/keyboard/internal/KeyDrawParams.java
index b3a913baa..8447aaae4 100644
--- a/java/src/org/futo/inputmethod/keyboard/internal/KeyDrawParams.java
+++ b/java/src/org/futo/inputmethod/keyboard/internal/KeyDrawParams.java
@@ -37,6 +37,7 @@ public final class KeyDrawParams {
public int mTextColor;
public int mTextInactivatedColor;
+ public int mPressedTextColor;
public int mTextShadowColor;
public int mFunctionalTextColor;
public int mHintLetterColor;
@@ -66,6 +67,7 @@ public final class KeyDrawParams {
mTextColor = copyFrom.mTextColor;
mTextInactivatedColor = copyFrom.mTextInactivatedColor;
+ mPressedTextColor = copyFrom.mPressedTextColor;
mTextShadowColor = copyFrom.mTextShadowColor;
mFunctionalTextColor = copyFrom.mFunctionalTextColor;
mHintLetterColor = copyFrom.mHintLetterColor;
@@ -103,6 +105,7 @@ public final class KeyDrawParams {
mTextColor = selectColor(attr.mTextColor, mTextColor);
mTextInactivatedColor = selectColor(attr.mTextInactivatedColor, mTextInactivatedColor);
+ mPressedTextColor = selectColor(attr.mPressedTextColor, mPressedTextColor);
mTextShadowColor = selectColor(attr.mTextShadowColor, mTextShadowColor);
mFunctionalTextColor = selectColor(attr.mFunctionalTextColor, mFunctionalTextColor);
mHintLetterColor = selectColor(attr.mHintLetterColor, mHintLetterColor);
diff --git a/java/src/org/futo/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/org/futo/inputmethod/keyboard/internal/KeyVisualAttributes.java
index dd5e8704c..5aaf75aad 100644
--- a/java/src/org/futo/inputmethod/keyboard/internal/KeyVisualAttributes.java
+++ b/java/src/org/futo/inputmethod/keyboard/internal/KeyVisualAttributes.java
@@ -41,8 +41,9 @@ public final class KeyVisualAttributes {
public final float mHintLabelRatio;
public final float mPreviewTextRatio;
- public final int mTextColor;
+ public int mTextColor;
public final int mTextInactivatedColor;
+ public final int mPressedTextColor;
public final int mTextShadowColor;
public final int mFunctionalTextColor;
public final int mHintLetterColor;
@@ -130,6 +131,8 @@ public final class KeyVisualAttributes {
R.styleable.Keyboard_Key_keyTextColor, 0, keyAttr, provider);
mTextInactivatedColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyTextInactivatedColor, 0, keyAttr, provider);
+ mPressedTextColor = DynamicThemeProvider.Companion.getColorOrDefault(
+ R.styleable.Keyboard_Key_keyPressedTextColor, 0, keyAttr, provider);
mTextShadowColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyTextShadowColor, 0, keyAttr, provider);
mFunctionalTextColor = DynamicThemeProvider.Companion.getColorOrDefault(
diff --git a/java/src/org/futo/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/org/futo/inputmethod/keyboard/internal/KeyboardTextsTable.java
index 96f606605..3a8018850 100644
--- a/java/src/org/futo/inputmethod/keyboard/internal/KeyboardTextsTable.java
+++ b/java/src/org/futo/inputmethod/keyboard/internal/KeyboardTextsTable.java
@@ -302,7 +302,7 @@ public final class KeyboardTextsTable {
/* ~ additional_morekeys_symbols_0 */
/* morekeys_tablet_period */ "!text/morekeys_tablet_punctuation",
/* morekeys_nordic_row2_11 */ EMPTY,
- /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,?,!,#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,',@,:,-,\",+,\\%,&",
+ /* morekeys_punctuation */ "_,\\\\,|,=",
/* keyspec_tablet_comma */ ",",
// Period key
/* keyspec_period */ ".",
diff --git a/java/src/org/futo/inputmethod/latin/LatinIME.kt b/java/src/org/futo/inputmethod/latin/LatinIME.kt
index fa9d29185..d4ce31926 100644
--- a/java/src/org/futo/inputmethod/latin/LatinIME.kt
+++ b/java/src/org/futo/inputmethod/latin/LatinIME.kt
@@ -1,6 +1,5 @@
package org.futo.inputmethod.latin
-import android.content.Context
import android.content.res.Configuration
import android.inputmethodservice.InputMethodService
import android.os.Build
@@ -13,34 +12,17 @@ import android.view.inputmethod.InlineSuggestionsRequest
import android.view.inputmethod.InlineSuggestionsResponse
import android.view.inputmethod.InputMethodSubtype
import androidx.annotation.RequiresApi
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ColorScheme
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.key
-import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewModelStore
@@ -56,34 +38,24 @@ import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
-import org.futo.inputmethod.latin.common.Constants
-import org.futo.inputmethod.latin.uix.Action
-import org.futo.inputmethod.latin.uix.ActionBar
-import org.futo.inputmethod.latin.uix.ActionInputTransaction
-import org.futo.inputmethod.latin.uix.ActionWindow
import org.futo.inputmethod.latin.uix.BasicThemeProvider
import org.futo.inputmethod.latin.uix.DynamicThemeProvider
import org.futo.inputmethod.latin.uix.DynamicThemeProviderOwner
-import org.futo.inputmethod.latin.uix.KeyboardManagerForAction
-import org.futo.inputmethod.latin.uix.PersistentActionState
import org.futo.inputmethod.latin.uix.THEME_KEY
+import org.futo.inputmethod.latin.uix.UixManager
import org.futo.inputmethod.latin.uix.createInlineSuggestionsRequest
import org.futo.inputmethod.latin.uix.deferGetSetting
import org.futo.inputmethod.latin.uix.deferSetSetting
import org.futo.inputmethod.latin.uix.differsFrom
-import org.futo.inputmethod.latin.uix.inflateInlineSuggestion
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
import org.futo.inputmethod.latin.uix.theme.ThemeOption
import org.futo.inputmethod.latin.uix.theme.ThemeOptions
-import org.futo.inputmethod.latin.uix.theme.Typography
-import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme
import org.futo.inputmethod.latin.xlm.LanguageModelFacilitator
class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner,
- LatinIMELegacy.SuggestionStripController, DynamicThemeProviderOwner, KeyboardManagerForAction {
+ LatinIMELegacy.SuggestionStripController, DynamicThemeProviderOwner {
private val mSavedStateRegistryController = SavedStateRegistryController.create(this)
@@ -101,7 +73,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
override val viewModelStore
get() = store
- private fun setOwners() {
+ fun setOwners() {
val decorView = window.window?.decorView
if (decorView?.findViewTreeLifecycleOwner() == null) {
decorView?.setViewTreeLifecycleOwner(this)
@@ -114,14 +86,14 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
}
- private var composeView: ComposeView? = null
-
- private val latinIMELegacy = LatinIMELegacy(
+ val latinIMELegacy = LatinIMELegacy(
this as InputMethodService,
this as LatinIMELegacy.SuggestionStripController
)
- public val languageModelFacilitator = LanguageModelFacilitator(
+ val inputLogic get() = latinIMELegacy.mInputLogic
+
+ val languageModelFacilitator = LanguageModelFacilitator(
this,
latinIMELegacy.mInputLogic,
latinIMELegacy.mDictionaryFacilitator,
@@ -130,21 +102,18 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
lifecycleScope
)
+ val uixManager = UixManager(this)
+
private var activeThemeOption: ThemeOption? = null
private var activeColorScheme = DarkColorScheme
private var colorSchemeLoaderJob: Job? = null
+ private var pendingRecreateKeyboard: Boolean = false
+
+ val themeOption get() = activeThemeOption
+ val colorScheme get() = activeColorScheme
private var drawableProvider: DynamicThemeProvider? = null
- private var currWindowAction: Action? = null
- private var currWindowActionWindow: ActionWindow? = null
- private var persistentStates: HashMap = hashMapOf()
- private fun isActionWindowOpen(): Boolean {
- return currWindowActionWindow != null
- }
-
- private var inlineSuggestions: List> = listOf()
-
private var lastEditorInfo: EditorInfo? = null
// TODO: Calling this repeatedly as the theme changes tends to slow everything to a crawl
@@ -158,7 +127,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
drawableProvider = BasicThemeProvider(this, overrideColorScheme = colorScheme)
window.window?.navigationBarColor = drawableProvider!!.primaryKeyboardColor
- setContent()
+ uixManager.onColorSchemeChanged()
}
override fun getDrawableProvider(): DynamicThemeProvider {
@@ -193,6 +162,32 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
}
+ fun updateTheme(newTheme: ThemeOption) {
+ assert(newTheme.available(this))
+
+ if (activeThemeOption != newTheme) {
+ activeThemeOption = newTheme
+ updateDrawableProvider(newTheme.obtainColors(this))
+ deferSetSetting(THEME_KEY, newTheme.key)
+
+ if(!uixManager.isMainKeyboardHidden) {
+ recreateKeyboard()
+ } else {
+ pendingRecreateKeyboard = true
+ }
+ }
+ }
+
+ // Called by UixManager when the intention is to subsequently call LegacyKeyboardView with hidden=false
+ // Maybe this can be changed to LaunchedEffect
+ fun onKeyboardShown() {
+ //if(pendingRecreateKeyboard) {
+ // pendingRecreateKeyboard = false
+ // recreateKeyboard()
+ //}
+ }
+
+
override fun onCreate() {
super.onCreate()
@@ -241,41 +236,38 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
private var touchableHeight: Int = 0
override fun onCreateInputView(): View {
legacyInputView = latinIMELegacy.onCreateInputView()
- composeView = ComposeView(this).apply {
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setParentCompositionContext(null)
-
- this@LatinIME.setOwners()
- }
-
- setContent()
+ val composeView = uixManager.createComposeView()
latinIMELegacy.setComposeInputView(composeView)
- return composeView!!
- }
-
- private fun onActionActivated(action: Action) {
- // Finish what we are typing so far
- latinIMELegacy.onFinishInputViewInternal(false)
-
- if (action.windowImpl != null) {
- enterActionWindowView(action)
- } else if (action.simplePressImpl != null) {
- action.simplePressImpl.invoke(this, persistentStates[action])
- } else {
- throw IllegalStateException("An action must have either a window implementation or a simple press implementation")
- }
+ return composeView
}
private var inputViewHeight: Int = -1
- private var shouldShowSuggestionStrip: Boolean = true
- private var suggestedWords: SuggestedWords? = null
+ // Both called by UixManager
+ fun updateTouchableHeight(to: Int) { touchableHeight = to }
+ fun getInputViewHeight(): Int = inputViewHeight
+
+ // The keyboard view really doesn't like being detached, so it's always
+ // shown, but resized to 0 if an action window is open
@Composable
- private fun LegacyKeyboardView(hidden: Boolean) {
+ internal fun LegacyKeyboardView(hidden: Boolean) {
+ LaunchedEffect(hidden) {
+ if(hidden) {
+ latinIMELegacy.mKeyboardSwitcher.saveKeyboardState()
+ } else {
+ if(pendingRecreateKeyboard) {
+ pendingRecreateKeyboard = false
+ recreateKeyboard()
+ }
+ }
+ }
+
val modifier = if(hidden) {
- Modifier.clipToBounds().size(0.dp)
+ Modifier
+ .clipToBounds()
+ .size(0.dp)
} else {
Modifier.onSizeChanged {
inputViewHeight = it.height
@@ -288,127 +280,13 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
}
- @Composable
- private fun MainKeyboardViewWithActionBar() {
- Column {
- // Don't show suggested words when it's not meant to be shown
- val suggestedWordsOrNull = if(shouldShowSuggestionStrip) {
- suggestedWords
- } else {
- null
- }
-
- ActionBar(
- suggestedWordsOrNull,
- latinIMELegacy,
- inlineSuggestions = inlineSuggestions,
- onActionActivated = { onActionActivated(it) }
- )
- }
- }
-
- private fun enterActionWindowView(action: Action) {
- assert(action.windowImpl != null)
-
- latinIMELegacy.mKeyboardSwitcher.saveKeyboardState()
-
- currWindowAction = action
-
- if (persistentStates[action] == null) {
- persistentStates[action] = action.persistentState?.let { it(this) }
- }
-
- currWindowActionWindow = action.windowImpl?.let { it(this, persistentStates[action]) }
-
- setContent()
- }
-
- private fun returnBackToMainKeyboardViewFromAction() {
- if(currWindowActionWindow == null) return
-
- currWindowActionWindow!!.close()
-
- currWindowAction = null
- currWindowActionWindow = null
-
- if(hasThemeChanged) {
- hasThemeChanged = false
- recreateKeyboard()
- }
-
- setContent()
- }
-
- @Composable
- private fun ActionViewWithHeader(windowImpl: ActionWindow) {
- Column {
- Surface(
- modifier = Modifier
- .fillMaxWidth()
- .height(40.dp), color = MaterialTheme.colorScheme.background
- )
- {
- Row {
- IconButton(onClick = {
- returnBackToMainKeyboardViewFromAction()
- }) {
- Icon(
- painter = painterResource(id = R.drawable.arrow_left),
- contentDescription = "Back"
- )
- }
-
- Text(
- windowImpl.windowName(),
- style = Typography.titleMedium,
- modifier = Modifier.align(CenterVertically)
- )
- }
- }
-
- Box(modifier = Modifier
- .fillMaxWidth()
- .height(with(LocalDensity.current) { inputViewHeight.toDp() })
- ) {
- windowImpl.WindowContents()
- }
- }
- }
-
- private fun setContent() {
- composeView?.setContent {
- UixThemeWrapper(activeColorScheme) {
- Column {
- Spacer(modifier = Modifier.weight(1.0f))
- Surface(modifier = Modifier.onSizeChanged {
- touchableHeight = it.height
- }) {
- Column {
- when {
- isActionWindowOpen() -> ActionViewWithHeader(
- currWindowActionWindow!!
- )
-
- else -> MainKeyboardViewWithActionBar()
- }
-
- // The keyboard view really doesn't like being detached, so it's always
- // shown, but resized to 0 if an action window is open
- LegacyKeyboardView(hidden = isActionWindowOpen())
- }
- }
- }
- }
- }
- }
-
// necessary for when KeyboardSwitcher updates the theme
fun updateLegacyView(newView: View) {
legacyInputView = newView
- setContent()
- if (composeView != null) {
- latinIMELegacy.setComposeInputView(composeView)
+ uixManager.setContent()
+ uixManager.getComposeView()?.let {
+ latinIMELegacy.setComposeInputView(it)
}
latinIMELegacy.setInputView(legacyInputView)
@@ -417,8 +295,8 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
override fun setInputView(view: View?) {
super.setInputView(view)
- if (composeView != null) {
- latinIMELegacy.setComposeInputView(composeView)
+ uixManager.getComposeView()?.let {
+ latinIMELegacy.setComposeInputView(it)
}
latinIMELegacy.setInputView(legacyInputView)
@@ -443,15 +321,14 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
override fun onFinishInputView(finishingInput: Boolean) {
super.onFinishInputView(finishingInput)
latinIMELegacy.onFinishInputView(finishingInput)
-
- closeActionWindow()
+ uixManager.onInputFinishing()
}
override fun onFinishInput() {
super.onFinishInput()
latinIMELegacy.onFinishInput()
- closeActionWindow()
+ uixManager.onInputFinishing()
languageModelFacilitator.saveHistoryLog()
}
@@ -471,7 +348,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
super.onWindowHidden()
latinIMELegacy.onWindowHidden()
- closeActionWindow()
+ uixManager.onInputFinishing()
}
override fun onUpdateSelection(
@@ -521,12 +398,14 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
override fun onComputeInsets(outInsets: Insets?) {
+ val composeView = uixManager.getComposeView()
+
// This method may be called before {@link #setInputView(View)}.
if (legacyInputView == null || composeView == null) {
return
}
- val inputHeight: Int = composeView!!.height
+ val inputHeight: Int = composeView.height
if (latinIMELegacy.isImeSuppressedByHardwareKeyboard && !legacyInputView!!.isShown) {
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
// no visual element will be shown on the screen.
@@ -541,7 +420,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
val touchLeft = 0
val touchTop = visibleTopY
- val touchRight = composeView!!.width
+ val touchRight = composeView.width
val touchBottom = inputHeight
latinIMELegacy.setInsets(outInsets!!.apply {
@@ -581,124 +460,25 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
override fun updateVisibility(shouldShowSuggestionsStrip: Boolean, fullscreenMode: Boolean) {
- this.shouldShowSuggestionStrip = shouldShowSuggestionsStrip
- setContent()
+ uixManager.updateVisibility(shouldShowSuggestionsStrip, fullscreenMode)
}
override fun setSuggestions(suggestedWords: SuggestedWords?, rtlSubtype: Boolean) {
- this.suggestedWords = suggestedWords
- setContent()
+ uixManager.setSuggestions(suggestedWords, rtlSubtype)
}
override fun maybeShowImportantNoticeTitle(): Boolean {
return false
}
- private fun cleanUpPersistentStates() {
- println("Cleaning up persistent states")
- for((key, value) in persistentStates.entries) {
- if(currWindowAction != key) {
- lifecycleScope.launch { value?.cleanUp() }
- }
- }
- }
-
override fun onLowMemory() {
super.onLowMemory()
- cleanUpPersistentStates()
+ uixManager.cleanUpPersistentStates()
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
- cleanUpPersistentStates()
- }
-
- override fun getContext(): Context {
- return this
- }
-
- override fun getLifecycleScope(): LifecycleCoroutineScope {
- return lifecycleScope
- }
-
- override fun triggerContentUpdate() {
- setContent()
- }
-
- private class LatinIMEActionInputTransaction(
- private val latinIME: LatinIME,
- shouldApplySpace: Boolean
- ): ActionInputTransaction {
- private val isSpaceNecessary: Boolean
- init {
- val priorText = latinIME.latinIMELegacy.mInputLogic.mConnection.getTextBeforeCursor(1, 0)
- isSpaceNecessary = shouldApplySpace && !priorText.isNullOrEmpty() && !priorText.last().isWhitespace()
- }
-
- private fun transformText(text: String): String {
- return if(isSpaceNecessary) { " $text" } else { text }
- }
-
- override fun updatePartial(text: String) {
- latinIME.latinIMELegacy.mInputLogic.mConnection.setComposingText(
- transformText(text),
- 1
- )
- }
-
- override fun commit(text: String) {
- latinIME.latinIMELegacy.mInputLogic.mConnection.commitText(
- transformText(text),
- 1
- )
- }
-
- override fun cancel() {
- // TODO: Do we want to leave the composing text as-is, or delete it?
- latinIME.latinIMELegacy.mInputLogic.mConnection.finishComposingText()
- }
- }
-
- override fun createInputTransaction(applySpaceIfNeeded: Boolean): ActionInputTransaction {
- return LatinIMEActionInputTransaction(this, applySpaceIfNeeded)
- }
-
- override fun typeText(v: String) {
- latinIMELegacy.mInputLogic.mConnection.commitText(v, 1)
- }
-
- override fun backspace(amount: Int) {
- latinIMELegacy.mInputLogic.mConnection.deleteTextBeforeCursor(amount)
- }
-
- override fun closeActionWindow() {
- if(currWindowActionWindow == null) return
- returnBackToMainKeyboardViewFromAction()
- }
-
- override fun triggerSystemVoiceInput() {
- latinIMELegacy.onCodeInput(
- Constants.CODE_SHORTCUT,
- Constants.SUGGESTION_STRIP_COORDINATE,
- Constants.SUGGESTION_STRIP_COORDINATE,
- false
- );
- }
-
- private var hasThemeChanged: Boolean = false
- override fun updateTheme(newTheme: ThemeOption) {
- assert(newTheme.available(this))
-
- if (activeThemeOption != newTheme) {
- activeThemeOption = newTheme
- updateDrawableProvider(newTheme.obtainColors(this))
- deferSetSetting(THEME_KEY, newTheme.key)
-
- hasThemeChanged = true
- if(!isActionWindowOpen()) {
- recreateKeyboard()
- }
- }
+ uixManager.cleanUpPersistentStates()
}
@RequiresApi(Build.VERSION_CODES.R)
@@ -708,12 +488,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
@RequiresApi(Build.VERSION_CODES.R)
override fun onInlineSuggestionsResponse(response: InlineSuggestionsResponse): Boolean {
- inlineSuggestions = response.inlineSuggestions.map {
- inflateInlineSuggestion(it)
- }
- setContent()
-
- return true
+ return uixManager.onInlineSuggestionsResponse(response)
}
fun postUpdateSuggestionStrip(inputStyle: Int) {
diff --git a/java/src/org/futo/inputmethod/latin/LatinIMELegacy.java b/java/src/org/futo/inputmethod/latin/LatinIMELegacy.java
index 74f123d34..a6dfe0782 100644
--- a/java/src/org/futo/inputmethod/latin/LatinIMELegacy.java
+++ b/java/src/org/futo/inputmethod/latin/LatinIMELegacy.java
@@ -41,6 +41,7 @@ import android.os.IBinder;
import android.os.Message;
import android.preference.PreferenceManager;
import android.text.InputType;
+import android.text.TextUtils;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -158,6 +159,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
private static final String SCHEME_PACKAGE = "package";
final Settings mSettings;
+ private Locale mLocale;
final DictionaryFacilitator mDictionaryFacilitator =
DictionaryFacilitatorProvider.getDictionaryFacilitator(
false /* isNeededForSpellChecking */);
@@ -671,18 +673,18 @@ public class LatinIMELegacy implements KeyboardActionListener,
// Has to be package-visible for unit tests
@UsedForTesting
void loadSettings() {
- final Locale locale = mRichImm.getCurrentSubtypeLocale();
+ mLocale = mRichImm.getCurrentSubtypeLocale();
final EditorInfo editorInfo = mInputMethodService.getCurrentInputEditorInfo();
final InputAttributes inputAttributes = new InputAttributes(
editorInfo, mInputMethodService.isFullscreenMode(), mInputMethodService.getPackageName());
- mSettings.loadSettings(mInputMethodService, locale, inputAttributes);
+ mSettings.loadSettings(mInputMethodService, mLocale, inputAttributes);
final SettingsValues currentSettingsValues = mSettings.getCurrent();
AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(currentSettingsValues);
// This method is called on startup and language switch, before the new layout has
// been displayed. Opening dictionaries never affects responsivity as dictionaries are
// asynchronously loaded.
if (!mHandler.hasPendingReopenDictionaries()) {
- resetDictionaryFacilitator(locale);
+ resetDictionaryFacilitator(mLocale);
}
refreshPersonalizationDictionarySession(currentSettingsValues);
resetDictionaryFacilitatorIfNecessary();
@@ -1365,6 +1367,54 @@ public class LatinIMELegacy implements KeyboardActionListener,
return false;
}
+ @Override
+ public void onMovePointer(int steps) {
+ if (mInputLogic.mConnection.hasCursorPosition()) {
+ if (TextUtils.getLayoutDirectionFromLocale(mLocale) == View.LAYOUT_DIRECTION_RTL)
+ steps = -steps;
+
+ steps = mInputLogic.mConnection.getUnicodeSteps(steps, true);
+ final int end = mInputLogic.mConnection.getExpectedSelectionEnd() + steps;
+ final int start = mInputLogic.mConnection.hasSelection() ? mInputLogic.mConnection.getExpectedSelectionStart() : end;
+
+ mInputLogic.finishInput();
+ mInputLogic.mConnection.setSelection(start, end);
+ } else {
+ for (; steps < 0; steps++)
+ mInputLogic.sendDownUpKeyEvent(KeyEvent.KEYCODE_DPAD_LEFT, 0);
+ for (; steps > 0; steps--)
+ mInputLogic.sendDownUpKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, 0);
+ }
+ }
+
+ @Override
+ public void onMoveDeletePointer(int steps) {
+ if (mInputLogic.mConnection.hasCursorPosition()) {
+ steps = mInputLogic.mConnection.getUnicodeSteps(steps, false);
+ final int end = mInputLogic.mConnection.getExpectedSelectionEnd();
+ final int start = mInputLogic.mConnection.getExpectedSelectionStart() + steps;
+ if (start > end)
+ return;
+
+ mInputLogic.finishInput();
+ mInputLogic.mConnection.setSelection(start, end);
+ } else {
+ for (; steps < 0; steps++)
+ mInputLogic.sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, 0);
+ }
+ }
+
+ @Override
+ public void onUpWithDeletePointerActive() {
+ if (mInputLogic.mConnection.hasSelection())
+ mInputLogic.sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, 0);
+ }
+
+ @Override
+ public void onUpWithPointerActive() {
+ mInputLogic.restartSuggestionsOnWordTouchedByCursor(mSettings.getCurrent(), false, mKeyboardSwitcher.getCurrentKeyboardScriptId());
+ }
+
private boolean isShowingOptionDialog() {
return mOptionsDialog != null && mOptionsDialog.isShowing();
}
diff --git a/java/src/org/futo/inputmethod/latin/RichInputConnection.java b/java/src/org/futo/inputmethod/latin/RichInputConnection.java
index 5c07d7e4d..6e719a1dc 100644
--- a/java/src/org/futo/inputmethod/latin/RichInputConnection.java
+++ b/java/src/org/futo/inputmethod/latin/RichInputConnection.java
@@ -1044,4 +1044,41 @@ public final class RichInputConnection implements PrivateCommandPerformer {
return InputConnectionCompatUtils.requestCursorUpdates(
mIC, enableMonitor, requestImmediateCallback);
}
+
+ public boolean hasCursorPosition() {
+ return mExpectedSelStart != INVALID_CURSOR_POSITION && mExpectedSelEnd != INVALID_CURSOR_POSITION;
+ }
+
+ /**
+ * Some chars, such as emoji consist of 2 chars (surrogate pairs). We should treat them as one character.
+ */
+ public int getUnicodeSteps(int chars, boolean rightSidePointer) {
+ int steps = 0;
+ if (chars < 0) {
+ CharSequence charsBeforeCursor = rightSidePointer && hasSelection() ?
+ getSelectedText(0) :
+ getTextBeforeCursor(-chars * 2, 0);
+ if (charsBeforeCursor != null) {
+ for (int i = charsBeforeCursor.length() - 1; i >= 0 && chars < 0; i--, chars++, steps--) {
+ if (Character.isSurrogate(charsBeforeCursor.charAt(i))) {
+ steps--;
+ i--;
+ }
+ }
+ }
+ } else if (chars > 0) {
+ CharSequence charsAfterCursor = !rightSidePointer && hasSelection() ?
+ getSelectedText(0) :
+ getTextAfterCursor(chars * 2, 0);
+ if (charsAfterCursor != null) {
+ for (int i = 0; i < charsAfterCursor.length() && chars > 0; i++, chars--, steps++) {
+ if (Character.isSurrogate(charsAfterCursor.charAt(i))) {
+ steps++;
+ i++;
+ }
+ }
+ }
+ }
+ return steps;
+ }
}
diff --git a/java/src/org/futo/inputmethod/latin/inputlogic/InputLogic.java b/java/src/org/futo/inputmethod/latin/inputlogic/InputLogic.java
index 41ff7f60b..96a3fcf11 100644
--- a/java/src/org/futo/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/org/futo/inputmethod/latin/inputlogic/InputLogic.java
@@ -1132,7 +1132,7 @@ public final class InputLogic {
// As for the case where we don't know the cursor position, it can happen
// because of bugs in the framework. But the framework should know, so the next
// best thing is to leave it to whatever it thinks is best.
- sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
+ sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, 0);
int totalDeletedLength = 1;
if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
// If this is an accelerated (i.e., double) deletion, then we need to
@@ -1140,7 +1140,7 @@ public final class InputLogic {
// the previous word, and will lose it after next deletion.
hasUnlearnedWordBeingDeleted |= unlearnWordBeingDeleted(
inputTransaction.mSettingsValues, currentKeyboardScriptId);
- sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
+ sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, 0);
totalDeletedLength++;
}
StatsUtils.onBackspacePressed(totalDeletedLength);
@@ -2021,13 +2021,13 @@ public final class InputLogic {
*
* @param keyCode the key code to send inside the key event.
*/
- private void sendDownUpKeyEvent(final int keyCode) {
+ public void sendDownUpKeyEvent(final int keyCode, final int metaState) {
final long eventTime = SystemClock.uptimeMillis();
mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime,
- KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ KeyEvent.ACTION_DOWN, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
- KeyEvent.ACTION_UP, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ KeyEvent.ACTION_UP, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
}
@@ -2045,7 +2045,7 @@ public final class InputLogic {
// TODO: Remove this special handling of digit letters.
// For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
if (codePoint >= '0' && codePoint <= '9') {
- sendDownUpKeyEvent(codePoint - '0' + KeyEvent.KEYCODE_0);
+ sendDownUpKeyEvent(codePoint - '0' + KeyEvent.KEYCODE_0, 0);
return;
}
@@ -2055,7 +2055,7 @@ public final class InputLogic {
// a hardware keyboard event on pressing enter or delete. This is bad for many
// reasons (there are race conditions with commits) but some applications are
// relying on this behavior so we continue to support it for older apps.
- sendDownUpKeyEvent(KeyEvent.KEYCODE_ENTER);
+ sendDownUpKeyEvent(KeyEvent.KEYCODE_ENTER, 0);
} else {
mConnection.commitText(StringUtils.newSingleCodePointString(codePoint), 1);
}
diff --git a/java/src/org/futo/inputmethod/latin/uix/Action.kt b/java/src/org/futo/inputmethod/latin/uix/Action.kt
index aa21b1bfd..c23244ed5 100644
--- a/java/src/org/futo/inputmethod/latin/uix/Action.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/Action.kt
@@ -29,6 +29,9 @@ interface KeyboardManagerForAction {
fun triggerSystemVoiceInput()
fun updateTheme(newTheme: ThemeOption)
+
+ fun sendCodePointEvent(codePoint: Int)
+ fun sendKeyEvent(keyCode: Int, metaState: Int)
}
interface ActionWindow {
@@ -36,7 +39,7 @@ interface ActionWindow {
fun windowName(): String
@Composable
- fun WindowContents()
+ fun WindowContents(keyboardShown: Boolean)
fun close()
}
@@ -54,6 +57,8 @@ interface PersistentActionState {
data class Action(
@DrawableRes val icon: Int,
@StringRes val name: Int,
+ val canShowKeyboard: Boolean = false,
+
val windowImpl: ((KeyboardManagerForAction, PersistentActionState?) -> ActionWindow)?,
val simplePressImpl: ((KeyboardManagerForAction, PersistentActionState?) -> Unit)?,
val persistentState: ((KeyboardManagerForAction) -> PersistentActionState)? = null,
diff --git a/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt b/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt
index df22b8249..0b6abfc03 100644
--- a/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/ActionBar.kt
@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.Icon
@@ -20,6 +21,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
@@ -60,10 +62,15 @@ import org.futo.inputmethod.latin.SuggestedWords
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_TYPED
import org.futo.inputmethod.latin.suggestions.SuggestionStripView
+import org.futo.inputmethod.latin.uix.actions.ClipboardAction
import org.futo.inputmethod.latin.uix.actions.EmojiAction
+import org.futo.inputmethod.latin.uix.actions.RedoAction
+import org.futo.inputmethod.latin.uix.actions.TextEditAction
import org.futo.inputmethod.latin.uix.actions.ThemeAction
+import org.futo.inputmethod.latin.uix.actions.UndoAction
import org.futo.inputmethod.latin.uix.actions.VoiceInputAction
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
+import org.futo.inputmethod.latin.uix.theme.Typography
import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
import java.lang.Integer.min
import kotlin.math.ceil
@@ -287,7 +294,7 @@ fun ActionItem(action: Action, onSelect: (Action) -> Unit) {
cornerRadius = CornerRadius(radius, radius)
)
}
- .width(64.dp)
+ .width(50.dp)
.fillMaxHeight(),
colors = IconButtonDefaults.iconButtonColors(contentColor = contentCol)
) {
@@ -317,12 +324,10 @@ fun RowScope.ActionItems(onSelect: (Action) -> Unit) {
ActionItem(EmojiAction, onSelect)
ActionItem(VoiceInputAction, onSelect)
ActionItem(ThemeAction, onSelect)
-
- Box(modifier = Modifier
- .fillMaxHeight()
- .weight(1.0f)) {
-
- }
+ ActionItem(UndoAction, onSelect)
+ ActionItem(RedoAction, onSelect)
+ ActionItem(ClipboardAction, onSelect)
+ ActionItem(TextEditAction, onSelect)
}
@@ -386,7 +391,11 @@ fun ActionBar(
ExpandActionsButton(isActionsOpen.value) { isActionsOpen.value = !isActionsOpen.value }
if(isActionsOpen.value) {
- ActionItems(onActionActivated)
+ LazyRow {
+ item {
+ ActionItems(onActionActivated)
+ }
+ }
} else if(inlineSuggestions.isNotEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
InlineSuggestions(inlineSuggestions)
} else if(words != null) {
@@ -399,7 +408,110 @@ fun ActionBar(
Spacer(modifier = Modifier.weight(1.0f))
}
- ActionItemSmall(VoiceInputAction, onActionActivated)
+ if(!isActionsOpen.value) {
+ ActionItemSmall(VoiceInputAction, onActionActivated)
+ }
+ }
+ }
+}
+
+@Composable
+fun ActionWindowBar(
+ windowName: String,
+ canExpand: Boolean,
+ onBack: () -> Unit,
+ onExpand: () -> Unit
+) {
+ Surface(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(40.dp), color = MaterialTheme.colorScheme.background
+ )
+ {
+ Row {
+ IconButton(onClick = onBack) {
+ Icon(
+ painter = painterResource(id = R.drawable.arrow_left_26),
+ contentDescription = "Back"
+ )
+ }
+
+ Text(
+ windowName,
+ style = Typography.titleMedium,
+ modifier = Modifier.align(CenterVertically)
+ )
+
+ Spacer(modifier = Modifier.weight(1.0f))
+
+ if(canExpand) {
+ IconButton(onClick = onExpand) {
+ Icon(
+ painter = painterResource(id = R.drawable.arrow_up),
+ contentDescription = "Show Keyboard"
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun CollapsibleSuggestionsBar(
+ onClose: () -> Unit,
+ onCollapse: () -> Unit,
+ words: SuggestedWords?,
+ suggestionStripListener: SuggestionStripView.Listener,
+ inlineSuggestions: List>,
+) {
+ Surface(modifier = Modifier
+ .fillMaxWidth()
+ .height(40.dp), color = MaterialTheme.colorScheme.background)
+ {
+ Row {
+ val color = MaterialTheme.colorScheme.primary
+
+ IconButton(
+ onClick = onClose,
+ modifier = Modifier
+ .width(42.dp)
+ .fillMaxHeight()
+ .drawBehind {
+ drawCircle(color = color, radius = size.width / 3.0f + 1.0f)
+ },
+
+ colors = IconButtonDefaults.iconButtonColors(contentColor = MaterialTheme.colorScheme.onPrimary)
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.close),
+ contentDescription = "Close"
+ )
+ }
+
+ if(inlineSuggestions.isNotEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ InlineSuggestions(inlineSuggestions)
+ } else if(words != null) {
+ SuggestionItems(words) {
+ suggestionStripListener.pickSuggestionManually(
+ words.getInfo(it)
+ )
+ }
+ } else {
+ Spacer(modifier = Modifier.weight(1.0f))
+ }
+
+ IconButton(
+ onClick = onCollapse,
+ modifier = Modifier
+ .width(42.dp)
+ .fillMaxHeight(),
+ colors = IconButtonDefaults.iconButtonColors(contentColor = MaterialTheme.colorScheme.onBackground)
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.arrow_down),
+ contentDescription = "Collapse"
+ )
+ }
}
}
}
@@ -491,6 +603,18 @@ fun PreviewExpandedActionBar(colorScheme: ColorScheme = DarkColorScheme) {
}
}
+@Composable
+@Preview
+fun PreviewCollapsibleBar(colorScheme: ColorScheme = DarkColorScheme) {
+ CollapsibleSuggestionsBar(
+ onCollapse = { },
+ onClose = { },
+ words = exampleSuggestedWords,
+ suggestionStripListener = ExampleListener(),
+ inlineSuggestions = listOf()
+ )
+}
+
@Composable
@Preview
diff --git a/java/src/org/futo/inputmethod/latin/uix/BasicThemeProvider.kt b/java/src/org/futo/inputmethod/latin/uix/BasicThemeProvider.kt
index cc18d61d3..2d4359cde 100644
--- a/java/src/org/futo/inputmethod/latin/uix/BasicThemeProvider.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/BasicThemeProvider.kt
@@ -31,6 +31,7 @@ class BasicThemeProvider(val context: Context, val overrideColorScheme: ColorSch
override val keyFeedback: Drawable
+ override val moreKeysTextColor: Int
override val moreKeysKeyboardBackground: Drawable
override val popupKey: Drawable
@@ -107,6 +108,10 @@ class BasicThemeProvider(val context: Context, val overrideColorScheme: ColorSch
val surface = colorScheme.background.toArgb()
val outline = colorScheme.outline.toArgb()
+ val primaryContainer = colorScheme.primaryContainer.toArgb()
+ val onPrimaryContainer = colorScheme.onPrimaryContainer.toArgb()
+
+ val onPrimary = colorScheme.onPrimary.toArgb()
val onSecondary = colorScheme.onSecondary.toArgb()
val onBackground = colorScheme.onBackground.toArgb()
val onBackgroundHalf = colorScheme.onBackground.copy(alpha = 0.5f).toArgb()
@@ -115,6 +120,7 @@ class BasicThemeProvider(val context: Context, val overrideColorScheme: ColorSch
colors[R.styleable.Keyboard_Key_keyTextColor] = onBackground
colors[R.styleable.Keyboard_Key_keyTextInactivatedColor] = onBackgroundHalf
+ colors[R.styleable.Keyboard_Key_keyPressedTextColor] = onPrimary
colors[R.styleable.Keyboard_Key_keyTextShadowColor] = 0
colors[R.styleable.Keyboard_Key_functionalTextColor] = onBackground
colors[R.styleable.Keyboard_Key_keyHintLetterColor] = onBackgroundHalf
@@ -208,10 +214,11 @@ class BasicThemeProvider(val context: Context, val overrideColorScheme: ColorSch
setPadding(0, 0, 0, dp(50.dp).roundToInt())
}
- moreKeysKeyboardBackground = coloredRoundedRectangle(surface, dp(8.dp))
+ moreKeysTextColor = onPrimaryContainer
+ moreKeysKeyboardBackground = coloredRoundedRectangle(primaryContainer, dp(8.dp))
popupKey = StateListDrawable().apply {
- addStateWithHighlightLayerOnPressed(highlight, intArrayOf(),
- coloredRoundedRectangle(surface, dp(8.dp))
+ addStateWithHighlightLayerOnPressed(primary, intArrayOf(),
+ coloredRoundedRectangle(primaryContainer, dp(8.dp))
)
}
}
diff --git a/java/src/org/futo/inputmethod/latin/uix/DynamicThemeProvider.kt b/java/src/org/futo/inputmethod/latin/uix/DynamicThemeProvider.kt
index b090921c2..8ab4a5b2c 100644
--- a/java/src/org/futo/inputmethod/latin/uix/DynamicThemeProvider.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/DynamicThemeProvider.kt
@@ -13,6 +13,7 @@ interface DynamicThemeProvider {
val keyFeedback: Drawable
+ val moreKeysTextColor: Int
val moreKeysKeyboardBackground: Drawable
val popupKey: Drawable
diff --git a/java/src/org/futo/inputmethod/latin/uix/UixManager.kt b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt
new file mode 100644
index 000000000..1e8463c92
--- /dev/null
+++ b/java/src/org/futo/inputmethod/latin/uix/UixManager.kt
@@ -0,0 +1,344 @@
+package org.futo.inputmethod.latin.uix
+
+import android.content.Context
+import android.os.Build
+import android.view.View
+import android.view.inputmethod.InlineSuggestionsResponse
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.launch
+import org.futo.inputmethod.latin.LatinIME
+import org.futo.inputmethod.latin.SuggestedWords
+import org.futo.inputmethod.latin.common.Constants
+import org.futo.inputmethod.latin.inputlogic.InputLogic
+import org.futo.inputmethod.latin.suggestions.SuggestionStripView
+import org.futo.inputmethod.latin.uix.theme.ThemeOption
+import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
+
+private class LatinIMEActionInputTransaction(
+ private val inputLogic: InputLogic,
+ shouldApplySpace: Boolean
+): ActionInputTransaction {
+ private val isSpaceNecessary: Boolean
+ init {
+ val priorText = inputLogic.mConnection.getTextBeforeCursor(1, 0)
+ isSpaceNecessary = shouldApplySpace && !priorText.isNullOrEmpty() && !priorText.last().isWhitespace()
+ }
+
+ private fun transformText(text: String): String {
+ return if(isSpaceNecessary) { " $text" } else { text }
+ }
+
+ override fun updatePartial(text: String) {
+ inputLogic.mConnection.setComposingText(
+ transformText(text),
+ 1
+ )
+ }
+
+ override fun commit(text: String) {
+ inputLogic.mConnection.commitText(
+ transformText(text),
+ 1
+ )
+ }
+
+ override fun cancel() {
+ inputLogic.mConnection.finishComposingText()
+ }
+}
+
+class UixActionKeyboardManager(val uixManager: UixManager, val latinIME: LatinIME) : KeyboardManagerForAction {
+ override fun getContext(): Context {
+ return latinIME
+ }
+
+ override fun getLifecycleScope(): LifecycleCoroutineScope {
+ return latinIME.lifecycleScope
+ }
+
+ override fun triggerContentUpdate() {
+ uixManager.setContent()
+ }
+
+ override fun createInputTransaction(applySpaceIfNeeded: Boolean): ActionInputTransaction {
+ return LatinIMEActionInputTransaction(latinIME.inputLogic, applySpaceIfNeeded)
+ }
+
+ override fun typeText(v: String) {
+ latinIME.latinIMELegacy.onTextInput(v)
+ }
+
+ override fun backspace(amount: Int) {
+ latinIME.latinIMELegacy.onCodeInput(
+ Constants.CODE_DELETE,
+ Constants.NOT_A_COORDINATE,
+ Constants.NOT_A_COORDINATE, false)
+ }
+
+ override fun closeActionWindow() {
+ if(uixManager.currWindowActionWindow == null) return
+ uixManager.returnBackToMainKeyboardViewFromAction()
+ }
+
+ override fun triggerSystemVoiceInput() {
+ latinIME.latinIMELegacy.onCodeInput(
+ Constants.CODE_SHORTCUT,
+ Constants.SUGGESTION_STRIP_COORDINATE,
+ Constants.SUGGESTION_STRIP_COORDINATE,
+ false
+ );
+ }
+
+ override fun updateTheme(newTheme: ThemeOption) {
+ latinIME.updateTheme(newTheme)
+ }
+
+ override fun sendCodePointEvent(codePoint: Int) {
+ latinIME.latinIMELegacy.onCodeInput(codePoint,
+ Constants.NOT_A_COORDINATE,
+ Constants.NOT_A_COORDINATE, false)
+ }
+
+ override fun sendKeyEvent(keyCode: Int, metaState: Int) {
+ latinIME.inputLogic.sendDownUpKeyEvent(keyCode, metaState)
+ }
+}
+
+class UixManager(private val latinIME: LatinIME) {
+ private var shouldShowSuggestionStrip: Boolean = true
+ private var suggestedWords: SuggestedWords? = null
+
+ private var composeView: ComposeView? = null
+
+ private var currWindowAction: Action? = null
+ private var persistentStates: HashMap = hashMapOf()
+
+ private var inlineSuggestions: List> = listOf()
+ private val keyboardManagerForAction = UixActionKeyboardManager(this, latinIME)
+
+ private var mainKeyboardHidden = false
+
+ var currWindowActionWindow: ActionWindow? = null
+
+ val isMainKeyboardHidden get() = mainKeyboardHidden
+
+ private fun onActionActivated(action: Action) {
+ latinIME.inputLogic.finishInput()
+
+ if (action.windowImpl != null) {
+ enterActionWindowView(action)
+ } else if (action.simplePressImpl != null) {
+ action.simplePressImpl.invoke(keyboardManagerForAction, persistentStates[action])
+ } else {
+ throw IllegalStateException("An action must have either a window implementation or a simple press implementation")
+ }
+ }
+
+ @Composable
+ private fun MainKeyboardViewWithActionBar() {
+ Column {
+ // Don't show suggested words when it's not meant to be shown
+ val suggestedWordsOrNull = if(shouldShowSuggestionStrip) {
+ suggestedWords
+ } else {
+ null
+ }
+
+ ActionBar(
+ suggestedWordsOrNull,
+ latinIME.latinIMELegacy as SuggestionStripView.Listener,
+ inlineSuggestions = inlineSuggestions,
+ onActionActivated = { onActionActivated(it) }
+ )
+ }
+ }
+
+ private fun enterActionWindowView(action: Action) {
+ assert(action.windowImpl != null)
+
+ mainKeyboardHidden = true
+
+ currWindowAction = action
+
+ if (persistentStates[action] == null) {
+ persistentStates[action] = action.persistentState?.let { it(keyboardManagerForAction) }
+ }
+
+ currWindowActionWindow = action.windowImpl?.let { it(keyboardManagerForAction, persistentStates[action]) }
+
+ setContent()
+ }
+
+ fun returnBackToMainKeyboardViewFromAction() {
+ if(currWindowActionWindow == null) return
+
+ currWindowActionWindow!!.close()
+
+ currWindowAction = null
+ currWindowActionWindow = null
+
+ mainKeyboardHidden = false
+
+ latinIME.onKeyboardShown()
+
+ setContent()
+ }
+
+ private fun toggleExpandAction() {
+ mainKeyboardHidden = !mainKeyboardHidden
+ if(!mainKeyboardHidden) {
+ latinIME.onKeyboardShown()
+ }
+
+ setContent()
+ }
+
+ @Composable
+ private fun ActionViewWithHeader(windowImpl: ActionWindow) {
+ val heightDiv = if(mainKeyboardHidden) {
+ 1
+ } else {
+ 1.5
+ }
+ Column {
+ if(mainKeyboardHidden) {
+ ActionWindowBar(
+ onBack = { returnBackToMainKeyboardViewFromAction() },
+ canExpand = currWindowAction!!.canShowKeyboard,
+ onExpand = { toggleExpandAction() },
+ windowName = windowImpl.windowName()
+ )
+ }
+
+ Box(modifier = Modifier
+ .fillMaxWidth()
+ .height(with(LocalDensity.current) {
+ (latinIME.getInputViewHeight().toFloat() / heightDiv.toFloat()).toDp()
+ })
+ ) {
+ windowImpl.WindowContents(keyboardShown = !isMainKeyboardHidden)
+ }
+
+ if(!mainKeyboardHidden) {
+ val suggestedWordsOrNull = if (shouldShowSuggestionStrip) {
+ suggestedWords
+ } else {
+ null
+ }
+
+ CollapsibleSuggestionsBar(
+ onCollapse = { toggleExpandAction() },
+ onClose = { returnBackToMainKeyboardViewFromAction() },
+ words = suggestedWordsOrNull,
+ suggestionStripListener = latinIME.latinIMELegacy as SuggestionStripView.Listener,
+ inlineSuggestions = inlineSuggestions
+ )
+ }
+ }
+ }
+
+ fun setContent() {
+ composeView?.setContent {
+ UixThemeWrapper(latinIME.colorScheme) {
+ Column {
+ Spacer(modifier = Modifier.weight(1.0f))
+ Surface(modifier = Modifier.onSizeChanged {
+ latinIME.updateTouchableHeight(it.height)
+ }) {
+ Column {
+ when {
+ currWindowActionWindow != null -> ActionViewWithHeader(
+ currWindowActionWindow!!
+ )
+
+ else -> MainKeyboardViewWithActionBar()
+ }
+
+ latinIME.LegacyKeyboardView(hidden = isMainKeyboardHidden)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fun createComposeView(): View {
+ if(composeView != null) {
+ composeView = null
+ //throw IllegalStateException("Attempted to create compose view, when one is already created!")
+ }
+
+ composeView = ComposeView(latinIME).apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setParentCompositionContext(null)
+
+ latinIME.setOwners()
+ }
+
+ setContent()
+
+ return composeView!!
+ }
+
+ fun getComposeView(): View? {
+ return composeView
+ }
+
+ fun onColorSchemeChanged() {
+ setContent()
+ }
+
+ fun onInputFinishing() {
+ closeActionWindow()
+ }
+
+ fun cleanUpPersistentStates() {
+ println("Cleaning up persistent states")
+ for((key, value) in persistentStates.entries) {
+ if(currWindowAction != key) {
+ latinIME.lifecycleScope.launch { value?.cleanUp() }
+ }
+ }
+ }
+
+ fun closeActionWindow() {
+ if(currWindowActionWindow == null) return
+ returnBackToMainKeyboardViewFromAction()
+ }
+
+
+ fun updateVisibility(shouldShowSuggestionsStrip: Boolean, fullscreenMode: Boolean) {
+ this.shouldShowSuggestionStrip = shouldShowSuggestionsStrip
+ setContent()
+ }
+
+ fun setSuggestions(suggestedWords: SuggestedWords?, rtlSubtype: Boolean) {
+ this.suggestedWords = suggestedWords
+ setContent()
+ }
+
+ @RequiresApi(Build.VERSION_CODES.R)
+ fun onInlineSuggestionsResponse(response: InlineSuggestionsResponse): Boolean {
+ inlineSuggestions = response.inlineSuggestions.map {
+ latinIME.inflateInlineSuggestion(it)
+ }
+ setContent()
+
+ return true
+ }
+}
\ No newline at end of file
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardAction.kt
new file mode 100644
index 000000000..07ee7f3f1
--- /dev/null
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardAction.kt
@@ -0,0 +1,14 @@
+package org.futo.inputmethod.latin.uix.actions
+
+import android.view.KeyEvent
+import org.futo.inputmethod.latin.R
+import org.futo.inputmethod.latin.uix.Action
+
+val ClipboardAction = Action(
+ icon = R.drawable.clipboard,
+ name = R.string.clipboard_action_title,
+ simplePressImpl = { manager, _ ->
+ manager.sendKeyEvent(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON)
+ },
+ windowImpl = null,
+)
\ No newline at end of file
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt
index d23637fda..ec1691ef1 100644
--- a/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/EmojiAction.kt
@@ -55,6 +55,7 @@ import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.futo.inputmethod.latin.R
+import org.futo.inputmethod.latin.common.Constants
import org.futo.inputmethod.latin.uix.Action
import org.futo.inputmethod.latin.uix.ActionWindow
import org.futo.inputmethod.latin.uix.PersistentActionState
@@ -161,7 +162,7 @@ data class BitmapRecycler(
}
@Composable
-fun EmojiGrid(onClick: (EmojiItem) -> Unit, onExit: () -> Unit, onBackspace: () -> Unit, onSpace: () -> Unit, bitmaps: BitmapRecycler, emojis: List) {
+fun EmojiGrid(onClick: (EmojiItem) -> Unit, onExit: () -> Unit, onBackspace: () -> Unit, onSpace: () -> Unit, bitmaps: BitmapRecycler, emojis: List, keyboardShown: Boolean) {
val context = LocalContext.current
val spToDp = context.resources.displayMetrics.scaledDensity / context.resources.displayMetrics.density
@@ -181,41 +182,48 @@ fun EmojiGrid(onClick: (EmojiItem) -> Unit, onExit: () -> Unit, onBackspace: ()
}
}
}
- Surface(color = MaterialTheme.colorScheme.background, modifier = Modifier
- .fillMaxWidth()
- .height(48.dp)) {
- Row(modifier = Modifier.padding(2.dp, 8.dp, 2.dp, 0.dp)) {
- IconButton(onClick = { onExit() }) {
- Text("ABC", fontSize = 14.sp)
- }
- Button(onClick = { onSpace() }, modifier = Modifier
- .weight(1.0f)
- .padding(8.dp, 2.dp), colors = ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.33f),
- contentColor = MaterialTheme.colorScheme.onBackground,
- disabledContainerColor = MaterialTheme.colorScheme.outline,
- disabledContentColor = MaterialTheme.colorScheme.onBackground,
- ), shape = RoundedCornerShape(32.dp)) {
- Text("")
- }
+ if(!keyboardShown) {
+ Surface(
+ color = MaterialTheme.colorScheme.background, modifier = Modifier
+ .fillMaxWidth()
+ .height(48.dp)
+ ) {
+ Row(modifier = Modifier.padding(2.dp, 8.dp, 2.dp, 0.dp)) {
+ IconButton(onClick = { onExit() }) {
+ Text("ABC", fontSize = 14.sp)
+ }
- IconButton(onClick = { onBackspace() }) {
- val icon = painterResource(id = R.drawable.delete)
- val iconColor = MaterialTheme.colorScheme.onBackground
+ Button(
+ onClick = { onSpace() }, modifier = Modifier
+ .weight(1.0f)
+ .padding(8.dp, 2.dp), colors = ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.33f),
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ disabledContainerColor = MaterialTheme.colorScheme.outline,
+ disabledContentColor = MaterialTheme.colorScheme.onBackground,
+ ), shape = RoundedCornerShape(32.dp)
+ ) {
+ Text("")
+ }
- Canvas(modifier = Modifier.fillMaxSize()) {
- translate(
- left = this.size.width / 2.0f - icon.intrinsicSize.width / 2.0f,
- top = this.size.height / 2.0f - icon.intrinsicSize.height / 2.0f
- ) {
- with(icon) {
- draw(
- icon.intrinsicSize,
- colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(
- iconColor
+ IconButton(onClick = { onBackspace() }) {
+ val icon = painterResource(id = R.drawable.delete)
+ val iconColor = MaterialTheme.colorScheme.onBackground
+
+ Canvas(modifier = Modifier.fillMaxSize()) {
+ translate(
+ left = this.size.width / 2.0f - icon.intrinsicSize.width / 2.0f,
+ top = this.size.height / 2.0f - icon.intrinsicSize.height / 2.0f
+ ) {
+ with(icon) {
+ draw(
+ icon.intrinsicSize,
+ colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(
+ iconColor
+ )
)
- )
+ }
}
}
}
@@ -266,7 +274,8 @@ class PersistentEmojiState: PersistentActionState {
val EmojiAction = Action(
icon = R.drawable.smile,
- name = R.string.title_emojis,
+ name = R.string.emoji_action_title,
+ canShowKeyboard = true,
simplePressImpl = null,
persistentState = { manager ->
val state = PersistentEmojiState()
@@ -282,21 +291,21 @@ val EmojiAction = Action(
object : ActionWindow {
@Composable
override fun windowName(): String {
- return stringResource(R.string.title_emojis)
+ return stringResource(R.string.emoji_action_title)
}
@Composable
- override fun WindowContents() {
+ override fun WindowContents(keyboardShown: Boolean) {
state.emojis.value?.let { emojis ->
EmojiGrid(onClick = {
manager.typeText(it.emoji)
}, onExit = {
manager.closeActionWindow()
}, onSpace = {
- manager.typeText(" ")
+ manager.sendCodePointEvent(Constants.CODE_SPACE)
}, onBackspace = {
- manager.backspace(1)
- }, bitmaps = state.bitmaps, emojis = emojis)
+ manager.sendCodePointEvent(Constants.CODE_DELETE)
+ }, bitmaps = state.bitmaps, emojis = emojis, keyboardShown = keyboardShown)
}
}
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/TextEditAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/TextEditAction.kt
new file mode 100644
index 000000000..2990bf2e6
--- /dev/null
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/TextEditAction.kt
@@ -0,0 +1,379 @@
+package org.futo.inputmethod.latin.uix.actions
+
+import android.view.KeyEvent
+import androidx.annotation.DrawableRes
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.LocalIndication
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.delay
+import org.futo.inputmethod.latin.R
+import org.futo.inputmethod.latin.common.Constants
+import org.futo.inputmethod.latin.uix.Action
+import org.futo.inputmethod.latin.uix.ActionWindow
+
+@Composable
+fun IconWithColor(@DrawableRes iconId: Int, iconColor: Color, modifier: Modifier = Modifier) {
+ val icon = painterResource(id = iconId)
+
+ Canvas(modifier = modifier) {
+ translate(
+ left = this.size.width / 2.0f - icon.intrinsicSize.width / 2.0f,
+ top = this.size.height / 2.0f - icon.intrinsicSize.height / 2.0f
+ ) {
+ with(icon) {
+ draw(
+ icon.intrinsicSize,
+ colorFilter = ColorFilter.tint(
+ iconColor
+ )
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun TogglableKey(
+ onToggle: (Boolean) -> Unit,
+ toggled: Boolean,
+ modifier: Modifier = Modifier,
+ contents: @Composable (color: Color) -> Unit
+) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val isPressed by interactionSource.collectIsPressedAsState()
+
+ LaunchedEffect(isPressed) {
+ if(isPressed) {
+ onToggle(!toggled)
+ }
+ }
+
+ Surface(
+ modifier = modifier
+ .padding(4.dp)
+ .clickable(
+ interactionSource = interactionSource,
+ indication = LocalIndication.current,
+ onClick = { }
+ ),
+ shape = RoundedCornerShape(8.dp),
+ color = if(toggled) { MaterialTheme.colorScheme.secondary } else { MaterialTheme.colorScheme.secondaryContainer }
+ ) {
+ contents(if(toggled) { MaterialTheme.colorScheme.onSecondary } else { MaterialTheme.colorScheme.onSecondaryContainer })
+ }
+
+}
+
+@Composable
+fun ActionKey(
+ onTrigger: () -> Unit,
+ modifier: Modifier = Modifier,
+ repeatable: Boolean = true,
+ color: Color = MaterialTheme.colorScheme.primary,
+ contents: @Composable () -> Unit
+) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val isPressed by interactionSource.collectIsPressedAsState()
+
+ LaunchedEffect(isPressed) {
+ if(isPressed) {
+ onTrigger()
+ if(repeatable) {
+ delay(670L)
+ while (isPressed) {
+ onTrigger()
+ delay(50L)
+ }
+ }
+ }
+ }
+
+ Surface(
+ modifier = modifier
+ .padding(4.dp)
+ .clickable(
+ interactionSource = interactionSource,
+ indication = LocalIndication.current,
+ onClick = { }
+ ),
+ shape = RoundedCornerShape(8.dp),
+ color = color
+ ) {
+ contents()
+ }
+}
+
+@Composable
+fun ArrowKeys(modifier: Modifier, sendEvent: (Int) -> Unit) {
+ Row(modifier = modifier) {
+ ActionKey(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxHeight(),
+ onTrigger = { sendEvent(KeyEvent.KEYCODE_DPAD_LEFT) }
+ ) {
+ IconWithColor(
+ iconId = R.drawable.arrow_left,
+ iconColor = MaterialTheme.colorScheme.onPrimary
+ )
+ }
+
+ Column(modifier = Modifier
+ .weight(1.0f)
+ .fillMaxHeight()) {
+ ActionKey(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxWidth(),
+ onTrigger = { sendEvent(KeyEvent.KEYCODE_DPAD_UP) }
+ ) {
+ IconWithColor(
+ iconId = R.drawable.arrow_up,
+ iconColor = MaterialTheme.colorScheme.onPrimary
+ )
+ }
+
+
+ ActionKey(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxWidth(),
+ onTrigger = { sendEvent(KeyEvent.KEYCODE_DPAD_DOWN) }
+ ) {
+ IconWithColor(
+ iconId = R.drawable.arrow_down,
+ iconColor = MaterialTheme.colorScheme.onPrimary
+ )
+ }
+ }
+
+ ActionKey(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxHeight(),
+ onTrigger = { sendEvent(KeyEvent.KEYCODE_DPAD_RIGHT) }
+ ) {
+ IconWithColor(
+ iconId = R.drawable.arrow_right,
+ iconColor = MaterialTheme.colorScheme.onPrimary
+ )
+ }
+ }
+}
+
+@Composable
+fun CtrlShiftMetaKeys(modifier: Modifier, ctrlState: MutableState, shiftState: MutableState) {
+ Row(modifier = modifier) {
+ TogglableKey(
+ onToggle = { ctrlState.value = it },
+ toggled = ctrlState.value,
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxHeight()
+ ) {
+ IconWithColor(
+ iconId = R.drawable.ctrl,
+ iconColor = it
+ )
+ }
+ TogglableKey(
+ onToggle = { shiftState.value = it },
+ toggled = shiftState.value,
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxHeight()
+ ) {
+ IconWithColor(
+ iconId = R.drawable.shift,
+ iconColor = it
+ )
+ }
+ }
+}
+
+@Composable
+fun SideKeys(modifier: Modifier, onEvent: (Int, Int) -> Unit, onCodePoint: (Int) -> Unit, keyboardShown: Boolean) {
+ Column(modifier = modifier) {
+ ActionKey(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxWidth(),
+ repeatable = false,
+ color = MaterialTheme.colorScheme.primaryContainer,
+ onTrigger = { onEvent(KeyEvent.KEYCODE_C, KeyEvent.META_CTRL_ON) }
+ ) {
+ IconWithColor(
+ iconId = R.drawable.copy,
+ iconColor = MaterialTheme.colorScheme.onPrimaryContainer
+ )
+ }
+
+ ActionKey(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxWidth(),
+ repeatable = false,
+ color = MaterialTheme.colorScheme.primaryContainer,
+ onTrigger = { onEvent(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON) }
+ ) {
+ IconWithColor(
+ iconId = R.drawable.clipboard,
+ iconColor = MaterialTheme.colorScheme.onPrimaryContainer
+ )
+ }
+
+ if(!keyboardShown) {
+ ActionKey(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxWidth(),
+ repeatable = true,
+ color = MaterialTheme.colorScheme.primaryContainer,
+ onTrigger = { onCodePoint(Constants.CODE_DELETE) }
+ ) {
+ IconWithColor(
+ iconId = R.drawable.delete,
+ iconColor = MaterialTheme.colorScheme.onPrimaryContainer
+ )
+ }
+ }
+
+
+ Row(modifier = Modifier
+ .weight(1.0f)
+ .fillMaxWidth()) {
+ ActionKey(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxHeight(),
+ repeatable = false,
+ color = MaterialTheme.colorScheme.primaryContainer,
+ onTrigger = { onEvent(KeyEvent.KEYCODE_Z, KeyEvent.META_CTRL_ON) }
+ ) {
+ IconWithColor(
+ iconId = R.drawable.undo,
+ iconColor = MaterialTheme.colorScheme.onPrimaryContainer
+ )
+ }
+
+ ActionKey(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxHeight(),
+ repeatable = false,
+ color = MaterialTheme.colorScheme.primaryContainer,
+ onTrigger = { onEvent(KeyEvent.KEYCODE_Y, KeyEvent.META_CTRL_ON) }
+ ) {
+ IconWithColor(
+ iconId = R.drawable.redo,
+ iconColor = MaterialTheme.colorScheme.onPrimaryContainer
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun TextEditScreen(onCodePoint: (Int) -> Unit, onEvent: (Int, Int) -> Unit, keyboardShown: Boolean) {
+ val shiftState = remember { mutableStateOf(false) }
+ val ctrlState = remember { mutableStateOf(false) }
+
+ val metaState = 0 or
+ (if(shiftState.value) { KeyEvent.META_SHIFT_ON } else { 0 }) or
+ (if(ctrlState.value) { KeyEvent.META_CTRL_ON } else { 0 })
+
+ val sendEvent = { keycode: Int -> onEvent(keycode, metaState) }
+
+ Row(modifier = Modifier.fillMaxSize()) {
+ Column(modifier = Modifier
+ .fillMaxHeight()
+ .weight(3.0f)) {
+ ArrowKeys(
+ modifier = Modifier
+ .weight(3.0f)
+ .fillMaxWidth(),
+ sendEvent = sendEvent
+ )
+ CtrlShiftMetaKeys(
+ modifier = Modifier
+ .weight(1.0f)
+ .fillMaxWidth(),
+ ctrlState = ctrlState,
+ shiftState = shiftState
+ )
+ }
+ SideKeys(
+ modifier = Modifier
+ .fillMaxHeight()
+ .weight(1.0f),
+ onEvent = onEvent,
+ onCodePoint = onCodePoint,
+ keyboardShown = keyboardShown
+ )
+ }
+}
+
+val TextEditAction = Action(
+ icon = R.drawable.edit_text,
+ name = R.string.text_edit_action_title,
+ simplePressImpl = null,
+ persistentState = null,
+ canShowKeyboard = true,
+ windowImpl = { manager, persistentState ->
+ object : ActionWindow {
+ @Composable
+ override fun windowName(): String {
+ return stringResource(R.string.text_edit_action_title)
+ }
+
+ @Composable
+ override fun WindowContents(keyboardShown: Boolean) {
+ TextEditScreen(onCodePoint = { a -> manager.sendCodePointEvent(a)}, onEvent = { a, b -> manager.sendKeyEvent(a, b) }, keyboardShown = keyboardShown)
+ }
+
+ override fun close() {
+ }
+ }
+ }
+)
+
+@Composable
+@Preview(showBackground = true)
+fun TextEditScreenPreview() {
+ Surface(modifier = Modifier.height(256.dp)) {
+ TextEditScreen(onCodePoint = { }, onEvent = { _, _ -> }, keyboardShown = false)
+ }
+}
+@Composable
+@Preview(showBackground = true)
+fun TextEditScreenPreviewWithKb() {
+ Surface(modifier = Modifier.height(256.dp)) {
+ TextEditScreen(onCodePoint = { }, onEvent = { _, _ -> }, keyboardShown = true)
+ }
+}
\ No newline at end of file
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/ThemeAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/ThemeAction.kt
index 63577e24a..b25bf7303 100644
--- a/java/src/org/futo/inputmethod/latin/uix/actions/ThemeAction.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/ThemeAction.kt
@@ -1,27 +1,17 @@
package org.futo.inputmethod.latin.uix.actions
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.uix.Action
import org.futo.inputmethod.latin.uix.ActionWindow
-import org.futo.inputmethod.latin.uix.KeyboardManagerForAction
-import org.futo.inputmethod.latin.uix.theme.ThemeOptionKeys
-import org.futo.inputmethod.latin.uix.theme.ThemeOptions
import org.futo.inputmethod.latin.uix.theme.selector.ThemePicker
val ThemeAction = Action(
icon = R.drawable.eye,
name = R.string.theme_switcher_action_title,
simplePressImpl = null,
+ canShowKeyboard = true,
windowImpl = { manager, _ ->
object : ActionWindow {
@Composable
@@ -30,33 +20,8 @@ val ThemeAction = Action(
}
@Composable
- override fun WindowContents() {
- val context = LocalContext.current
-
+ override fun WindowContents(keyboardShown: Boolean) {
ThemePicker { manager.updateTheme(it) }
- /*
- LazyColumn(
- modifier = Modifier
- .padding(8.dp, 0.dp)
- .fillMaxWidth()
- )
- {
- items(ThemeOptionKeys.count()) {
- val key = ThemeOptionKeys[it]
- val themeOption = ThemeOptions[key]
- if (themeOption != null && themeOption.available(context)) {
- Button(onClick = {
- manager.updateTheme(
- themeOption
- )
- }) {
- Text(stringResource(themeOption.name))
- }
- }
- }
- }
-
- */
}
override fun close() {
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/UndoRedoActions.kt b/java/src/org/futo/inputmethod/latin/uix/actions/UndoRedoActions.kt
new file mode 100644
index 000000000..09742728a
--- /dev/null
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/UndoRedoActions.kt
@@ -0,0 +1,22 @@
+package org.futo.inputmethod.latin.uix.actions
+
+import android.view.KeyEvent
+import org.futo.inputmethod.latin.R
+import org.futo.inputmethod.latin.uix.Action
+
+val UndoAction = Action(
+ icon = R.drawable.undo,
+ name = R.string.undo_action_title,
+ simplePressImpl = { manager, _ ->
+ manager.sendKeyEvent(KeyEvent.KEYCODE_Z, KeyEvent.META_CTRL_ON)
+ },
+ windowImpl = null,
+)
+val RedoAction = Action(
+ icon = R.drawable.redo,
+ name = R.string.redo_action_title,
+ simplePressImpl = { manager, _ ->
+ manager.sendKeyEvent(KeyEvent.KEYCODE_Y, KeyEvent.META_CTRL_ON)
+ },
+ windowImpl = null,
+)
\ No newline at end of file
diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/VoiceInputAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/VoiceInputAction.kt
index d4ee4504f..4cc1e574c 100644
--- a/java/src/org/futo/inputmethod/latin/uix/actions/VoiceInputAction.kt
+++ b/java/src/org/futo/inputmethod/latin/uix/actions/VoiceInputAction.kt
@@ -180,7 +180,7 @@ private class VoiceInputActionWindow(
}
@Composable
- override fun WindowContents() {
+ override fun WindowContents(keyboardShown: Boolean) {
Box(modifier = Modifier
.fillMaxSize()
.clickable(enabled = true,
diff --git a/native/jni/org_futo_inputmethod_latin_xlm_LanguageModel.cpp b/native/jni/org_futo_inputmethod_latin_xlm_LanguageModel.cpp
index f2a2f4e94..a844ae0ee 100644
--- a/native/jni/org_futo_inputmethod_latin_xlm_LanguageModel.cpp
+++ b/native/jni/org_futo_inputmethod_latin_xlm_LanguageModel.cpp
@@ -651,6 +651,9 @@ namespace latinime {
std::vector mixes;
for(int i=0; i= 'a' && wc <= 'z') && !(wc >= 'A' && wc <= 'Z')) continue;
+
std::vector proportions = pInfo->decomposeTapPosition(xCoordinates[i], yCoordinates[i]);
for(float &f : proportions) {
if(f < 0.05f) f = 0.0f;
@@ -701,12 +704,12 @@ namespace latinime {
results.x = ((float)xCoordinates[i]) / ((float)pInfo->getKeyboardWidth());
results.y = ((float)yCoordinates[i]) / ((float)pInfo->getKeyboardHeight());
- AKLOGI("%d | Char %c, pos %.6f %.6f, nearest is %c at %.2f, then %c at %.2f, finally %c at %.2f", i, partialWordString[i],
- results.x, results.y,
- (char)(pInfo->getKeyCodePoint(index_value[0].second)), (float)(index_value[0].first),
- (char)(pInfo->getKeyCodePoint(index_value[1].second)), (float)(index_value[1].first),
- (char)(pInfo->getKeyCodePoint(index_value[2].second)), (float)(index_value[2].first)
- );
+ //AKLOGI("%d | Char %c, pos %.6f %.6f, nearest is %c at %.2f, then %c at %.2f, finally %c at %.2f", i, partialWordString[i],
+ // results.x, results.y,
+ // (char)(pInfo->getKeyCodePoint(index_value[0].second)), (float)(index_value[0].first),
+ // (char)(pInfo->getKeyCodePoint(index_value[1].second)), (float)(index_value[1].first),
+ // (char)(pInfo->getKeyCodePoint(index_value[2].second)), (float)(index_value[2].first)
+ // );
for(int j=0; j