From fa4a5afe35af4e93059b089c3107af916a5b21ab Mon Sep 17 00:00:00 2001 From: Dave Miller Date: Tue, 2 Feb 2016 00:01:42 -0800 Subject: [PATCH] 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 --- .../react/views/textinput/ReactEditText.java | 66 +++++++++++++++++++ .../textinput/ReactTextInputManager.java | 39 +++++++++-- 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index de7178d76a..52a18a40ce 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -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); + } + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index ed0d84935f..a9b071c72b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -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 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")