Skip to content

Commit

Permalink
Issue#11068 PR#38649 [Android] Feature Flag the keeping of Composing …
Browse files Browse the repository at this point in the history
…Spans
  • Loading branch information
fknives committed Aug 4, 2023
1 parent 53617d2 commit f32f142
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,7 @@ public class ReactFeatureFlags {

/** Clean yoga node when <Text /> does not change. */
public static boolean enableCleanParagraphYogaNode = false;

/** Enable keeping Composing Spans on Text input change if the new text has the same length. */
public static boolean enableComposingSpanRestorationOnSameLength = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import static com.facebook.react.uimanager.UIManagerHelper.getReactContext;
import static com.facebook.react.views.text.TextAttributeProps.UNSET;
import static com.facebook.react.config.ReactFeatureFlags.enableComposingSpanRestorationOnSameLength;

import android.content.ClipData;
import android.content.ClipboardManager;
Expand Down Expand Up @@ -50,6 +51,7 @@
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactSoftExceptionLogger;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.uimanager.FabricViewStateManager;
import com.facebook.react.uimanager.ReactAccessibilityDelegate;
import com.facebook.react.uimanager.UIManagerModule;
Expand Down Expand Up @@ -721,20 +723,20 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) {
* as long as the text they cover is the same unless they are {@link Spanned#SPAN_COMPOSING}.
* All other spans will remain the same, since they will adapt to the new text, hence why {@link SpannableStringBuilder#replace} never removes
* them.
* Keep copy of {@link Spanned#SPAN_COMPOSING} Spans in {@param spannableStringBuilder}, because they are important for
* When {@link ReactFeatureFlags#enableComposingSpanRestorationOnSameLength} is enabled,
* keep copy of {@link Spanned#SPAN_COMPOSING} Spans in {@param spannableStringBuilder}, because they are important for
* keyboard suggestions. Without keeping these Spans, suggestions default to be put after the current selection position,
* possibly resulting in letter duplication (ex. Samsung Keyboard).
*/
private void manageSpans(SpannableStringBuilder spannableStringBuilder) {
Object[] spans = getText().getSpans(0, length(), Object.class);
boolean shouldKeepComposingSpans = length() == spannableStringBuilder.length();
boolean shouldKeepComposingSpans = enableComposingSpanRestorationOnSameLength
&& length() == spannableStringBuilder.length();
for (int spanIdx = 0; spanIdx < spans.length; spanIdx++) {
Object span = spans[spanIdx];
int spanFlags = getText().getSpanFlags(span);
boolean isExclusiveExclusive =
(spanFlags & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
boolean isComposing =
(spanFlags & Spanned.SPAN_COMPOSING) == Spanned.SPAN_COMPOSING;

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

// We keep a copy of Composing spans
if (shouldKeepComposingSpans && isComposing) {
spannableStringBuilder.setSpan(span, spanStart, spanEnd, spanFlags);
continue;
if (shouldKeepComposingSpans) {
// We keep a copy of Composing spans
boolean isComposing = (spanFlags & Spanned.SPAN_COMPOSING) == Spanned.SPAN_COMPOSING;
if (isComposing) {
spannableStringBuilder.setSpan(span, spanStart, spanEnd, spanFlags);
continue;
}
}

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

/**
* Attaches the {@link Spanned#SPAN_COMPOSING} from {@param spannableStringBuilder} to {@link ReactEditText#getText}
* if they are the same length.
* When {@link ReactFeatureFlags#enableComposingSpanRestorationOnSameLength} is enabled, this
* function attaches the {@link Spanned#SPAN_COMPOSING} from {@param spannableStringBuilder} to
* {@link ReactEditText#getText} if they are the same length.
*
* See {@link ReactEditText#manageSpans} for more details.
* Also https://github.com/facebook/react-native/issues/11068
* Also this <a href="https://github.com/facebook/react-native/issues/11068">GitHub issue</a>
*/
private void attachCompositeSpansToTextFrom(SpannableStringBuilder spannableStringBuilder) {
if (!enableComposingSpanRestorationOnSameLength) {
return;
}

Editable text = getText();
if (text == null || text.length() != spannableStringBuilder.length()) {
return;
Expand Down

0 comments on commit f32f142

Please sign in to comment.