Skip to content

Commit f32f142

Browse files
committed
Issue#11068 PR#38649 [Android] Feature Flag the keeping of Composing Spans
1 parent 53617d2 commit f32f142

File tree

2 files changed

+24
-11
lines changed

2 files changed

+24
-11
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,7 @@ public class ReactFeatureFlags {
160160

161161
/** Clean yoga node when <Text /> does not change. */
162162
public static boolean enableCleanParagraphYogaNode = false;
163+
164+
/** Enable keeping Composing Spans on Text input change if the new text has the same length. */
165+
public static boolean enableComposingSpanRestorationOnSameLength = false;
163166
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import static com.facebook.react.uimanager.UIManagerHelper.getReactContext;
1111
import static com.facebook.react.views.text.TextAttributeProps.UNSET;
12+
import static com.facebook.react.config.ReactFeatureFlags.enableComposingSpanRestorationOnSameLength;
1213

1314
import android.content.ClipData;
1415
import android.content.ClipboardManager;
@@ -50,6 +51,7 @@
5051
import com.facebook.react.bridge.ReactContext;
5152
import com.facebook.react.bridge.ReactSoftExceptionLogger;
5253
import com.facebook.react.common.build.ReactBuildConfig;
54+
import com.facebook.react.config.ReactFeatureFlags;
5355
import com.facebook.react.uimanager.FabricViewStateManager;
5456
import com.facebook.react.uimanager.ReactAccessibilityDelegate;
5557
import com.facebook.react.uimanager.UIManagerModule;
@@ -721,20 +723,20 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) {
721723
* as long as the text they cover is the same unless they are {@link Spanned#SPAN_COMPOSING}.
722724
* All other spans will remain the same, since they will adapt to the new text, hence why {@link SpannableStringBuilder#replace} never removes
723725
* them.
724-
* Keep copy of {@link Spanned#SPAN_COMPOSING} Spans in {@param spannableStringBuilder}, because they are important for
726+
* When {@link ReactFeatureFlags#enableComposingSpanRestorationOnSameLength} is enabled,
727+
* keep copy of {@link Spanned#SPAN_COMPOSING} Spans in {@param spannableStringBuilder}, because they are important for
725728
* keyboard suggestions. Without keeping these Spans, suggestions default to be put after the current selection position,
726729
* possibly resulting in letter duplication (ex. Samsung Keyboard).
727730
*/
728731
private void manageSpans(SpannableStringBuilder spannableStringBuilder) {
729732
Object[] spans = getText().getSpans(0, length(), Object.class);
730-
boolean shouldKeepComposingSpans = length() == spannableStringBuilder.length();
733+
boolean shouldKeepComposingSpans = enableComposingSpanRestorationOnSameLength
734+
&& length() == spannableStringBuilder.length();
731735
for (int spanIdx = 0; spanIdx < spans.length; spanIdx++) {
732736
Object span = spans[spanIdx];
733737
int spanFlags = getText().getSpanFlags(span);
734738
boolean isExclusiveExclusive =
735739
(spanFlags & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
736-
boolean isComposing =
737-
(spanFlags & Spanned.SPAN_COMPOSING) == Spanned.SPAN_COMPOSING;
738740

739741
// Remove all styling spans we might have previously set
740742
if (span instanceof ReactSpan) {
@@ -749,10 +751,13 @@ private void manageSpans(SpannableStringBuilder spannableStringBuilder) {
749751
final int spanStart = getText().getSpanStart(span);
750752
final int spanEnd = getText().getSpanEnd(span);
751753

752-
// We keep a copy of Composing spans
753-
if (shouldKeepComposingSpans && isComposing) {
754-
spannableStringBuilder.setSpan(span, spanStart, spanEnd, spanFlags);
755-
continue;
754+
if (shouldKeepComposingSpans) {
755+
// We keep a copy of Composing spans
756+
boolean isComposing = (spanFlags & Spanned.SPAN_COMPOSING) == Spanned.SPAN_COMPOSING;
757+
if (isComposing) {
758+
spannableStringBuilder.setSpan(span, spanStart, spanEnd, spanFlags);
759+
continue;
760+
}
756761
}
757762

758763
// Make sure the span is removed from existing text, otherwise the spans we set will be
@@ -883,13 +888,18 @@ private void addSpansFromStyleAttributes(SpannableStringBuilder workingText) {
883888
}
884889

885890
/**
886-
* Attaches the {@link Spanned#SPAN_COMPOSING} from {@param spannableStringBuilder} to {@link ReactEditText#getText}
887-
* if they are the same length.
891+
* When {@link ReactFeatureFlags#enableComposingSpanRestorationOnSameLength} is enabled, this
892+
* function attaches the {@link Spanned#SPAN_COMPOSING} from {@param spannableStringBuilder} to
893+
* {@link ReactEditText#getText} if they are the same length.
888894
*
889895
* See {@link ReactEditText#manageSpans} for more details.
890-
* Also https://github.com/facebook/react-native/issues/11068
896+
* Also this <a href="https://github.com/facebook/react-native/issues/11068">GitHub issue</a>
891897
*/
892898
private void attachCompositeSpansToTextFrom(SpannableStringBuilder spannableStringBuilder) {
899+
if (!enableComposingSpanRestorationOnSameLength) {
900+
return;
901+
}
902+
893903
Editable text = getText();
894904
if (text == null || text.length() != spannableStringBuilder.length()) {
895905
return;

0 commit comments

Comments
 (0)