Add support for delete swipe and spacebar navigation.

This is taken from simplekeyboard; the spacebar logic is the old one, to avoid a bug in some applications when moving the cursor at the beginning of the text.
This commit is contained in:
dslul 2020-01-18 16:26:22 +01:00
parent 3263ae00cc
commit ec4c6ea2b9
10 changed files with 130 additions and 13 deletions

View file

@ -19,11 +19,9 @@
package="org.dslul.openboard.inputmethod.latin">
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
<!-- A signature-protected permission to ask AOSP Keyboard to close the software keyboard.

View file

@ -100,6 +100,9 @@ public interface KeyboardActionListener {
* @return true if the request has been consumed, false otherwise.
*/
public boolean onCustomRequest(int requestCode);
public void onMovePointer(int steps);
public void onMoveDeletePointer(int steps);
public void onUpWithDeletePointerActive();
public static final KeyboardActionListener EMPTY_LISTENER = new Adapter();
@ -128,5 +131,11 @@ public interface KeyboardActionListener {
public boolean onCustomRequest(int requestCode) {
return false;
}
@Override
public void onMovePointer(int steps) {}
@Override
public void onMoveDeletePointer(int steps) {}
@Override
public void onUpWithDeletePointerActive() {}
}
}

View file

@ -85,6 +85,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
// Parameters for pointer handling.
private static PointerTrackerParams sParams;
private static int sPointerStep = (int)(10.0 * Resources.getSystem().getDisplayMetrics().density);
private static GestureStrokeRecognitionParams sGestureStrokeRecognitionParams;
private static GestureStrokeDrawingParams sGestureStrokeDrawingParams;
private static boolean sNeedsPhantomSuddenMoveEventHack;
@ -127,6 +128,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
// Last pointer position.
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;
@ -696,6 +701,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
startRepeatKey(key);
startLongPressTimer(key);
setPressedKeyGraphics(key, eventTime);
mStartX = x;
mStartY = y;
mStartTime = System.currentTimeMillis();
}
}
@ -894,10 +902,35 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
}
private void onMoveEventInternal(final int x, final int y, final long eventTime) {
final Key oldKey = mCurrentKey;
if (oldKey != null && oldKey.getCode() == Constants.CODE_SPACE && Settings.getInstance().getCurrent().mSpaceTrackpadEnabled) {
//Pointer slider
int steps = (x - mStartX) / sPointerStep;
final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout / MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
if (steps != 0 && mStartTime + longpressTimeout < System.currentTimeMillis()) {
mCursorMoved = true;
mStartX += steps * sPointerStep;
sListener.onMovePointer(steps);
}
return;
}
if (oldKey != null && oldKey.getCode() == Constants.CODE_DELETE && Settings.getInstance().getCurrent().mDeleteSwipeEnabled) {
//Delete slider
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);
final int lastX = mLastX;
final int lastY = mLastY;
final Key oldKey = mCurrentKey;
final Key newKey = onMoveKey(x, y);
if (sGestureEnabler.shouldHandleGesture()) {
// Register move event on gesture tracker.
@ -971,6 +1004,10 @@ 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 (isShowingMoreKeysPanel()) {
if (!mIsTrackingForActionDisabled) {
final int translatedX = mMoreKeysPanel.translateX(x);
@ -981,6 +1018,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
return;
}
if (mCursorMoved) {
mCursorMoved = false;
return;
}
if (sInGesture) {
if (currentKey != null) {
callListenerOnRelease(currentKey, currentKey.getCode(), true /* withSliding */);
@ -1023,6 +1065,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
if (isShowingMoreKeysPanel()) {
return;
}
if(mCursorMoved) {
return;
}
final Key key = getKey();
if (key == null) {
return;
@ -1148,6 +1193,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) {
// Cursor can be moved in space
return longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
}
return longpressTimeout;
}

View file

@ -40,6 +40,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;
@ -1357,6 +1358,38 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return false;
}
@Override
public void onMovePointer(int steps) {
if (steps < 0) {
int availableCharacters = getCurrentInputConnection().getTextBeforeCursor(64, 0).length();
steps = availableCharacters < -steps ? -availableCharacters : steps;
}
else if (steps > 0) {
int availableCharacters = getCurrentInputConnection().getTextAfterCursor(64, 0).length();
steps = availableCharacters < steps ? availableCharacters : steps;
} else return;
int newPosition = mInputLogic.mConnection.mExpectedSelStart + steps;
getCurrentInputConnection().setSelection(newPosition, newPosition);
}
@Override
public void onMoveDeletePointer(int steps) {
int end = mInputLogic.mConnection.getExpectedSelectionEnd();
int start = mInputLogic.mConnection.getExpectedSelectionStart() + steps;
if (start > end)
return;
mInputLogic.mConnection.setSelection(start, end);
}
@Override
public void onUpWithDeletePointerActive() {
if (mInputLogic.mConnection.hasSelection()) {
mInputLogic.sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
mInputLogic.finishInput();
}
}
private boolean isShowingOptionDialog() {
return mOptionsDialog != null && mOptionsDialog.isShowing();
}

View file

@ -103,12 +103,12 @@ public final class RichInputConnection implements PrivateCommandPerformer {
* It's not really the selection start position: the selection start may not be there yet, and
* in some cases, it may never arrive there.
*/
private int mExpectedSelStart = INVALID_CURSOR_POSITION; // in chars, not code points
public int mExpectedSelStart = INVALID_CURSOR_POSITION; // in chars, not code points
/**
* The expected selection end. Only differs from mExpectedSelStart if a non-empty selection is
* expected. The same caveats as mExpectedSelStart apply.
*/
private int mExpectedSelEnd = INVALID_CURSOR_POSITION; // in chars, not code points
public int mExpectedSelEnd = INVALID_CURSOR_POSITION; // in chars, not code points
/**
* This contains the committed text immediately preceding the cursor and the composing
* text, if any. It is refreshed when the cursor moves by calling upon the TextView.

View file

@ -1969,7 +1969,7 @@ 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 long eventTime = SystemClock.uptimeMillis();
mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,

View file

@ -83,6 +83,8 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
public static final String PREF_ENABLE_SPLIT_KEYBOARD = "pref_split_keyboard";
public static final String PREF_KEYBOARD_HEIGHT_SCALE = "pref_keyboard_height_scale";
public static final String PREF_SPACE_TRACKPAD = "pref_space_trackpad";
public static final String PREF_DELETE_SWIPE = "pref_delete_swipe";
// TODO: consolidate key preview dismiss delay with the key preview animation parameters.
public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
"pref_key_preview_popup_dismiss_delay";
@ -354,6 +356,14 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return (percentage != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? percentage : defaultValue;
}
public static boolean readSpaceTrackpadEnabled(final SharedPreferences prefs) {
return prefs.getBoolean(PREF_SPACE_TRACKPAD, true);
}
public static boolean readDeleteSwipeEnabled(final SharedPreferences prefs) {
return prefs.getBoolean(PREF_DELETE_SWIPE, true);
}
public static boolean readUseFullscreenMode(final Resources res) {
return res.getBoolean(R.bool.config_use_fullscreen_mode);
}

View file

@ -73,6 +73,8 @@ public class SettingsValues {
public final boolean mUsePersonalizedDicts;
public final boolean mUseDoubleSpacePeriod;
public final boolean mBlockPotentiallyOffensive;
public final boolean mSpaceTrackpadEnabled;
public final boolean mDeleteSwipeEnabled;
// Use bigrams to predict the next word when there is no input for it yet
public final boolean mBigramPredictionEnabled;
public final boolean mGestureInputEnabled;
@ -223,6 +225,8 @@ public class SettingsValues {
new TargetPackageInfoGetterTask(context, mAppWorkarounds)
.execute(mInputAttributes.mTargetApplicationPackageName);
}
mSpaceTrackpadEnabled = Settings.readSpaceTrackpadEnabled(prefs);
mDeleteSwipeEnabled = Settings.readDeleteSwipeEnabled(prefs);
}
public boolean isMetricsLoggingEnabled() {

View file

@ -597,4 +597,8 @@ Tip: You can download and remove dictionaries by going to &lt;b>Languages&#160;&
This resource is copied from packages/apps/Settings/res/values/strings.xml -->
<!-- This resource is corresponding to msgid="5433275485499039199" -->
<string name="user_dict_fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
<string name="delete_swipe">Delete swipe</string>
<string name="space_trackpad">Space bar trackpad</string>
<string name="delete_swipe_summary">Perform a swipe from the delete key to select and remove bigger portions of text at once</string>
<string name="space_trackpad_summary">Swipe on the spacebar to move the cursor</string>
</resources>

View file

@ -48,6 +48,16 @@
android:title="@string/show_setup_wizard_icon"
android:summary="@string/show_setup_wizard_icon_summary"
android:persistent="true" />
<CheckBoxPreference
android:key="pref_space_trackpad"
android:title="@string/space_trackpad"
android:summary="@string/space_trackpad_summary"
android:defaultValue="true" />
<CheckBoxPreference
android:key="pref_delete_swipe"
android:title="@string/delete_swipe"
android:summary="@string/delete_swipe_summary"
android:defaultValue="true" />
<PreferenceScreen
android:fragment="org.dslul.openboard.inputmethod.latin.settings.DebugSettingsFragment"
android:key="screen_debug"