RESTRICT AUTOMERGE Do not linkify text with RLO/LRO characters.

am: a69950ce18

Change-Id: I0830e1c1a1cea2fd93f0b1ed8a1a792260201bb2
This commit is contained in:
Abodunrinwa Toki
2019-01-07 17:10:30 -08:00
committed by android-build-merger
6 changed files with 77 additions and 2 deletions

View File

@@ -30,6 +30,7 @@ import android.text.Spanned;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod; import android.text.method.MovementMethod;
import android.text.style.URLSpan; import android.text.style.URLSpan;
import android.util.Log;
import android.util.Patterns; import android.util.Patterns;
import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks; import android.view.textclassifier.TextLinks;
@@ -78,6 +79,9 @@ import java.util.regex.Pattern;
*/ */
public class Linkify { public class Linkify {
private static final String LOG_TAG = "Linkify";
/** /**
* Bit field indicating that web URLs should be matched in methods that * Bit field indicating that web URLs should be matched in methods that
* take an options mask * take an options mask
@@ -247,6 +251,11 @@ public class Linkify {
private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask, private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
@Nullable Context context) { @Nullable Context context) {
if (text != null && containsUnsupportedCharacters(text.toString())) {
android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
return false;
}
if (mask == 0) { if (mask == 0) {
return false; return false;
} }
@@ -292,6 +301,29 @@ public class Linkify {
return true; 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 * Scans the text of the provided TextView and turns all occurrences of
* the link types indicated in the mask into clickable links. If matches * the link types indicated in the mask into clickable links. If matches
@@ -462,6 +494,11 @@ public class Linkify {
public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern, public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
@Nullable String defaultScheme, @Nullable String[] schemes, @Nullable String defaultScheme, @Nullable String[] schemes,
@Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) { @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
if (spannable != null && containsUnsupportedCharacters(spannable.toString())) {
android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
return false;
}
final String[] schemesCopy; final String[] schemesCopy;
if (defaultScheme == null) defaultScheme = ""; if (defaultScheme == null) defaultScheme = "";
if (schemes == null || schemes.length < 1) { if (schemes == null || schemes.length < 1) {

View File

@@ -104,7 +104,7 @@ public final class TextClassification implements Parcelable {
/** /**
* @hide * @hide
*/ */
static final TextClassification EMPTY = new TextClassification.Builder().build(); public static final TextClassification EMPTY = new TextClassification.Builder().build();
private static final String LOG_TAG = "TextClassification"; private static final String LOG_TAG = "TextClassification";
// TODO(toki): investigate a way to derive this based on device properties. // TODO(toki): investigate a way to derive this based on device properties.

View File

@@ -107,6 +107,13 @@ public final class TextLinksParams {
Preconditions.checkNotNull(textLinks); Preconditions.checkNotNull(textLinks);
final String textString = text.toString(); final String textString = text.toString();
if (Linkify.containsUnsupportedCharacters(textString)) {
// Do not apply links to text containing unsupported characters.
android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
return TextLinks.STATUS_NO_LINKS_APPLIED;
}
if (!textString.startsWith(textLinks.getText())) { if (!textString.startsWith(textLinks.getText())) {
return TextLinks.STATUS_DIFFERENT_TEXT; return TextLinks.STATUS_DIFFERENT_TEXT;
} }

View File

@@ -31,6 +31,7 @@ import android.text.Layout;
import android.text.Selection; import android.text.Selection;
import android.text.Spannable; import android.text.Spannable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.util.Linkify;
import android.util.Log; import android.util.Log;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.textclassifier.SelectionEvent; import android.view.textclassifier.SelectionEvent;
@@ -1045,7 +1046,12 @@ public final class SelectionActionModeHelper {
trimText(); trimText();
final TextClassification classification; final TextClassification classification;
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { 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 if (mContext.getApplicationInfo().targetSdkVersion
>= Build.VERSION_CODES.P) {
final TextClassification.Request request = final TextClassification.Request request =
new TextClassification.Request.Builder( new TextClassification.Request.Builder(
mTrimmedText, mRelativeStart, mRelativeEnd) mTrimmedText, mRelativeStart, mRelativeEnd)

View File

@@ -39,6 +39,8 @@ import android.service.textclassifier.TextClassifierService;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.text.Spannable;
import android.text.SpannableString;
import org.hamcrest.BaseMatcher; import org.hamcrest.BaseMatcher;
import org.hamcrest.Description; import org.hamcrest.Description;
@@ -306,6 +308,16 @@ public class TextClassificationManagerTest {
mClassifier.generateLinks(request); mClassifier.generateLinks(request);
} }
@Test
public void testApplyLinks_unsupportedCharacter() {
if (isTextClassifierDisabled()) return;
Spannable url = new SpannableString("\u202Emoc.diordna.com");
TextLinks.Request request = new TextLinks.Request.Builder(url).build();
assertEquals(
TextLinks.STATUS_NO_LINKS_APPLIED,
mClassifier.generateLinks(request).apply(url, 0, null));
}
@Test @Test
public void testSetTextClassifier() { public void testSetTextClassifier() {
TextClassifier classifier = mock(TextClassifier.class); TextClassifier classifier = mock(TextClassifier.class);

View File

@@ -982,6 +982,19 @@ public class TextViewActivityTest {
assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist); 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 @Test
public void testSelectionMetricsLogger_noAbandonAfterCopy() throws Throwable { public void testSelectionMetricsLogger_noAbandonAfterCopy() throws Throwable {
final List<SelectionEvent> selectionEvents = new ArrayList<>(); final List<SelectionEvent> selectionEvents = new ArrayList<>();