Skip to content

Commit 485b8ef

Browse files
NickGerlemankelset
authored andcommitted
Minimize EditText Spans 2/9: Make stripAttributeEquivalentSpans generic (#36546)
Summary: Pull Request resolved: #36546 This is part of a series of changes to minimize the number of spans committed to EditText, as a mitigation for platform issues on Samsung devices. See this [GitHub thread]( #35936 (comment)) for greater context on the platform behavior. This change generalizes `stripAttributeEquivalentSpans()` to allow plugging in different spans. Changelog: [Internal] Reviewed By: rshest Differential Revision: D44240781 fbshipit-source-id: 89005266020f216368e9ad9ce382699bd8db85a8 # Conflicts: # ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
1 parent 11755c1 commit 485b8ef

File tree

1 file changed

+34
-20
lines changed

1 file changed

+34
-20
lines changed

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

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -549,9 +549,7 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) {
549549
new SpannableStringBuilder(reactTextUpdate.getText());
550550

551551
manageSpans(spannableStringBuilder, reactTextUpdate.mContainsMultipleFragments);
552-
553-
// Mitigation for https://github.com/facebook/react-native/issues/35936 (S318090)
554-
stripAtributeEquivalentSpans(spannableStringBuilder);
552+
stripStyleEquivalentSpans(spannableStringBuilder);
555553

556554
mContainsImages = reactTextUpdate.containsImages();
557555

@@ -626,28 +624,44 @@ private void manageSpans(
626624
}
627625
}
628626

629-
private void stripAtributeEquivalentSpans(SpannableStringBuilder sb) {
630-
// We have already set a font size on the EditText itself. We can safely remove sizing spans
631-
// which are the same as the set font size, and not otherwise overlapped.
632-
final int effectiveFontSize = mTextAttributes.getEffectiveFontSize();
633-
ReactAbsoluteSizeSpan[] spans = sb.getSpans(0, sb.length(), ReactAbsoluteSizeSpan.class);
627+
// TODO: Replace with Predicate<T> and lambdas once Java 8 builds in OSS
628+
interface SpanPredicate<T> {
629+
boolean test(T span);
630+
}
634631

635-
outerLoop:
636-
for (ReactAbsoluteSizeSpan span : spans) {
637-
ReactAbsoluteSizeSpan[] overlappingSpans =
638-
sb.getSpans(sb.getSpanStart(span), sb.getSpanEnd(span), ReactAbsoluteSizeSpan.class);
632+
/**
633+
* Remove spans from the SpannableStringBuilder which can be represented by TextAppearance
634+
* attributes on the underlying EditText. This works around instability on Samsung devices with
635+
* the presence of spans https://github.com/facebook/react-native/issues/35936 (S318090)
636+
*/
637+
private void stripStyleEquivalentSpans(SpannableStringBuilder sb) {
638+
stripSpansOfKind(
639+
sb,
640+
ReactAbsoluteSizeSpan.class,
641+
new SpanPredicate<ReactAbsoluteSizeSpan>() {
642+
@Override
643+
public boolean test(ReactAbsoluteSizeSpan span) {
644+
return span.getSize() == mTextAttributes.getEffectiveFontSize();
645+
}
646+
});
647+
}
639648

640-
for (ReactAbsoluteSizeSpan overlappingSpan : overlappingSpans) {
641-
if (span.getSize() != effectiveFontSize) {
642-
continue outerLoop;
643-
}
644-
}
649+
private <T> void stripSpansOfKind(
650+
SpannableStringBuilder sb, Class<T> clazz, SpanPredicate<T> shouldStrip) {
651+
T[] spans = sb.getSpans(0, sb.length(), clazz);
645652

646-
sb.removeSpan(span);
653+
for (T span : spans) {
654+
if (shouldStrip.test(span)) {
655+
sb.removeSpan(span);
656+
}
647657
}
648658
}
649659

650-
private void unstripAttributeEquivalentSpans(SpannableStringBuilder workingText) {
660+
/**
661+
* Copy back styles represented as attributes to the underlying span, for later measurement
662+
* outside the ReactEditText.
663+
*/
664+
private void restoreStyleEquivalentSpans(SpannableStringBuilder workingText) {
651665
int spanFlags = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
652666

653667
// Set all bits for SPAN_PRIORITY so that this span has the highest possible priority
@@ -1076,7 +1090,7 @@ private void updateCachedSpannable(boolean resetStyles) {
10761090
// - android.app.Activity.dispatchKeyEvent (Activity.java:3447)
10771091
try {
10781092
sb.append(currentText.subSequence(0, currentText.length()));
1079-
unstripAttributeEquivalentSpans(sb);
1093+
restoreStyleEquivalentSpans(sb);
10801094
} catch (IndexOutOfBoundsException e) {
10811095
ReactSoftExceptionLogger.logSoftException(TAG, e);
10821096
}

0 commit comments

Comments
 (0)