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
This commit is contained in:
David Vacca
2019-12-20 17:27:53 -08:00
committed by Facebook Github Bot
parent 282b8b04e1
commit a8fbbe2350
3 changed files with 32 additions and 7 deletions
@@ -9,8 +9,12 @@ package com.facebook.react.uimanager;
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 class UIManagerHelper {
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.
* <p>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.
* <p>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;
}
}
@@ -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 class ReactEditText extends AppCompatEditText {
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 @@ public class ReactEditText extends AppCompatEditText {
@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 @@ public class ReactEditText extends AppCompatEditText {
// 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);
@@ -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 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
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 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
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 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
public ReactScrollWatcher(ReactEditText editText) {
mReactEditText = editText;
ReactContext reactContext = (ReactContext) editText.getContext();
ReactContext reactContext = getReactContext(editText);
mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
}