Make Linkify links clickable in nonselectable text.

Test: bit FrameworksCoreTests:android.widget.TextViewActivityTest\#testToolbarAppearsAfterLinkClickedNonselectable
Bug: b/67629726

Change-Id: I94452116453fff1a36f5d567b3a686e92e97a34f
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 05d18d1..bddba07 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2095,14 +2095,7 @@
         if (!(mTextView.getText() instanceof Spannable)) {
             return;
         }
-        Spannable text = (Spannable) mTextView.getText();
         stopTextActionMode();
-        if (mTextView.isTextSelectable()) {
-            Selection.setSelection((Spannable) text, link.getStart(), link.getEnd());
-        } else {
-            //TODO: Nonselectable text
-        }
-
         getSelectionActionModeHelper().startLinkActionModeAsync(link);
     }
 
@@ -2179,7 +2172,8 @@
             return false;
         }
 
-        if (!checkField() || !mTextView.hasSelection()) {
+        if (actionMode != TextActionMode.TEXT_LINK
+                && (!checkField() || !mTextView.hasSelection())) {
             return false;
         }
 
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 2c6466c..3bfa520 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -235,10 +235,13 @@
             @Editor.TextActionMode int actionMode, @Nullable SelectionResult result) {
         final CharSequence text = getText(mTextView);
         if (result != null && text instanceof Spannable
-                && (mTextView.isTextSelectable() || mTextView.isTextEditable())) {
+                && (mTextView.isTextSelectable()
+                    || mTextView.isTextEditable()
+                    || actionMode == Editor.TextActionMode.TEXT_LINK)) {
             // Do not change the selection if TextClassifier should be dark launched.
             if (!mTextView.getTextClassifier().getSettings().isDarkLaunch()) {
                 Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
+                mTextView.invalidate();
             }
             mTextClassification = result.mClassification;
         } else {
@@ -250,8 +253,17 @@
                     && (mTextView.isTextSelectable() || mTextView.isTextEditable())) {
                 controller.show();
             }
-            if (result != null && actionMode == Editor.TextActionMode.SELECTION) {
-                mSelectionTracker.onSmartSelection(result);
+            if (result != null) {
+                switch (actionMode) {
+                    case Editor.TextActionMode.SELECTION:
+                        mSelectionTracker.onSmartSelection(result);
+                        break;
+                    case Editor.TextActionMode.TEXT_LINK:
+                        mSelectionTracker.onLinkSelected(result);
+                        break;
+                    default:
+                        break;
+                }
             }
         }
         mEditor.setRestartActionModeOnNextRefresh(false);
@@ -486,12 +498,24 @@
          * Called when selection action mode is started and the results come from a classifier.
          */
         public void onSmartSelection(SelectionResult result) {
+            onClassifiedSelection(result);
+            mLogger.logSelectionModified(
+                    result.mStart, result.mEnd, result.mClassification, result.mSelection);
+        }
+
+        /**
+         * Called when link action mode is started and the classification comes from a classifier.
+         */
+        public void onLinkSelected(SelectionResult result) {
+            onClassifiedSelection(result);
+            // TODO: log (b/70246800)
+        }
+
+        private void onClassifiedSelection(SelectionResult result) {
             if (isSelectionStarted()) {
                 mSelectionStart = result.mStart;
                 mSelectionEnd = result.mEnd;
                 mAllowReset = mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
-                mLogger.logSelectionModified(
-                        result.mStart, result.mEnd, result.mClassification, result.mSelection);
             }
         }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1e17f34..18933fe 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11164,11 +11164,9 @@
      */
     public boolean requestActionMode(@NonNull TextLinks.TextLink link) {
         Preconditions.checkNotNull(link);
-        if (mEditor != null) {
-            mEditor.startLinkActionModeAsync(link);
-            return true;
-        }
-        return false;
+        createEditorIfNeeded();
+        mEditor.startLinkActionModeAsync(link);
+        return true;
     }
     /**
      * @hide
diff --git a/core/tests/coretests/res/layout/activity_text_view.xml b/core/tests/coretests/res/layout/activity_text_view.xml
index e795c10..dca1656 100644
--- a/core/tests/coretests/res/layout/activity_text_view.xml
+++ b/core/tests/coretests/res/layout/activity_text_view.xml
@@ -25,4 +25,9 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
 
+    <TextView
+        android:id="@+id/nonselectable_textview"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
 </LinearLayout>
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 1a654f4..bbca12f 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -311,11 +311,20 @@
 
     @Test
     public void testToolbarAppearsAfterLinkClicked() throws Throwable {
+        runToolbarAppearsAfterLinkClickedTest(R.id.textview);
+    }
+
+    @Test
+    public void testToolbarAppearsAfterLinkClickedNonselectable() throws Throwable {
+        runToolbarAppearsAfterLinkClickedTest(R.id.nonselectable_textview);
+    }
+
+    private void runToolbarAppearsAfterLinkClickedTest(int id) throws Throwable {
+        TextView textView = mActivity.findViewById(id);
         useSystemDefaultTextClassifier();
         TextClassificationManager textClassificationManager =
                 mActivity.getSystemService(TextClassificationManager.class);
         TextClassifier textClassifier = textClassificationManager.getTextClassifier();
-        final TextView textView = mActivity.findViewById(R.id.textview);
         SpannableString content = new SpannableString("Call me at +19148277737");
         TextLinks links = textClassifier.generateLinks(content);
         links.apply(content, null);
@@ -331,7 +340,7 @@
 
         TextLinks.TextLink textLink = links.getLinks().iterator().next();
         int position = (textLink.getStart() + textLink.getEnd()) / 2;
-        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(position));
+        onView(withId(id)).perform(clickOnTextAtIndex(position));
         sleepForFloatingToolbarPopup();
         assertFloatingToolbarIsDisplayed();
     }