diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index 768aee91e5b3e..bc462aa2b30bf 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -28,6 +28,7 @@ import android.text.Spanned; import android.text.method.LinkMovementMethod; import android.text.method.MovementMethod; import android.text.style.URLSpan; +import android.util.Log; import android.util.Patterns; import android.webkit.WebView; import android.widget.TextView; @@ -66,6 +67,9 @@ import java.util.regex.Pattern; */ public class Linkify { + + private static final String LOG_TAG = "Linkify"; + /** * Bit field indicating that web URLs should be matched in methods that * take an options mask @@ -228,6 +232,11 @@ public class Linkify { private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask, @Nullable Context context) { + if (text != null && containsUnsupportedCharacters(text.toString())) { + android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, ""); + return false; + } + if (mask == 0) { return false; } @@ -273,6 +282,29 @@ public class Linkify { return true; } + /** + * Returns true if the specified text contains at least one unsupported character for applying + * links. Also logs the error. + * + * @param text the text to apply links to + * @hide + */ + public static boolean containsUnsupportedCharacters(String text) { + if (text.contains("\u202C")) { + Log.e(LOG_TAG, "Unsupported character for applying links: u202C"); + return true; + } + if (text.contains("\u202D")) { + Log.e(LOG_TAG, "Unsupported character for applying links: u202D"); + return true; + } + if (text.contains("\u202E")) { + Log.e(LOG_TAG, "Unsupported character for applying links: u202E"); + return true; + } + return false; + } + /** * Scans the text of the provided TextView and turns all occurrences of * the link types indicated in the mask into clickable links. If matches @@ -420,6 +452,10 @@ public class Linkify { public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern, @Nullable String scheme, @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) { + if (spannable != null && containsUnsupportedCharacters(spannable.toString())) { + android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, ""); + return false; + } return addLinks(spannable, pattern, scheme, null, matchFilter, transformFilter); } diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 1849368f6ae95..c879233929159 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -38,7 +38,7 @@ public final class TextClassification { /** * @hide */ - static final TextClassification EMPTY = new TextClassification.Builder().build(); + public static final TextClassification EMPTY = new TextClassification.Builder().build(); @NonNull private final String mText; @Nullable private final Drawable mIcon; diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 384e254e205fa..f5db93e295152 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -25,6 +25,7 @@ import android.os.LocaleList; import android.text.Selection; import android.text.Spannable; import android.text.TextUtils; +import android.text.util.Linkify; import android.util.Log; import android.view.ActionMode; import android.view.textclassifier.TextClassification; @@ -692,11 +693,19 @@ final class SelectionActionModeHelper { mLastClassificationLocales = mLocales; trimText(); + final TextClassification classification; + if (Linkify.containsUnsupportedCharacters(mText)) { + // Do not show smart actions for text containing unsupported characters. + android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, ""); + classification = TextClassification.EMPTY; + } else { + classification = mTextClassifier.classifyText( + mTrimmedText, mRelativeStart, mRelativeEnd, mLocales); + } mLastClassificationResult = new SelectionResult( mSelectionStart, mSelectionEnd, - mTextClassifier.classifyText( - mTrimmedText, mRelativeStart, mRelativeEnd, mLocales), + classification, selection); } diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 56c72d2143477..ec8dee6583607 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -838,6 +838,19 @@ public class TextViewActivityTest { assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist); } + @Test + public void testNoAssistItemForTextFieldWithUnsupportedCharacters() throws Throwable { + useSystemDefaultTextClassifier(); + final String text = "\u202Emoc.diordna.com"; + final TextView textView = mActivity.findViewById(R.id.textview); + mActivityRule.runOnUiThread(() -> textView.setText(text)); + mInstrumentation.waitForIdleSync(); + + onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.'))); + sleepForFloatingToolbarPopup(); + assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist); + } + @Test public void testPastePlainText_menuAction() { initializeClipboardWithText(TextStyle.STYLED);