Skip to content

Commit

Permalink
Fix Keyboard handling to allow all characters on Soft Input Panel and…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
Dave Miller authored and facebook-github-bot-5 committed Feb 2, 2016
1 parent 80e1ca8 commit fa4a5af
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@
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;

Expand Down Expand Up @@ -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);
Expand All @@ -81,6 +87,7 @@ public ReactEditText(Context context) {
mListeners = null;
mTextWatcherDelegator = null;
mStagedInputType = getInputType();
mKeyListener = new InternalKeyListener();
}

// After the text changes inside an EditText, TextView checks if a layout() has been requested.
Expand Down Expand Up @@ -190,6 +197,12 @@ public void setSelectionWatcher(SelectionWatcher selectionWatcher) {
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}.
Expand Down Expand Up @@ -419,4 +432,57 @@ public void afterTextChanged(Editable s) {
}
}
}

/*
* 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import javax.annotation.Nullable;

import java.util.LinkedList;
import java.util.Map;

import android.graphics.PorterDuff;
Expand Down Expand Up @@ -246,13 +247,43 @@ public void setNumLines(ReactEditText view, int numLines) {

@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")
Expand Down

0 comments on commit fa4a5af

Please sign in to comment.