Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement onKeyPress Android #14720

Prev Previous commit
Next Next commit
Revise implementation methodology
  • Loading branch information
Josh Hargreaves committed Oct 5, 2017
commit 7ad03a242829655e268dfba1449d9170dd2a7fc9
Original file line number Diff line number Diff line change
@@ -112,12 +112,6 @@ public ReactEditText(Context context, InputConnectionWrapper inputConnection) {
mInputConnectionWrapper = inputConnection;
}

@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
mInputConnectionWrapper.setTarget(super.onCreateInputConnection(outAttrs));
return mInputConnectionWrapper;
}

// After the text changes inside an EditText, TextView checks if a layout() has been requested.
// If it has, it will not scroll the text to the end of the new text inserted, but wait for the
// next layout() to be called. However, we do not perform a layout() after a requestLayout(), so
@@ -190,12 +184,13 @@ protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {

@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnection connection = super.onCreateInputConnection(outAttrs);
mInputConnectionWrapper.setTarget(super.onCreateInputConnection(outAttrs));

if (isMultiline() && getBlurOnSubmit()) {
// Remove IME_FLAG_NO_ENTER_ACTION to keep the original IME_OPTION
outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
}
return connection;
return mInputConnectionWrapper;
}

@Override
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@

import javax.annotation.Nullable;

import android.view.KeyEvent;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
@@ -65,17 +67,15 @@
* input, and then set the composing text to be the character the user entered. In this case we
* choose our onKeyPress to be the new composing character.
*/
class ReactTextInputInputConnection extends InputConnectionWrapper {
class ReactTextInputInputConnection extends InputConnectionWrapper {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to have some high level comment here on what this class is for.

Also I think you didn't want to name this class this way ("InputInput"). As you need to change the name anyways I suggest that the class name include the word "Wrapper" (e.g. "ReactEditTextInputConnectionWrapper")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a more sensible name, thank you :P.

public static final String NewLineRawValue = "\n";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public static final fields should be all caps with underscores: NEW_LINE_RAW_VALUE etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

public static final String BackspaceKeyValue = "Backspace";
public static final String EnterKeyValue = "Enter";

private @Nullable ReactEditText mEditText;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use @Nullable annotation you should make sure to do null checks before calling methods on that object or use assertNoNull. Although as per my other comment about removing setEditText I think if you follow that comment you won't need @Nullable here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, with your suggested changes, @Nullable no longer necessary!

private EventDispatcher mEventDispatcher;
private int mPreviousSelectionStart;
private int mPreviousSelectionEnd;
private String mCommittedText;
private String mComposedText;
private boolean mIsBatchEdit;
private String mKey = null;

public ReactTextInputInputConnection(
InputConnection target,
@@ -89,67 +89,81 @@ public void setEditText(final ReactEditText editText) {
mEditText = editText;
}

@Override
public boolean beginBatchEdit() {
mIsBatchEdit = true;
return super.beginBatchEdit();
}

@Override
public boolean endBatchEdit() {
mIsBatchEdit = false;
if(mKey != null) {
dispatchKeyEvent(mKey);
mKey = null;
}
return super.endBatchEdit();
}

@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
mComposedText = text.toString();
return super.setComposingText(text, newCursorPosition);
final int previousSelectionStart = mEditText.getSelectionStart();
final int previousSelectionEnd = mEditText.getSelectionEnd();
String key;
final boolean consumed = super.setComposingText(text, newCursorPosition);
final boolean noPreviousSelection = previousSelectionStart == previousSelectionEnd;
if ((noPreviousSelection && mEditText.getSelectionStart() < previousSelectionStart)
|| !noPreviousSelection && mEditText.getSelectionStart() == previousSelectionStart) {
key = BackspaceKeyValue;
} else {
key = String.valueOf(mEditText.getText().charAt(mEditText.getSelectionStart() - 1));
}
queueKeyEventIfBatchEdit(key);
return consumed;
}

@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
mCommittedText = text.toString();
String key = text.toString();
if (key.equals("")) {
key = BackspaceKeyValue;
}
queueKeyEventIfBatchEdit(key);

return super.commitText(text, newCursorPosition);
}

@Override
public boolean beginBatchEdit() {
mPreviousSelectionStart = mEditText.getSelectionStart();
mPreviousSelectionEnd = mEditText.getSelectionEnd();
mCommittedText = null;
mComposedText = null;
return super.beginBatchEdit();
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
dispatchKeyEvent(BackspaceKeyValue);
return super.deleteSurroundingText(beforeLength, afterLength);
}

// Called by SwiftKey when cursor at beginning of input when there is a delete.
// Whereas stock Android Keyboard calls {@link InputConnection#deleteSurroundingText}
@Override
public boolean endBatchEdit() {
String key;
int selectionStart = mEditText.getSelectionStart();
if (mCommittedText == null) {
if ((noPreviousSelection() && selectionStart < mPreviousSelectionStart)
|| (!noPreviousSelection() && selectionStart == mPreviousSelectionStart)
|| (mPreviousSelectionStart == 0 && selectionStart == 0)) {
key = BackspaceKeyValue;
} else {
char enteredChar = mEditText.getText().charAt(selectionStart - 1);
key = String.valueOf(enteredChar);
}
public boolean sendKeyEvent(KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
dispatchKeyEvent(BackspaceKeyValue);
}
else {
key = mComposedText != null ? mComposedText : mCommittedText;
}
key = keyValueFromString(key);
mEventDispatcher.dispatchEvent(
new ReactTextInputKeyPressEvent(
mEditText.getId(),
key));
return super.endBatchEdit();
return super.sendKeyEvent(event);
}

private boolean noPreviousSelection() {
return mPreviousSelectionStart == mPreviousSelectionEnd;
private void queueKeyEventIfBatchEdit(String key) {
if(mIsBatchEdit) {
mKey = key;
} else {
dispatchKeyEvent(key);
}
}

private String keyValueFromString(final String key) {
String returnValue;
switch (key) {
case "": returnValue = BackspaceKeyValue;
break;
case NewLineRawValue: returnValue = EnterKeyValue;
break;
default:
returnValue = key;
break;
private void dispatchKeyEvent(String key) {
if (key.equals(NewLineRawValue)) {
key = EnterKeyValue;
}
return returnValue;
mEventDispatcher.dispatchEvent(
new ReactTextInputKeyPressEvent(
mEditText.getId(),
key));
}
}