Add option to swipe spacebar to change language

This commit is contained in:
Aleksandras Kostarevas 2024-07-14 16:19:36 +03:00
parent 6613e3e57f
commit d52b5ddfdd
10 changed files with 141 additions and 34 deletions

View File

@ -251,6 +251,9 @@ public final class Constants {
public static final int CODE_ACTION_0 = -1050;
public static final int CODE_ACTION_MAX = CODE_ACTION_0 + 100;
public static final int CODE_ALT_ACTION_0 = -2050;
public static final int CODE_ALT_ACTION_MAX = CODE_ALT_ACTION_0 + 100;
public static boolean isLetterCode(final int code) {
return code >= CODE_SPACE;
}

View File

@ -105,6 +105,8 @@ public interface KeyboardActionListener {
public void onMoveDeletePointer(int steps);
public void onUpWithDeletePointerActive();
public void onUpWithPointerActive();
public void onSwipeLanguage(int direction);
public void onMovingCursorLockEvent(boolean canMoveCursor);
public static final KeyboardActionListener EMPTY_LISTENER = new Adapter();
@ -139,5 +141,9 @@ public interface KeyboardActionListener {
public void onUpWithDeletePointerActive() {}
@Override
public void onUpWithPointerActive() {}
@Override
public void onSwipeLanguage(int direction) {}
@Override
public void onMovingCursorLockEvent(boolean canMoveCursor) {}
}
}

View File

@ -39,6 +39,7 @@ import org.futo.inputmethod.latin.common.CoordinateUtils;
import org.futo.inputmethod.latin.common.InputPointers;
import org.futo.inputmethod.latin.define.DebugFlags;
import org.futo.inputmethod.latin.settings.Settings;
import org.futo.inputmethod.latin.settings.SettingsValues;
import org.futo.inputmethod.latin.utils.ResourceUtils;
import java.util.ArrayList;
@ -86,6 +87,11 @@ 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 final int sPointerBigStep = (int)(32.0 * Resources.getSystem().getDisplayMetrics().density);
private static final int sPointerHugeStep = Integer.min(
(int)(128.0 * Resources.getSystem().getDisplayMetrics().density),
Resources.getSystem().getDisplayMetrics().widthPixels * 3 / 2
);
private static GestureStrokeRecognitionParams sGestureStrokeRecognitionParams;
private static GestureStrokeDrawingParams sGestureStrokeDrawingParams;
@ -135,6 +141,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
private int mStartY;
private long mStartTime;
private boolean mCursorMoved = false;
private boolean mSpacebarLongPressed = false;
// true if keyboard layout has been changed.
private boolean mKeyboardLayoutHasBeenChanged;
@ -705,6 +712,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
mStartX = x;
mStartY = y;
mStartTime = System.currentTimeMillis();
mSpacebarLongPressed = false;
mIsSlidingCursor = key.getCode() == Constants.CODE_DELETE || key.getCode() == Constants.CODE_SPACE;
mCurrentKey = key;
@ -910,25 +918,48 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
final int lastY = mLastY;
final Key oldKey = mCurrentKey;
final SettingsValues settingsValues = Settings.getInstance().getCurrent();
if (mIsSlidingCursor && 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;
int pointerStep = sPointerStep;
if(settingsValues.mSpacebarMode == Settings.SPACEBAR_MODE_SWIPE_LANGUAGE && !mSpacebarLongPressed) {
pointerStep = sPointerHugeStep;
}
int steps = (x - mStartX) / pointerStep;
final int swipeIgnoreTime = settingsValues.mKeyLongpressTimeout / MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
if (steps != 0 && mStartTime + swipeIgnoreTime < System.currentTimeMillis()) {
mCursorMoved = true;
mStartX += steps * sPointerStep;
sListener.onMovePointer(steps);
mStartX += steps * pointerStep;
if(settingsValues.mSpacebarMode == Settings.SPACEBAR_MODE_SWIPE_LANGUAGE && !mSpacebarLongPressed) {
sListener.onSwipeLanguage(steps);
} else {
sListener.onMovePointer(steps);
}
}
mLastX = x;
mLastY = y;
return;
}
if (mIsSlidingCursor && oldKey != null && oldKey.getCode() == Constants.CODE_DELETE) {
int steps = (x - mStartX) / sPointerStep;
int pointerStep = sPointerStep;
if(settingsValues.mBackspaceMode == Settings.BACKSPACE_MODE_WORDS) {
pointerStep = sPointerBigStep;
}
int steps = (x - mStartX) / pointerStep;
if (steps != 0) {
sTimerProxy.cancelKeyTimersOf(this);
mCursorMoved = true;
mStartX += steps * sPointerStep;
mStartX += steps * pointerStep;
sListener.onMoveDeletePointer(steps);
}
mLastX = x;
mLastY = y;
return;
}
@ -1086,6 +1117,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
}
final int code = key.getCode();
if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
int spacebarMode = Settings.getInstance().getCurrent().mSpacebarMode;
if(spacebarMode == Settings.SPACEBAR_MODE_SWIPE_LANGUAGE) {
mSpacebarLongPressed = true;
mStartX = mLastX;
mStartY = mLastY;
sListener.onMovingCursorLockEvent(true);
return;
}else if(spacebarMode == Settings.SPACEBAR_MODE_SWIPE_CURSOR_ONLY) {
return;
}
// Long pressing the space key invokes IME switcher dialog.
if (sListener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) {
cancelKeyTracking();
@ -1094,6 +1136,16 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
}
}
if (code > Constants.CODE_ACTION_0 && code < Constants.CODE_ACTION_MAX) {
cancelKeyTracking();
sListener.onCodeInput(
code - Constants.CODE_ACTION_0 + Constants.CODE_ALT_ACTION_0,
Constants.NOT_A_COORDINATE,
Constants.NOT_A_COORDINATE,
false /* isKeyRepeat */);
return;
}
setReleasedKeyGraphics(key, false /* withAnimation */);
final MoreKeysPanel moreKeysPanel = sDrawingProxy.showMoreKeysKeyboard(key, this);
if (moreKeysPanel == null) {
@ -1196,6 +1248,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
// We use longer timeout for sliding finger input started from the modifier key.
return longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
}
if (code == Constants.CODE_SPACE) {
return longpressTimeout * 2;
}
return longpressTimeout;
}

View File

@ -186,7 +186,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
mParams.mKeyStyles.addDynamicKeyStyle("bottomEmojiKeyStyle",
actionKeySpec,
2,
2);
0x02 | 0x08);
final XmlResourceParser parser = mResources.getXml(xmlId);
try {

View File

@ -535,7 +535,14 @@ public class LatinIMELegacy implements KeyboardActionListener,
public void triggerAction(int actionId) {
final LatinIMELegacy latinImeLegacy = getOwnerInstance();
if (latinImeLegacy != null) {
((LatinIME) (latinImeLegacy.getInputMethodService())).getUixManager().triggerAction(actionId);
((LatinIME) (latinImeLegacy.getInputMethodService())).getUixManager().triggerAction(actionId, false);
}
}
public void triggerActionAlt(int actionId) {
final LatinIMELegacy latinImeLegacy = getOwnerInstance();
if (latinImeLegacy != null) {
((LatinIME) (latinImeLegacy.getInputMethodService())).getUixManager().triggerAction(actionId, true);
}
}
}
@ -1355,12 +1362,24 @@ public class LatinIMELegacy implements KeyboardActionListener,
mInputLogic.restartSuggestionsOnWordTouchedByCursor(mSettings.getCurrent(), false, mKeyboardSwitcher.getCurrentKeyboardScriptId());
}
@Override
public void onSwipeLanguage(int direction) {
Subtypes.INSTANCE.switchToNextLanguage(mInputMethodService, direction);
}
@Override
public void onMovingCursorLockEvent(boolean canMoveCursor) {
if(canMoveCursor) {
hapticAndAudioFeedback(Constants.CODE_UNSPECIFIED, 0);
}
}
private boolean isShowingOptionDialog() {
return mOptionsDialog != null && mOptionsDialog.isShowing();
}
public void switchToNextSubtype() {
SwitchLanguageActionKt.switchToNextLanguage(mInputMethodService);
Subtypes.INSTANCE.switchToNextLanguage(mInputMethodService, 1);
}
// TODO: Instead of checking for alphabetic keyboard here, separate keycodes for

View File

@ -47,6 +47,7 @@ import org.futo.inputmethod.latin.uix.settings.useDataStoreValueBlocking
import org.futo.inputmethod.latin.uix.theme.Typography
import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils
import java.util.Locale
import kotlin.math.sign
fun Locale.stripExtensionsIfNeeded(): Locale {
val newLocale = if(Build.VERSION.SDK_INT >= 26) {
@ -251,6 +252,23 @@ object Subtypes {
return layouts
}
fun switchToNextLanguage(context: Context, direction: Int) {
if(direction == 0) return
val enabledSubtypes = context.getSettingBlocking(SubtypesSetting).toList()
val currentSubtype = context.getSettingBlocking(ActiveSubtype)
val index = enabledSubtypes.indexOf(currentSubtype)
val nextIndex = if(index == -1) {
0
} else {
(index + direction.sign).mod(enabledSubtypes.size)
}
if(enabledSubtypes.isEmpty()) return
context.setSettingBlocking(ActiveSubtype.key, enabledSubtypes[nextIndex])
}
}

View File

@ -683,6 +683,13 @@ public final class InputLogic {
return;
}
if(event.mKeyCode <= Constants.CODE_ALT_ACTION_MAX && event.mKeyCode >= Constants.CODE_ALT_ACTION_0) {
final int actionId = event.mKeyCode - Constants.CODE_ALT_ACTION_0;
handler.triggerActionAlt(actionId);
return;
}
switch (event.mKeyCode) {
case Constants.CODE_DELETE:
handleBackspaceEvent(event, inputTransaction, currentKeyboardScriptId);

View File

@ -61,6 +61,7 @@ interface KeyboardManagerForAction {
fun unsetInputConnection()
fun requestDialog(text: String, options: List<DialogRequestItem>, onCancel: () -> Unit)
fun openInputMethodPicker()
}
interface ActionWindow {
@ -105,5 +106,6 @@ data class Action(
val windowImpl: ((KeyboardManagerForAction, PersistentActionState?) -> ActionWindow)?,
val simplePressImpl: ((KeyboardManagerForAction, PersistentActionState?) -> Unit)?,
val persistentState: ((KeyboardManagerForAction) -> PersistentActionState)? = null,
val persistentStateInitialization: PersistentStateInitialization = PersistentStateInitialization.OnActionTrigger
val persistentStateInitialization: PersistentStateInitialization = PersistentStateInitialization.OnActionTrigger,
val altPressImpl: ((KeyboardManagerForAction, PersistentActionState?) -> Unit)? = null,
)

View File

@ -243,6 +243,10 @@ class UixActionKeyboardManager(val uixManager: UixManager, val latinIME: LatinIM
uixManager.activeDialogRequestDismissed.value = false
}
override fun openInputMethodPicker() {
uixManager.showLanguageSwitcher()
}
override fun announce(s: String) {
AccessibilityUtils.init(getContext())
if(AccessibilityUtils.getInstance().isAccessibilityEnabled) {
@ -666,14 +670,20 @@ class UixManager(private val latinIME: LatinIME) {
}
}
fun triggerAction(id: Int) {
fun triggerAction(id: Int, alt: Boolean) {
val action = AllActions.getOrNull(id) ?: throw IllegalArgumentException("No such action with ID $id")
if(currWindowAction != null && action.windowImpl != null) {
closeActionWindow()
}
if(alt) {
if(action.altPressImpl != null) {
action.altPressImpl.invoke(keyboardManagerForAction, persistentStates[action])
}
} else {
if (currWindowAction != null && action.windowImpl != null) {
closeActionWindow()
}
onActionActivated(action)
onActionActivated(action)
}
}
fun requestForgetWord(suggestedWordInfo: SuggestedWords.SuggestedWordInfo) {

View File

@ -1,32 +1,18 @@
package org.futo.inputmethod.latin.uix.actions
import android.content.Context
import org.futo.inputmethod.latin.ActiveSubtype
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.SubtypesSetting
import org.futo.inputmethod.latin.Subtypes
import org.futo.inputmethod.latin.uix.Action
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.setSettingBlocking
fun switchToNextLanguage(context: Context) {
val enabledSubtypes = context.getSettingBlocking(SubtypesSetting).toList()
val currentSubtype = context.getSettingBlocking(ActiveSubtype)
val index = enabledSubtypes.indexOf(currentSubtype)
val nextIndex = if(index == -1) {
0
} else {
(index + 1) % enabledSubtypes.size
}
context.setSettingBlocking(ActiveSubtype.key, enabledSubtypes[nextIndex])
}
val SwitchLanguageAction = Action(
icon = R.drawable.globe,
name = R.string.show_language_switch_key,
simplePressImpl = { manager, _ ->
switchToNextLanguage(manager.getContext())
Subtypes.switchToNextLanguage(manager.getContext(), 1)
},
altPressImpl = { manager, _ ->
manager.openInputMethodPicker()
},
windowImpl = null,
)