Log the "change selected text" gesture.

This logs when the selected text is changed e.g. by typing on
the hard or soft keyboard.

Bug: 64914512
Test: bit FrameworksCoreTests:android.widget.TextViewActivityTest
Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest
Merged-In: I82b3b2157856c607d08a82c0a3d9fb938af4c06a
Change-Id: I82b3b2157856c607d08a82c0a3d9fb938af4c06a
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 31d878f..f1f25bf 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1250,7 +1250,8 @@
         }
     }
 
-    void sendOnTextChanged(int start, int after) {
+    void sendOnTextChanged(int start, int before, int after) {
+        getSelectionActionModeHelper().onTextChanged(start, start + before);
         updateSpellCheckSpans(start, start + after, false);
 
         // Flip flag to indicate the word iterator needs to have the text reset.
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index aafa343..4ebb3cf 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -122,10 +122,8 @@
                 SelectionEvent.ActionType.DRAG, mTextClassification);
     }
 
-    public void onTypeOverSelection() {
-        mSelectionTracker.onSelectionAction(
-                mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
-                SelectionEvent.ActionType.OVERTYPE, mTextClassification);
+    public void onTextChanged(int start, int end) {
+        mSelectionTracker.onTextChanged(start, end, mTextClassification);
     }
 
     public boolean resetSelection(int textIndex) {
@@ -217,7 +215,6 @@
         private int mOriginalEnd;
         private int mSelectionStart;
         private int mSelectionEnd;
-        private boolean mSelectionStarted;
         private boolean mAllowReset;
 
         SelectionTracker(TextView textView) {
@@ -230,9 +227,8 @@
          */
         public void onOriginalSelection(
                 CharSequence text, int selectionStart, int selectionEnd, boolean editableText) {
-            mOriginalStart = selectionStart;
-            mOriginalEnd = selectionEnd;
-            mSelectionStarted = true;
+            mOriginalStart = mSelectionStart = selectionStart;
+            mOriginalEnd = mSelectionEnd = selectionEnd;
             mAllowReset = false;
             maybeInvalidateLogger();
             mLogger.logSelectionStarted(text, selectionStart);
@@ -242,7 +238,7 @@
          * Called when selection action mode is started and the results come from a classifier.
          */
         public void onSmartSelection(SelectionResult result) {
-            if (mSelectionStarted) {
+            if (isSelectionStarted()) {
                 mSelectionStart = result.mStart;
                 mSelectionEnd = result.mEnd;
                 mAllowReset = mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
@@ -257,7 +253,9 @@
         public void onSelectionUpdated(
                 int selectionStart, int selectionEnd,
                 @Nullable TextClassification classification) {
-            if (mSelectionStarted) {
+            if (isSelectionStarted()) {
+                mSelectionStart = selectionStart;
+                mSelectionEnd = selectionEnd;
                 mAllowReset = false;
                 mLogger.logSelectionModified(selectionStart, selectionEnd, classification, null);
             }
@@ -268,10 +266,13 @@
          */
         public void onSelectionDestroyed() {
             mAllowReset = false;
-            mSelectionStarted = false;
-            mLogger.logSelectionAction(
-                    mSelectionStart, mSelectionEnd,
-                    SelectionEvent.ActionType.ABANDON, null /* classification */);
+            // Wait a few ms to see if the selection was destroyed because of a text change event.
+            mTextView.postDelayed(() -> {
+                mLogger.logSelectionAction(
+                        mSelectionStart, mSelectionEnd,
+                        SelectionEvent.ActionType.ABANDON, null /* classification */);
+                mSelectionStart = mSelectionEnd = -1;
+            }, 100 /* ms */);
         }
 
         /**
@@ -281,7 +282,7 @@
                 int selectionStart, int selectionEnd,
                 @SelectionEvent.ActionType int action,
                 @Nullable TextClassification classification) {
-            if (mSelectionStarted) {
+            if (isSelectionStarted()) {
                 mAllowReset = false;
                 mLogger.logSelectionAction(selectionStart, selectionEnd, action, classification);
             }
@@ -295,13 +296,15 @@
          */
         public boolean resetSelection(int textIndex, Editor editor) {
             final TextView textView = editor.getTextView();
-            if (mSelectionStarted
+            if (isSelectionStarted()
                     && mAllowReset
                     && textIndex >= mSelectionStart && textIndex <= mSelectionEnd
                     && textView.getText() instanceof Spannable) {
                 mAllowReset = false;
                 boolean selected = editor.selectCurrentWord();
                 if (selected) {
+                    mSelectionStart = editor.getTextView().getSelectionStart();
+                    mSelectionEnd = editor.getTextView().getSelectionEnd();
                     mLogger.logSelectionAction(
                             textView.getSelectionStart(), textView.getSelectionEnd(),
                             SelectionEvent.ActionType.RESET, null /* classification */);
@@ -311,11 +314,21 @@
             return false;
         }
 
+        public void onTextChanged(int start, int end, TextClassification classification) {
+            if (isSelectionStarted() && start == mSelectionStart && end == mSelectionEnd) {
+                onSelectionAction(start, end, SelectionEvent.ActionType.OVERTYPE, classification);
+            }
+        }
+
         private void maybeInvalidateLogger() {
             if (mLogger.isEditTextLogger() != mTextView.isTextEditable()) {
                 mLogger = new SelectionMetricsLogger(mTextView);
             }
         }
+
+        private boolean isSelectionStarted() {
+            return mSelectionStart >= 0 && mSelectionEnd >= 0 && mSelectionStart != mSelectionEnd;
+        }
     }
 
     // TODO: Write tests
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cd2d2fd..11fe67c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9364,7 +9364,7 @@
             }
         }
 
-        if (mEditor != null) mEditor.sendOnTextChanged(start, after);
+        if (mEditor != null) mEditor.sendOnTextChanged(start, before, after);
     }
 
     /**