Fix insertion handle disappearing in extract mode

In extract mode, on every screen touch
TextView#setExtractedText gets called which calls
SpannableStringBuilder#sendTextChanged which in turn stops
the action mode. As a fix, if the text is the same only
copy the spans without replacing everything.

Bug: 22315095
Change-Id: I28da760b3dc11e1cfbaf720e547bd817c5b89d7e
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index 8bc2876..24596c3 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -105,8 +105,8 @@
     @Override public boolean onTextContextMenuItem(int id) {
         if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
             // Mode was started on Extracted, needs to be stopped here.
-            // Cut and paste will change the text, which stops selection mode.
-            if (id == android.R.id.copy) stopTextActionMode();
+            // Cut will change the text, which stops selection mode.
+            if (id == android.R.id.copy || id == android.R.id.paste) stopTextActionMode();
             return true;
         }
         return super.onTextContextMenuItem(id);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 42ac599..131ba46 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6347,17 +6347,28 @@
         if (text.text != null) {
             if (content == null) {
                 setText(text.text, TextView.BufferType.EDITABLE);
-            } else if (text.partialStartOffset < 0) {
-                removeParcelableSpans(content, 0, content.length());
-                content.replace(0, content.length(), text.text);
             } else {
-                final int N = content.length();
-                int start = text.partialStartOffset;
-                if (start > N) start = N;
-                int end = text.partialEndOffset;
-                if (end > N) end = N;
+                int start = 0;
+                int end = content.length();
+
+                if (text.partialStartOffset >= 0) {
+                    final int N = content.length();
+                    start = text.partialStartOffset;
+                    if (start > N) start = N;
+                    end = text.partialEndOffset;
+                    if (end > N) end = N;
+                }
+
                 removeParcelableSpans(content, start, end);
-                content.replace(start, end, text.text);
+                if (TextUtils.equals(content.subSequence(start, end), text.text)) {
+                    if (text.text instanceof Spanned) {
+                        // OK to copy spans only.
+                        TextUtils.copySpansFrom((Spanned) text.text, start, end,
+                                Object.class, content, start);
+                    }
+                } else {
+                    content.replace(start, end, text.text);
+                }
             }
         }