Skip to content

Commit 7374892

Browse files
NickGerlemankelset
authored andcommittedApr 19, 2023
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 ee2d815 commit 7374892

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

+34-20
Original file line numberDiff line numberDiff line change
@@ -550,9 +550,7 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) {
550550
new SpannableStringBuilder(reactTextUpdate.getText());
551551

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

557555
mContainsImages = reactTextUpdate.containsImages();
558556

@@ -627,28 +625,44 @@ private void manageSpans(
627625
}
628626
}
629627

630-
private void stripAtributeEquivalentSpans(SpannableStringBuilder sb) {
631-
// We have already set a font size on the EditText itself. We can safely remove sizing spans
632-
// which are the same as the set font size, and not otherwise overlapped.
633-
final int effectiveFontSize = mTextAttributes.getEffectiveFontSize();
634-
ReactAbsoluteSizeSpan[] spans = sb.getSpans(0, sb.length(), ReactAbsoluteSizeSpan.class);
628+
// TODO: Replace with Predicate<T> and lambdas once Java 8 builds in OSS
629+
interface SpanPredicate<T> {
630+
boolean test(T span);
631+
}
632+
633+
/**
634+
* Remove spans from the SpannableStringBuilder which can be represented by TextAppearance
635+
* attributes on the underlying EditText. This works around instability on Samsung devices with
636+
* the presence of spans https://github.com/facebook/react-native/issues/35936 (S318090)
637+
*/
638+
private void stripStyleEquivalentSpans(SpannableStringBuilder sb) {
639+
stripSpansOfKind(
640+
sb,
641+
ReactAbsoluteSizeSpan.class,
642+
new SpanPredicate<ReactAbsoluteSizeSpan>() {
643+
@Override
644+
public boolean test(ReactAbsoluteSizeSpan span) {
645+
return span.getSize() == mTextAttributes.getEffectiveFontSize();
646+
}
647+
});
648+
}
635649

636-
outerLoop:
637-
for (ReactAbsoluteSizeSpan span : spans) {
638-
ReactAbsoluteSizeSpan[] overlappingSpans =
639-
sb.getSpans(sb.getSpanStart(span), sb.getSpanEnd(span), ReactAbsoluteSizeSpan.class);
650+
private <T> void stripSpansOfKind(
651+
SpannableStringBuilder sb, Class<T> clazz, SpanPredicate<T> shouldStrip) {
652+
T[] spans = sb.getSpans(0, sb.length(), clazz);
640653

641-
for (ReactAbsoluteSizeSpan overlappingSpan : overlappingSpans) {
642-
if (span.getSize() != effectiveFontSize) {
643-
continue outerLoop;
644-
}
654+
for (T span : spans) {
655+
if (shouldStrip.test(span)) {
656+
sb.removeSpan(span);
645657
}
646-
647-
sb.removeSpan(span);
648658
}
649659
}
650660

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

654668
// Set all bits for SPAN_PRIORITY so that this span has the highest possible priority
@@ -1081,7 +1095,7 @@ private void updateCachedSpannable(boolean resetStyles) {
10811095
// - android.app.Activity.dispatchKeyEvent (Activity.java:3447)
10821096
try {
10831097
sb.append(currentText.subSequence(0, currentText.length()));
1084-
unstripAttributeEquivalentSpans(sb);
1098+
restoreStyleEquivalentSpans(sb);
10851099
} catch (IndexOutOfBoundsException e) {
10861100
ReactSoftExceptionLogger.logSoftException(TAG, e);
10871101
}

0 commit comments

Comments
 (0)
Please sign in to comment.