Visible spans changes only invalidate the affected text sub display lists

When a span with a visible influence on the text is modified, we only
need to invalidate the text sub display lists that overlap this span.

This is especially useful when typing and the composing span (an underline
span) gets updated after each key stroke.

Change-Id: Ib2af3219c41eb79ec5d0a2eee317aca8c4efdef9
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 900f0d3..8d199d7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1339,6 +1339,38 @@
         if (translate) canvas.translate(0, -cursorOffsetVertical);
     }
 
+    /**
+     * Invalidates all the sub-display lists that overlap the specified character range
+     */
+    void invalidateTextDisplayList(Layout layout, int start, int end) {
+        if (mTextDisplayLists != null && layout instanceof DynamicLayout) {
+            final int firstLine = layout.getLineForOffset(start);
+            final int lastLine = layout.getLineForOffset(end);
+
+            DynamicLayout dynamicLayout = (DynamicLayout) layout;
+            int[] blockEndLines = dynamicLayout.getBlockEndLines();
+            int[] blockIndices = dynamicLayout.getBlockIndices();
+            final int numberOfBlocks = dynamicLayout.getNumberOfBlocks();
+
+            int i = 0;
+            // Skip the blocks before firstLine
+            while (i < numberOfBlocks) {
+                if (blockEndLines[i] >= firstLine) break;
+                i++;
+            }
+
+            // Invalidate all subsequent blocks until lastLine is passed
+            while (i < numberOfBlocks) {
+                final int blockIndex = blockIndices[i];
+                if (blockIndex != DynamicLayout.INVALID_BLOCK_INDEX) {
+                    mTextDisplayLists[blockIndex].invalidate();
+                }
+                if (blockEndLines[i] >= lastLine) break;
+                i++;
+            }
+        }
+    }
+
     void invalidateTextDisplayList() {
         if (mTextDisplayLists != null) {
             for (int i = 0; i < mTextDisplayLists.length; i++) {