diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index 7e6eb4906c205..63929eae3e0d4 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -26,6 +26,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; @@ -64,6 +65,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 @@ -221,6 +225,11 @@ public class Linkify { * @return True if at least one link is found and applied. */ public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) { + if (text != null && containsUnsupportedCharacters(text.toString())) { + android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, ""); + return false; + } + if (mask == 0) { return false; } @@ -266,6 +275,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 @@ -413,6 +445,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 b6dd0b9487399..961e15b5b4bdb 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 227ef4cae275d..72d87eebfb4f7 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.view.ActionMode; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassifier; @@ -454,11 +455,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); } return mLastClassificationResult; diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 5a7bca4d44f56..a4f69873dd46d 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -761,6 +761,7 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2 textView.setText(text)); + getInstrumentation().waitForIdleSync(); + + onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.'))); + sleepForFloatingToolbarPopup(); + assertFloatingToolbarItemIndex(android.R.id.cut, 0); + } + public void testPastePlainText_menuAction() throws Exception { initializeClipboardWithText(TextStyle.STYLED);