@@ -585,9 +585,7 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) {
585585        new  SpannableStringBuilder (reactTextUpdate .getText ());
586586
587587    manageSpans (spannableStringBuilder , reactTextUpdate .mContainsMultipleFragments );
588- 
589-     // Mitigation for https://github.com/facebook/react-native/issues/35936 (S318090) 
590-     stripAttributeEquivalentSpans (spannableStringBuilder );
588+     stripStyleEquivalentSpans (spannableStringBuilder );
591589
592590    mContainsImages  = reactTextUpdate .containsImages ();
593591
@@ -662,19 +660,44 @@ private void manageSpans(
662660    }
663661  }
664662
665-   private  void  stripAttributeEquivalentSpans (SpannableStringBuilder  sb ) {
666-     // We have already set a font size on the EditText itself. We can safely remove sizing spans 
667-     // which are the same as the set font size, and not otherwise overlapped. 
668-     final  int  effectiveFontSize  = mTextAttributes .getEffectiveFontSize ();
669-     ReactAbsoluteSizeSpan [] spans  = sb .getSpans (0 , sb .length (), ReactAbsoluteSizeSpan .class );
663+   // TODO: Replace with Predicate<T> and lambdas once Java 8 builds in OSS 
664+   interface  SpanPredicate <T > {
665+     boolean  test (T  span );
666+   }
670667
668+   /** 
669+    * Remove spans from the SpannableStringBuilder which can be represented by TextAppearance 
670+    * attributes on the underlying EditText. This works around instability on Samsung devices with 
671+    * the presence of spans https://github.com/facebook/react-native/issues/35936 (S318090) 
672+    */ 
673+   private  void  stripStyleEquivalentSpans (SpannableStringBuilder  sb ) {
674+     stripSpansOfKind (
675+         sb ,
676+         ReactAbsoluteSizeSpan .class ,
677+         new  SpanPredicate <ReactAbsoluteSizeSpan >() {
678+           @ Override 
679+           public  boolean  test (ReactAbsoluteSizeSpan  span ) {
680+             return  span .getSize () == mTextAttributes .getEffectiveFontSize ();
681+           }
682+         });
683+   }
684+ 
685+   private  <T > void  stripSpansOfKind (
686+       SpannableStringBuilder  sb , Class <T > clazz , SpanPredicate <T > isEquivalentToAttributes ) {
687+     T [] spans  = sb .getSpans (0 , sb .length (), clazz );
671688    outerLoop :
672-     for  (ReactAbsoluteSizeSpan  span  : spans ) {
673-       ReactAbsoluteSizeSpan [] overlappingSpans  =
674-           sb .getSpans (sb .getSpanStart (span ), sb .getSpanEnd (span ), ReactAbsoluteSizeSpan .class );
689+     for  (T  span  : spans ) {
690+       if  (!isEquivalentToAttributes .test (span )) {
691+         continue ;
692+       }
675693
676-       for  (ReactAbsoluteSizeSpan  overlappingSpan  : overlappingSpans ) {
677-         if  (span .getSize () != effectiveFontSize ) {
694+       int  priority  = sb .getSpanFlags (span ) & Spannable .SPAN_PRIORITY ;
695+       T [] overlappingSpans  = sb .getSpans (sb .getSpanStart (span ), sb .getSpanEnd (span ), clazz );
696+ 
697+       // Do not strip the span if removing it should show a non-equivalent span under it 
698+       for  (T  overlappingSpan  : overlappingSpans ) {
699+         int  overlappingPriority  = sb .getSpanFlags (overlappingSpan ) & Spanned .SPAN_PRIORITY ;
700+         if  (!isEquivalentToAttributes .test (overlappingSpan ) && priority  < overlappingPriority ) {
678701          continue  outerLoop ;
679702        }
680703      }
@@ -683,7 +706,11 @@ private void stripAttributeEquivalentSpans(SpannableStringBuilder sb) {
683706    }
684707  }
685708
686-   private  void  unstripAttributeEquivalentSpans (SpannableStringBuilder  workingText ) {
709+   /** 
710+    * Copy back styles represented as attributes to the underlying span, for later measurement 
711+    * outside the ReactEditText. 
712+    */ 
713+   private  void  restoreStyleEquivalentSpans (SpannableStringBuilder  workingText ) {
687714    int  spanFlags  = Spannable .SPAN_INCLUSIVE_INCLUSIVE ;
688715
689716    // Set all bits for SPAN_PRIORITY so that this span has the highest possible priority 
@@ -1122,7 +1149,7 @@ private void updateCachedSpannable(boolean resetStyles) {
11221149      // - android.app.Activity.dispatchKeyEvent (Activity.java:3447) 
11231150      try  {
11241151        sb .append (currentText .subSequence (0 , currentText .length ()));
1125-         unstripAttributeEquivalentSpans (sb );
1152+         restoreStyleEquivalentSpans (sb );
11261153      } catch  (IndexOutOfBoundsException  e ) {
11271154        ReactSoftExceptionLogger .logSoftException (TAG , e );
11281155      }
0 commit comments