From a8fbbe2350cef12c07e209a897c94b487b5bce4c Mon Sep 17 00:00:00 2001 From: David Vacca Date: Fri, 20 Dec 2019 17:25:34 -0800 Subject: [PATCH] Fix rendering of TextInput in Android 4 Summary: This diff fixes the rendering of TextInput component for Android 4 devices. This bug was caused by D18196901, when we changed the base class of ReactEditText from EditText to AppCompatEditText. The root of the problem is that AppCompatEditText wraps the ReactContext received as a parameter in the construction of the View into a ContextWrapper object. This break the implicity assumption that the method View.getContext will return the same context that was used during the construction of the view. https://android.googlesource.com/platform/frameworks/support/+/dd55716/v7/appcompat/src/android/support/v7/widget/AppCompatEditText.java#55 Changelog: [internal] Reviewed By: ejanzer Differential Revision: D19204032 fbshipit-source-id: eefb562b1da22e6cc58c75845c87dd032d727f49 --- .../react/uimanager/UIManagerHelper.java | 22 ++++++++++++++++++- .../react/views/textinput/ReactEditText.java | 8 ++++--- .../textinput/ReactTextInputManager.java | 9 +++++--- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java index 8384d767007be8..295f45c19ce57d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java @@ -9,8 +9,12 @@ import static com.facebook.react.uimanager.common.UIManagerType.FABRIC; import static com.facebook.react.uimanager.common.ViewUtil.getUIManagerType; -import com.facebook.react.bridge.CatalystInstance; + +import android.content.Context; +import android.content.ContextWrapper; +import android.view.View; import androidx.annotation.Nullable; +import com.facebook.react.bridge.CatalystInstance; import com.facebook.react.bridge.JSIModuleType; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactSoftException; @@ -65,4 +69,20 @@ public static EventDispatcher getEventDispatcher( UIManager uiManager = getUIManager(context, uiManagerType); return uiManager == null ? null : (EventDispatcher) uiManager.getEventDispatcher(); } + + /** + * @return The {@link ReactContext} associated to the {@link View} received as a parameter. + *

We can't rely that the method View.getContext() will return the same context that was + * passed as a parameter during the construction of the View. + *

For example the AppCompatEditText class wraps the context received as a parameter in the + * constructor of the View into a TintContextWrapper object. See: + * https://android.googlesource.com/platform/frameworks/support/+/dd55716/v7/appcompat/src/android/support/v7/widget/AppCompatEditText.java#55 + */ + public static ReactContext getReactContext(View view) { + Context context = view.getContext(); + if (!(context instanceof ReactContext) && context instanceof ContextWrapper) { + context = ((ContextWrapper) context).getBaseContext(); + } + return (ReactContext) context; + } } 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 81b8cb87da9457..67b4c1aa702330 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 @@ -7,6 +7,8 @@ package com.facebook.react.views.textinput; +import static com.facebook.react.uimanager.UIManagerHelper.getReactContext; + import android.content.Context; import android.graphics.Rect; import android.graphics.Typeface; @@ -114,7 +116,7 @@ public ReactEditText(Context context) { mReactBackgroundManager = new ReactViewBackgroundManager(this); mInputMethodManager = (InputMethodManager) - Assertions.assertNotNull(getContext().getSystemService(Context.INPUT_METHOD_SERVICE)); + Assertions.assertNotNull(context.getSystemService(Context.INPUT_METHOD_SERVICE)); mDefaultGravityHorizontal = getGravity() & (Gravity.HORIZONTAL_GRAVITY_MASK | Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK); mDefaultGravityVertical = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; @@ -219,7 +221,7 @@ protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) { @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - ReactContext reactContext = (ReactContext) getContext(); + ReactContext reactContext = getReactContext(this); InputConnection inputConnection = super.onCreateInputConnection(outAttrs); if (inputConnection != null && mOnKeyPress) { inputConnection = @@ -601,7 +603,7 @@ private void setIntrinsicContentSize() { // Since the LocalData object is constructed by getting values from the underlying EditText // view, we don't need to construct one or apply it at all - it provides no use in Fabric. if (mStateWrapper == null) { - ReactContext reactContext = (ReactContext) getContext(); + ReactContext reactContext = getReactContext(this); final ReactTextInputLocalData localData = new ReactTextInputLocalData(this); UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); uiManager.setViewLocalData(getId(), localData); 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 d614989a3e7036..ba055c292aa92b 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 @@ -7,6 +7,8 @@ package com.facebook.react.views.textinput; +import static com.facebook.react.uimanager.UIManagerHelper.getReactContext; + import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Build; @@ -1056,7 +1058,7 @@ private class ReactContentSizeWatcher implements ContentSizeWatcher { public ReactContentSizeWatcher(ReactEditText editText) { mEditText = editText; - ReactContext reactContext = (ReactContext) editText.getContext(); + ReactContext reactContext = getReactContext(editText); mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); } @@ -1099,7 +1101,8 @@ private class ReactSelectionWatcher implements SelectionWatcher { public ReactSelectionWatcher(ReactEditText editText) { mReactEditText = editText; - ReactContext reactContext = (ReactContext) editText.getContext(); + + ReactContext reactContext = getReactContext(editText); mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); } @@ -1133,7 +1136,7 @@ private class ReactScrollWatcher implements ScrollWatcher { public ReactScrollWatcher(ReactEditText editText) { mReactEditText = editText; - ReactContext reactContext = (ReactContext) editText.getContext(); + ReactContext reactContext = getReactContext(editText); mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); }