Fix Keyboard handling to allow all characters on Soft Input Panel and hardware keyboard

Summary:
public
This diff fixes two issues:
1) Makes it so that when a keyboard is displayed, all keys in that keyboard actually can be set as text.  Previously you could display a Numeric keyboard and it would only allow entering numbers despite the keyboard having other keys like comma, plus, space, etc.
 a) This also allows any key entered on a physical keyboard to go through to the view even if not present on the Soft Input keyboard
2) Makes more robust our Filter setting in setMaxLength so that we only affect the InputFilter.LengthFilter if present instead of all.

This works by creating a new KeyListener which will respond to getInputType as the KeyListener it is replacing (like a DigitsKeyListener for a numeric keyboard) but allow all characters when actually entering text.

Reviewed By: andreicoman11

Differential Revision: D2880851

fb-gh-sync-id: fa5eb549a849d8f30c592d7eac48054ca6a75544
This commit is contained in:
Dave Miller 2016-02-02 00:01:42 -08:00 committed by facebook-github-bot-5
parent 80e1ca870b
commit fa4a5afe35
2 changed files with 101 additions and 4 deletions

View File

@ -21,11 +21,14 @@ import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.method.KeyListener;
import android.text.method.QwertyKeyListener;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
@ -65,6 +68,9 @@ public class ReactEditText extends EditText {
private int mStagedInputType;
private boolean mContainsImages;
private @Nullable SelectionWatcher mSelectionWatcher;
private final InternalKeyListener mKeyListener;
private static KeyListener sKeyListener = QwertyKeyListener.getInstanceForFullKeyboard();
public ReactEditText(Context context) {
super(context);
@ -81,6 +87,7 @@ public class ReactEditText extends EditText {
mListeners = null;
mTextWatcherDelegator = null;
mStagedInputType = getInputType();
mKeyListener = new InternalKeyListener();
}
// After the text changes inside an EditText, TextView checks if a layout() has been requested.
@ -190,6 +197,12 @@ public class ReactEditText extends EditText {
public void setInputType(int type) {
super.setInputType(type);
mStagedInputType = type;
// We override the KeyListener so that all keys on the soft input keyboard as well as hardware
// keyboards work. Some KeyListeners like DigitsKeyListener will display the keyboard but not
// accept all input from it
mKeyListener.setInputType(type);
setKeyListener(mKeyListener);
}
// VisibleForTesting from {@link TextInputEventsTestCase}.
@ -419,4 +432,57 @@ public class ReactEditText extends EditText {
}
}
}
/*
* This class is set as the KeyListener for the underlying TextView
* It does two things
* 1) Provides the same answer to getInputType() as the real KeyListener would have which allows
* the proper keyboard to pop up on screen
* 2) Permits all keyboard input through
*/
private static class InternalKeyListener implements KeyListener {
private int mInputType = 0;
public InternalKeyListener() {
}
public void setInputType(int inputType) {
mInputType = inputType;
}
/*
* getInputType will return whatever value is passed in. This will allow the proper keyboard
* to be shown on screen but without the actual filtering done by other KeyListeners
*/
@Override
public int getInputType() {
return mInputType;
}
/*
* All overrides of key handling defer to the underlying KeyListener which is shared by all
* ReactEditText instances. It will basically allow any/all keyboard input whether from
* physical keyboard or from soft input.
*/
@Override
public boolean onKeyDown(View view, Editable text, int keyCode, KeyEvent event) {
return sKeyListener.onKeyDown(view, text, keyCode, event);
}
@Override
public boolean onKeyUp(View view, Editable text, int keyCode, KeyEvent event) {
return sKeyListener.onKeyUp(view, text, keyCode, event);
}
@Override
public boolean onKeyOther(View view, Editable text, KeyEvent event) {
return sKeyListener.onKeyOther(view, text, event);
}
@Override
public void clearMetaKeyState(View view, Editable content, int states) {
sKeyListener.clearMetaKeyState(view, content, states);
}
}
}

View File

@ -11,6 +11,7 @@ package com.facebook.react.views.textinput;
import javax.annotation.Nullable;
import java.util.LinkedList;
import java.util.Map;
import android.graphics.PorterDuff;
@ -246,13 +247,43 @@ public class ReactTextInputManager extends
@ReactProp(name = "maxLength")
public void setMaxLength(ReactEditText view, @Nullable Integer maxLength) {
InputFilter [] currentFilters = view.getFilters();
InputFilter[] newFilters = EMPTY_FILTERS;
if (maxLength == null) {
view.setFilters(EMPTY_FILTERS);
if (currentFilters.length > 0) {
LinkedList<InputFilter> list = new LinkedList<>();
for (int i = 0; i < currentFilters.length; i++) {
if (!(currentFilters[i] instanceof InputFilter.LengthFilter)) {
list.add(currentFilters[i]);
}
}
if (list.size() > 0) {
newFilters = (InputFilter[])list.toArray();
}
}
} else {
InputFilter[] filterArray = new InputFilter[1];
filterArray[0] = new InputFilter.LengthFilter(maxLength);
view.setFilters(filterArray);
if (currentFilters.length > 0) {
newFilters = currentFilters;
boolean replaced = false;
for (int i = 0; i < currentFilters.length; i++) {
if (currentFilters[i] instanceof InputFilter.LengthFilter) {
currentFilters[i] = new InputFilter.LengthFilter(maxLength);
replaced = true;
}
}
if (!replaced) {
newFilters = new InputFilter[currentFilters.length + 1];
System.arraycopy(currentFilters, 0, newFilters, 0, currentFilters.length);
currentFilters[currentFilters.length] = new InputFilter.LengthFilter(maxLength);
}
} else {
newFilters = new InputFilter[1];
newFilters[0] = new InputFilter.LengthFilter(maxLength);
}
}
view.setFilters(newFilters);
}
@ReactProp(name = "autoCorrect")