Android: Add disableExtractUI prop to TextInput on Android
Summary: On Android, if there is a small amount of space available around a text input (e.g. landscape orientation on a phone), Android may choose to have the user edit the text inside of a full screen text input mode. This behavior isn't always desirable. For example, if your app offers some UI controls for controlling the formatting of the text, you want the controls to be visible while the user is editing the text. This Android feature conflicts with that desired experience because the UI controls would be hidden while the text is being edited. The `disableExtractUI` prop enables developers to choose whether or not Android's full screen text input editing mode is enabled. When this prop is true, Android's `IME_FLAG_NO_EXTRACT_UI` flag is passed to the `setImeOptions` method. **Test plan (required)** Verified `disableExtractUI` works for both `true` and `false` values in a test app. My team is also using this change in our app. Adam Comella Microsoft Corp. Closes https://github.com/facebook/react-native/pull/10900 Differential Revision: D4226483 Pulled By: mkonicek fbshipit-source-id: 8f1055f6e612b05bafabe6f07a3705dd8788e3da
This commit is contained in:
parent
9fb520e3b2
commit
1b870d2019
|
@ -304,6 +304,15 @@ const TextInput = React.createClass({
|
||||||
* @platform android
|
* @platform android
|
||||||
*/
|
*/
|
||||||
numberOfLines: PropTypes.number,
|
numberOfLines: PropTypes.number,
|
||||||
|
/**
|
||||||
|
* When `false`, if there is a small amount of space available around a text input
|
||||||
|
* (e.g. landscape orientation on a phone), the OS may choose to have the user edit
|
||||||
|
* the text inside of a full screen text input mode. When `true`, this feature is
|
||||||
|
* disabled and users will always edit the text directly inside of the text input.
|
||||||
|
* Defaults to `false`.
|
||||||
|
* @platform android
|
||||||
|
*/
|
||||||
|
disableFullscreenUI: PropTypes.bool,
|
||||||
/**
|
/**
|
||||||
* If `true`, the keyboard disables the return key when there is no text and
|
* If `true`, the keyboard disables the return key when there is no text and
|
||||||
* automatically enables it when there is text. The default value is `false`.
|
* automatically enables it when there is text. The default value is `false`.
|
||||||
|
@ -708,6 +717,7 @@ const TextInput = React.createClass({
|
||||||
onTextInput={this._onTextInput}
|
onTextInput={this._onTextInput}
|
||||||
text={this._getText()}
|
text={this._getText()}
|
||||||
children={children}
|
children={children}
|
||||||
|
disableFullscreenUI={this.props.disableFullscreenUI}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -33,6 +33,7 @@ import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
@ -74,6 +75,8 @@ public class ReactEditText extends EditText {
|
||||||
private int mStagedInputType;
|
private int mStagedInputType;
|
||||||
private boolean mContainsImages;
|
private boolean mContainsImages;
|
||||||
private boolean mBlurOnSubmit;
|
private boolean mBlurOnSubmit;
|
||||||
|
private boolean mDisableFullscreen;
|
||||||
|
private @Nullable String mReturnKeyType;
|
||||||
private @Nullable SelectionWatcher mSelectionWatcher;
|
private @Nullable SelectionWatcher mSelectionWatcher;
|
||||||
private @Nullable ContentSizeWatcher mContentSizeWatcher;
|
private @Nullable ContentSizeWatcher mContentSizeWatcher;
|
||||||
private final InternalKeyListener mKeyListener;
|
private final InternalKeyListener mKeyListener;
|
||||||
|
@ -97,6 +100,7 @@ public class ReactEditText extends EditText {
|
||||||
mIsSettingTextFromJS = false;
|
mIsSettingTextFromJS = false;
|
||||||
mIsJSSettingFocus = false;
|
mIsJSSettingFocus = false;
|
||||||
mBlurOnSubmit = true;
|
mBlurOnSubmit = true;
|
||||||
|
mDisableFullscreen = false;
|
||||||
mListeners = null;
|
mListeners = null;
|
||||||
mTextWatcherDelegator = null;
|
mTextWatcherDelegator = null;
|
||||||
mStagedInputType = getInputType();
|
mStagedInputType = getInputType();
|
||||||
|
@ -254,6 +258,24 @@ public class ReactEditText extends EditText {
|
||||||
return mBlurOnSubmit;
|
return mBlurOnSubmit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDisableFullscreenUI(boolean disableFullscreenUI) {
|
||||||
|
mDisableFullscreen = disableFullscreenUI;
|
||||||
|
updateImeOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDisableFullscreenUI() {
|
||||||
|
return mDisableFullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReturnKeyType(String returnKeyType) {
|
||||||
|
mReturnKeyType = returnKeyType;
|
||||||
|
updateImeOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReturnKeyType() {
|
||||||
|
return mReturnKeyType;
|
||||||
|
}
|
||||||
|
|
||||||
/*protected*/ int getStagedInputType() {
|
/*protected*/ int getStagedInputType() {
|
||||||
return mStagedInputType;
|
return mStagedInputType;
|
||||||
}
|
}
|
||||||
|
@ -407,6 +429,42 @@ public class ReactEditText extends EditText {
|
||||||
setGravity((getGravity() & ~Gravity.VERTICAL_GRAVITY_MASK) | gravityVertical);
|
setGravity((getGravity() & ~Gravity.VERTICAL_GRAVITY_MASK) | gravityVertical);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateImeOptions() {
|
||||||
|
// Default to IME_ACTION_DONE
|
||||||
|
int returnKeyFlag = EditorInfo.IME_ACTION_DONE;
|
||||||
|
if (mReturnKeyType != null) {
|
||||||
|
switch (mReturnKeyType) {
|
||||||
|
case "go":
|
||||||
|
returnKeyFlag = EditorInfo.IME_ACTION_GO;
|
||||||
|
break;
|
||||||
|
case "next":
|
||||||
|
returnKeyFlag = EditorInfo.IME_ACTION_NEXT;
|
||||||
|
break;
|
||||||
|
case "none":
|
||||||
|
returnKeyFlag = EditorInfo.IME_ACTION_NONE;
|
||||||
|
break;
|
||||||
|
case "previous":
|
||||||
|
returnKeyFlag = EditorInfo.IME_ACTION_PREVIOUS;
|
||||||
|
break;
|
||||||
|
case "search":
|
||||||
|
returnKeyFlag = EditorInfo.IME_ACTION_SEARCH;
|
||||||
|
break;
|
||||||
|
case "send":
|
||||||
|
returnKeyFlag = EditorInfo.IME_ACTION_SEND;
|
||||||
|
break;
|
||||||
|
case "done":
|
||||||
|
returnKeyFlag = EditorInfo.IME_ACTION_DONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDisableFullscreen) {
|
||||||
|
setImeOptions(returnKeyFlag | EditorInfo.IME_FLAG_NO_FULLSCREEN);
|
||||||
|
} else {
|
||||||
|
setImeOptions(returnKeyFlag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean verifyDrawable(Drawable drawable) {
|
protected boolean verifyDrawable(Drawable drawable) {
|
||||||
if (mContainsImages && getText() instanceof Spanned) {
|
if (mContainsImages && getText() instanceof Spanned) {
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||||
ReactEditText editText = new ReactEditText(context);
|
ReactEditText editText = new ReactEditText(context);
|
||||||
int inputType = editText.getInputType();
|
int inputType = editText.getInputType();
|
||||||
editText.setInputType(inputType & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
|
editText.setInputType(inputType & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
|
||||||
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
editText.setReturnKeyType("done");
|
||||||
editText.setTextSize(
|
editText.setTextSize(
|
||||||
TypedValue.COMPLEX_UNIT_PX,
|
TypedValue.COMPLEX_UNIT_PX,
|
||||||
(int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)));
|
(int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)));
|
||||||
|
@ -475,29 +475,12 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||||
|
|
||||||
@ReactProp(name = "returnKeyType")
|
@ReactProp(name = "returnKeyType")
|
||||||
public void setReturnKeyType(ReactEditText view, String returnKeyType) {
|
public void setReturnKeyType(ReactEditText view, String returnKeyType) {
|
||||||
switch (returnKeyType) {
|
view.setReturnKeyType(returnKeyType);
|
||||||
case "done":
|
}
|
||||||
view.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
|
||||||
break;
|
@ReactProp(name = "disableFullscreenUI", defaultBoolean = false)
|
||||||
case "go":
|
public void setDisableFullscreenUI(ReactEditText view, boolean disableFullscreenUI) {
|
||||||
view.setImeOptions(EditorInfo.IME_ACTION_GO);
|
view.setDisableFullscreenUI(disableFullscreenUI);
|
||||||
break;
|
|
||||||
case "next":
|
|
||||||
view.setImeOptions(EditorInfo.IME_ACTION_NEXT);
|
|
||||||
break;
|
|
||||||
case "none":
|
|
||||||
view.setImeOptions(EditorInfo.IME_ACTION_NONE);
|
|
||||||
break;
|
|
||||||
case "previous":
|
|
||||||
view.setImeOptions(EditorInfo.IME_ACTION_PREVIOUS);
|
|
||||||
break;
|
|
||||||
case "search":
|
|
||||||
view.setImeOptions(EditorInfo.IME_ACTION_SEARCH);
|
|
||||||
break;
|
|
||||||
case "send":
|
|
||||||
view.setImeOptions(EditorInfo.IME_ACTION_SEND);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int IME_ACTION_ID = 0x670;
|
private static final int IME_ACTION_ID = 0x670;
|
||||||
|
|
Loading…
Reference in New Issue