@@ -130,14 +130,15 @@ public int length() {
130130 private void resizeFor (int size ) {
131131 final int oldLength = mText .length ;
132132 final int newLength = ArrayUtils .idealCharArraySize (size + 1 );
133- final int after = oldLength - (mGapStart + mGapLength );
133+ final int delta = newLength - oldLength ;
134+ if (delta == 0 ) return ;
134135
135136 char [] newText = new char [newLength ];
136137 System .arraycopy (mText , 0 , newText , 0 , mGapStart );
138+ final int after = oldLength - (mGapStart + mGapLength );
137139 System .arraycopy (mText , oldLength - after , newText , newLength - after , after );
138140 mText = newText ;
139141
140- final int delta = newLength - oldLength ;
141142 mGapLength += delta ;
142143 if (mGapLength < 1 )
143144 new Exception ("mGapLength < 1" ).printStackTrace ();
@@ -305,6 +306,26 @@ private void change(int start, int end, CharSequence cs, int csStart, int csEnd)
305306 resizeFor (mText .length + nbNewChars - mGapLength );
306307 }
307308
309+ // The removal pass needs to be done before the gap is updated in order to broadcast the
310+ // correct previous positions to the correct intersecting SpanWatchers
311+ if (end > start ) { // no need for span fixup on pure insertion
312+ // A for loop will not work because the array is being modified
313+ // Do not iterate in reverse to keep the SpanWatchers notified in ordering
314+ // Also, a removed SpanWatcher should not get notified of removed spans located
315+ // further in the span array.
316+ int i = 0 ;
317+ while (i < mSpanCount ) {
318+ if ((mSpanFlags [i ] & Spanned .SPAN_EXCLUSIVE_EXCLUSIVE ) ==
319+ Spanned .SPAN_EXCLUSIVE_EXCLUSIVE &&
320+ mSpanStarts [i ] >= start && mSpanStarts [i ] < mGapStart + mGapLength &&
321+ mSpanEnds [i ] >= start && mSpanEnds [i ] < mGapStart + mGapLength ) {
322+ removeSpan (i );
323+ } else {
324+ i ++;
325+ }
326+ }
327+ }
328+
308329 mGapStart += nbNewChars ;
309330 mGapLength -= nbNewChars ;
310331
@@ -313,11 +334,10 @@ private void change(int start, int end, CharSequence cs, int csStart, int csEnd)
313334
314335 TextUtils .getChars (cs , csStart , csEnd , mText , start );
315336
316- if (end > start ) {
317- // no need for span fixup on pure insertion
318- boolean atEnd = (mGapStart + mGapLength == mText .length );
337+ if (end > start ) { // no need for span fixup on pure insertion
338+ final boolean atEnd = (mGapStart + mGapLength == mText .length );
319339
320- for (int i = mSpanCount - 1 ; i >= 0 ; i -- ) {
340+ for (int i = 0 ; i < mSpanCount ; i ++ ) {
321341 if (mSpanStarts [i ] >= start && mSpanStarts [i ] < mGapStart + mGapLength ) {
322342 int flag = (mSpanFlags [i ] & START_MASK ) >> START_SHIFT ;
323343
@@ -331,16 +351,11 @@ private void change(int start, int end, CharSequence cs, int csStart, int csEnd)
331351 if (mSpanEnds [i ] >= start && mSpanEnds [i ] < mGapStart + mGapLength ) {
332352 int flag = (mSpanFlags [i ] & END_MASK );
333353
334- if (flag == POINT || (flag == PARAGRAPH && atEnd ))
354+ if (flag == POINT || (flag == PARAGRAPH && atEnd )) {
335355 mSpanEnds [i ] = mGapStart + mGapLength ;
336- else
356+ } else {
337357 mSpanEnds [i ] = start ;
338- }
339-
340- // remove 0-length SPAN_EXCLUSIVE_EXCLUSIVE, which are POINT_MARK and could
341- // get their boundaries swapped by the above code
342- if (mSpanEnds [i ] < mSpanStarts [i ]) {
343- removeSpan (i );
358+ }
344359 }
345360 }
346361 }
@@ -359,7 +374,7 @@ private void change(int start, int end, CharSequence cs, int csStart, int csEnd)
359374 if (en > csEnd ) en = csEnd ;
360375
361376 // Add span only if this object is not yet used as a span in this string
362- if (getSpanStart (spans [i ]) < 0 && !( spans [ i ] instanceof SpanWatcher ) ) {
377+ if (getSpanStart (spans [i ]) < 0 ) {
363378 setSpan (false , spans [i ], st - csStart + start , en - csStart + start ,
364379 sp .getSpanFlags (spans [i ]));
365380 }
@@ -465,6 +480,7 @@ private void sendToSpanWatchers(int replaceStart, int replaceEnd, int nbNewChars
465480
466481 int newReplaceEnd = replaceEnd + nbNewChars ;
467482 boolean spanChanged = false ;
483+
468484 int previousSpanStart = spanStart ;
469485 if (spanStart > newReplaceEnd ) {
470486 if (nbNewChars != 0 ) {
@@ -477,11 +493,13 @@ private void sendToSpanWatchers(int replaceStart, int replaceEnd, int nbNewChars
477493 ((spanFlags & SPAN_START_AT_START ) != SPAN_START_AT_START )) &&
478494 (spanStart != newReplaceEnd ||
479495 ((spanFlags & SPAN_START_AT_END ) != SPAN_START_AT_END ))) {
480- // TODO previousSpanStart is incorrect, but we would need to save all the
481- // previous spans' positions before replace to provide it
496+ // TODO A correct previousSpanStart cannot be computed at this point.
497+ // It would require to save all the previous spans' positions before the replace
498+ // Using an invalid -1 value to convey this would break the broacast range
482499 spanChanged = true ;
483500 }
484501 }
502+
485503 int previousSpanEnd = spanEnd ;
486504 if (spanEnd > newReplaceEnd ) {
487505 if (nbNewChars != 0 ) {
0 commit comments