Merge "Fix smart_linkify_enabled flag." into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e9a7cb8158
@@ -650,7 +650,8 @@ public class Linkify {
|
||||
final CharSequence truncatedText = text.subSequence(
|
||||
0, Math.min(text.length(), classifier.getMaxGenerateLinksTextLength()));
|
||||
|
||||
final Supplier<TextLinks> supplier = () -> classifier.generateLinks(truncatedText, options);
|
||||
final Supplier<TextLinks> supplier = () ->
|
||||
classifier.generateLinks(truncatedText, options.setLegacyFallback(true));
|
||||
final Consumer<TextLinks> consumer = links -> {
|
||||
if (links.getLinks().isEmpty()) {
|
||||
if (callback != null) {
|
||||
|
||||
@@ -30,6 +30,8 @@ import android.service.textclassifier.ITextLinksCallback;
|
||||
import android.service.textclassifier.ITextSelectionCallback;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -37,8 +39,10 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Proxy to the system's default TextClassifier.
|
||||
* @hide
|
||||
*/
|
||||
final class SystemTextClassifier implements TextClassifier {
|
||||
@VisibleForTesting(visibility = Visibility.PACKAGE)
|
||||
public final class SystemTextClassifier implements TextClassifier {
|
||||
|
||||
private static final String LOG_TAG = "SystemTextClassifier";
|
||||
|
||||
@@ -53,13 +57,13 @@ final class SystemTextClassifier implements TextClassifier {
|
||||
@GuardedBy("mLoggerLock")
|
||||
private Logger mLogger;
|
||||
|
||||
SystemTextClassifier(Context context, TextClassificationConstants settings)
|
||||
public SystemTextClassifier(Context context, TextClassificationConstants settings)
|
||||
throws ServiceManager.ServiceNotFoundException {
|
||||
mManagerService = ITextClassifierService.Stub.asInterface(
|
||||
ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
|
||||
mSettings = Preconditions.checkNotNull(settings);
|
||||
mFallback = new TextClassifierImpl(context, settings);
|
||||
mPackageName = context.getPackageName();
|
||||
mPackageName = Preconditions.checkNotNull(context.getPackageName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,8 +128,9 @@ final class SystemTextClassifier implements TextClassifier {
|
||||
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
|
||||
Utils.validate(text, false /* allowInMainThread */);
|
||||
|
||||
if (!mSettings.isSmartLinkifyEnabled()) {
|
||||
return TextClassifier.NO_OP.generateLinks(text, options);
|
||||
final boolean legacyFallback = options != null && options.isLegacyFallback();
|
||||
if (!mSettings.isSmartLinkifyEnabled() && legacyFallback) {
|
||||
return Utils.generateLegacyLinks(text, options);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -344,6 +344,25 @@ public final class TextClassification implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the specified intent.
|
||||
*
|
||||
* @throws IllegalArgumentException if context or intent is null
|
||||
* @hide
|
||||
*/
|
||||
public static void fireIntent(@NonNull final Context context, @NonNull final Intent intent) {
|
||||
switch (getIntentType(intent, context)) {
|
||||
case IntentType.ACTIVITY:
|
||||
context.startActivity(intent);
|
||||
return;
|
||||
case IntentType.SERVICE:
|
||||
context.startService(intent);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@IntentType
|
||||
private static int getIntentType(@NonNull Intent intent, @NonNull Context context) {
|
||||
Preconditions.checkArgument(context != null);
|
||||
|
||||
@@ -26,6 +26,12 @@ import android.os.LocaleList;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.URLSpan;
|
||||
import android.text.util.Linkify;
|
||||
import android.text.util.Linkify.LinkifyMask;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
@@ -37,6 +43,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interface for providing text classification related features.
|
||||
@@ -511,6 +518,65 @@ public interface TextClassifier {
|
||||
Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates links using legacy {@link Linkify}.
|
||||
*/
|
||||
public static TextLinks generateLegacyLinks(
|
||||
@NonNull CharSequence text, @NonNull TextLinks.Options options) {
|
||||
final String string = Preconditions.checkNotNull(text).toString();
|
||||
final TextLinks.Builder links = new TextLinks.Builder(string);
|
||||
|
||||
final List<String> entities = Preconditions.checkNotNull(options).getEntityConfig()
|
||||
.resolveEntityListModifications(Collections.emptyList());
|
||||
if (entities.contains(TextClassifier.TYPE_URL)) {
|
||||
addLinks(links, string, TextClassifier.TYPE_URL);
|
||||
}
|
||||
if (entities.contains(TextClassifier.TYPE_PHONE)) {
|
||||
addLinks(links, string, TextClassifier.TYPE_PHONE);
|
||||
}
|
||||
if (entities.contains(TextClassifier.TYPE_EMAIL)) {
|
||||
addLinks(links, string, TextClassifier.TYPE_EMAIL);
|
||||
}
|
||||
// NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well.
|
||||
return links.build();
|
||||
}
|
||||
|
||||
private static void addLinks(
|
||||
TextLinks.Builder links, String string, @EntityType String entityType) {
|
||||
final Spannable spannable = new SpannableString(string);
|
||||
if (Linkify.addLinks(spannable, linkMask(entityType))) {
|
||||
final URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
|
||||
for (URLSpan urlSpan : spans) {
|
||||
links.addLink(
|
||||
spannable.getSpanStart(urlSpan),
|
||||
spannable.getSpanEnd(urlSpan),
|
||||
entityScores(entityType),
|
||||
urlSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@LinkifyMask
|
||||
private static int linkMask(@EntityType String entityType) {
|
||||
switch (entityType) {
|
||||
case TextClassifier.TYPE_URL:
|
||||
return Linkify.WEB_URLS;
|
||||
case TextClassifier.TYPE_PHONE:
|
||||
return Linkify.PHONE_NUMBERS;
|
||||
case TextClassifier.TYPE_EMAIL:
|
||||
return Linkify.EMAIL_ADDRESSES;
|
||||
default:
|
||||
// NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Float> entityScores(@EntityType String entityType) {
|
||||
final Map<String, Float> scores = new ArrayMap<>();
|
||||
scores.put(entityType, 1f);
|
||||
return scores;
|
||||
}
|
||||
|
||||
private static void checkMainThread(boolean allowInMainThread) {
|
||||
if (!allowInMainThread && Looper.myLooper() == Looper.getMainLooper()) {
|
||||
Slog.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
|
||||
|
||||
@@ -209,13 +209,15 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
public TextLinks generateLinks(
|
||||
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
|
||||
Utils.validate(text, getMaxGenerateLinksTextLength(), false /* allowInMainThread */);
|
||||
|
||||
final boolean legacyFallback = options != null && options.isLegacyFallback();
|
||||
if (!mSettings.isSmartLinkifyEnabled() && legacyFallback) {
|
||||
return Utils.generateLegacyLinks(text, options);
|
||||
}
|
||||
|
||||
final String textString = text.toString();
|
||||
final TextLinks.Builder builder = new TextLinks.Builder(textString);
|
||||
|
||||
if (!mSettings.isSmartLinkifyEnabled()) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
try {
|
||||
final long startTimeMs = System.currentTimeMillis();
|
||||
final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
|
||||
|
||||
@@ -20,17 +20,21 @@ import android.annotation.FloatRange;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.LocaleList;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.Spannable;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.text.util.Linkify;
|
||||
import android.text.util.Linkify.LinkifyMask;
|
||||
import android.view.View;
|
||||
import android.view.textclassifier.TextClassifier.EntityType;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -197,6 +201,7 @@ public final class TextLinks implements Parcelable {
|
||||
private final EntityConfidence mEntityScores;
|
||||
private final int mStart;
|
||||
private final int mEnd;
|
||||
@Nullable final URLSpan mUrlSpan;
|
||||
|
||||
/**
|
||||
* Create a new TextLink.
|
||||
@@ -204,16 +209,19 @@ public final class TextLinks implements Parcelable {
|
||||
* @param start The start index of the identified subsequence
|
||||
* @param end The end index of the identified subsequence
|
||||
* @param entityScores A mapping of entity type to confidence score
|
||||
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
|
||||
*
|
||||
* @throws IllegalArgumentException if entityScores is null or empty
|
||||
*/
|
||||
TextLink(int start, int end, Map<String, Float> entityScores) {
|
||||
TextLink(int start, int end, Map<String, Float> entityScores,
|
||||
@Nullable URLSpan urlSpan) {
|
||||
Preconditions.checkNotNull(entityScores);
|
||||
Preconditions.checkArgument(!entityScores.isEmpty());
|
||||
Preconditions.checkArgument(start <= end);
|
||||
mStart = start;
|
||||
mEnd = end;
|
||||
mEntityScores = new EntityConfidence(entityScores);
|
||||
mUrlSpan = urlSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,6 +299,7 @@ public final class TextLinks implements Parcelable {
|
||||
mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
|
||||
mStart = in.readInt();
|
||||
mEnd = in.readInt();
|
||||
mUrlSpan = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,6 +310,7 @@ public final class TextLinks implements Parcelable {
|
||||
|
||||
private LocaleList mDefaultLocales;
|
||||
private TextClassifier.EntityConfig mEntityConfig;
|
||||
private boolean mLegacyFallback;
|
||||
|
||||
private @ApplyStrategy int mApplyStrategy;
|
||||
private Function<TextLink, TextLinkSpan> mSpanFactory;
|
||||
@@ -353,6 +363,17 @@ public final class TextLinks implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the TextClassifier can fallback to legacy links if smart linkify is
|
||||
* disabled.
|
||||
* <strong>Note: </strong>This is not parcelled.
|
||||
* @hide
|
||||
*/
|
||||
public Options setLegacyFallback(boolean legacyFallback) {
|
||||
mLegacyFallback = legacyFallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a strategy for resolving conflicts when applying generated links to text that
|
||||
* already have links.
|
||||
@@ -405,6 +426,16 @@ public final class TextLinks implements Parcelable {
|
||||
return mEntityConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the TextClassifier can fallback to legacy links if smart linkify is
|
||||
* disabled.
|
||||
* <strong>Note: </strong>This is not parcelled.
|
||||
* @hide
|
||||
*/
|
||||
public boolean isLegacyFallback() {
|
||||
return mLegacyFallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the strategy for resolving conflictswhen applying generated links to text that
|
||||
* already have links
|
||||
@@ -497,7 +528,7 @@ public final class TextLinks implements Parcelable {
|
||||
|
||||
private final TextLink mTextLink;
|
||||
|
||||
public TextLinkSpan(@Nullable TextLink textLink) {
|
||||
public TextLinkSpan(@NonNull TextLink textLink) {
|
||||
mTextLink = textLink;
|
||||
}
|
||||
|
||||
@@ -505,13 +536,38 @@ public final class TextLinks implements Parcelable {
|
||||
public void onClick(View widget) {
|
||||
if (widget instanceof TextView) {
|
||||
final TextView textView = (TextView) widget;
|
||||
textView.requestActionMode(this);
|
||||
final Context context = textView.getContext();
|
||||
if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) {
|
||||
if (textView.requestFocus()) {
|
||||
textView.requestActionMode(this);
|
||||
} else {
|
||||
// If textView can not take focus, then simply handle the click as it will
|
||||
// be difficult to get rid of the floating action mode.
|
||||
textView.handleClick(this);
|
||||
}
|
||||
} else {
|
||||
if (mTextLink.mUrlSpan != null) {
|
||||
mTextLink.mUrlSpan.onClick(textView);
|
||||
} else {
|
||||
textView.handleClick(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final TextLink getTextLink() {
|
||||
return mTextLink;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
@Nullable
|
||||
public final String getUrl() {
|
||||
if (mTextLink.mUrlSpan != null) {
|
||||
return mTextLink.mUrlSpan.getURL();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -534,12 +590,24 @@ public final class TextLinks implements Parcelable {
|
||||
/**
|
||||
* Adds a TextLink.
|
||||
*
|
||||
* @return this instance
|
||||
* @param start The start index of the identified subsequence
|
||||
* @param end The end index of the identified subsequence
|
||||
* @param entityScores A mapping of entity type to confidence score
|
||||
*
|
||||
* @throws IllegalArgumentException if entityScores is null or empty.
|
||||
*/
|
||||
public Builder addLink(int start, int end, Map<String, Float> entityScores) {
|
||||
mLinks.add(new TextLink(start, end, entityScores));
|
||||
mLinks.add(new TextLink(start, end, entityScores, null));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #addLink(int, int, Map)
|
||||
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
|
||||
*/
|
||||
Builder addLink(int start, int end, Map<String, Float> entityScores,
|
||||
@Nullable URLSpan urlSpan) {
|
||||
mLinks.add(new TextLink(start, end, entityScores, urlSpan));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ public final class SelectionActionModeHelper {
|
||||
mTextView.invalidate();
|
||||
}
|
||||
mTextClassification = result.mClassification;
|
||||
} else if (actionMode == Editor.TextActionMode.TEXT_LINK) {
|
||||
} else if (result != null && actionMode == Editor.TextActionMode.TEXT_LINK) {
|
||||
mTextClassification = result.mClassification;
|
||||
} else {
|
||||
mTextClassification = null;
|
||||
|
||||
@@ -162,6 +162,7 @@ import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.textclassifier.TextClassification;
|
||||
import android.view.textclassifier.TextClassificationManager;
|
||||
import android.view.textclassifier.TextClassifier;
|
||||
import android.view.textclassifier.TextLinks;
|
||||
@@ -187,6 +188,10 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A user interface element that displays text to the user.
|
||||
@@ -11515,16 +11520,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an ActionMode for the specified TextLink.
|
||||
* Starts an ActionMode for the specified TextLinkSpan.
|
||||
*
|
||||
* @return Whether or not we're attempting to start the action mode.
|
||||
* @hide
|
||||
*/
|
||||
public boolean requestActionMode(@NonNull TextLinks.TextLinkSpan clickedSpan) {
|
||||
Preconditions.checkNotNull(clickedSpan);
|
||||
final TextLinks.TextLink link = clickedSpan.getTextLink();
|
||||
Preconditions.checkNotNull(link);
|
||||
createEditorIfNeeded();
|
||||
|
||||
if (!(mText instanceof Spanned)) {
|
||||
return false;
|
||||
@@ -11533,14 +11535,54 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
final int start = ((Spanned) mText).getSpanStart(clickedSpan);
|
||||
final int end = ((Spanned) mText).getSpanEnd(clickedSpan);
|
||||
|
||||
if (start < 0 || end < 1) {
|
||||
if (start < 0 || end > mText.length() || start >= end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
createEditorIfNeeded();
|
||||
mEditor.startLinkActionModeAsync(start, end);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a click on the specified TextLinkSpan.
|
||||
*
|
||||
* @return Whether or not the click is being handled.
|
||||
* @hide
|
||||
*/
|
||||
public boolean handleClick(@NonNull TextLinks.TextLinkSpan clickedSpan) {
|
||||
Preconditions.checkNotNull(clickedSpan);
|
||||
if (mText instanceof Spanned) {
|
||||
final Spanned spanned = (Spanned) mText;
|
||||
final int start = spanned.getSpanStart(clickedSpan);
|
||||
final int end = spanned.getSpanEnd(clickedSpan);
|
||||
if (start >= 0 && end <= mText.length() && start < end) {
|
||||
final TextClassification.Options options = new TextClassification.Options()
|
||||
.setDefaultLocales(getTextLocales());
|
||||
final Supplier<TextClassification> supplier = () ->
|
||||
getTextClassifier().classifyText(mText, start, end, options);
|
||||
final Consumer<TextClassification> consumer = classification -> {
|
||||
if (classification != null) {
|
||||
final Intent intent = classification.getIntent();
|
||||
if (intent != null) {
|
||||
TextClassification.fireIntent(mContext, intent);
|
||||
} else {
|
||||
Log.d(LOG_TAG, "No link action to perform");
|
||||
}
|
||||
} else {
|
||||
// classification == null
|
||||
Log.d(LOG_TAG, "Timeout while classifying text");
|
||||
}
|
||||
};
|
||||
CompletableFuture.supplyAsync(supplier)
|
||||
.completeOnTimeout(null, 1, TimeUnit.SECONDS)
|
||||
.thenAccept(consumer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,11 @@ import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.URLSpan;
|
||||
import android.util.Patterns;
|
||||
import android.view.textclassifier.SystemTextClassifier;
|
||||
import android.view.textclassifier.TextClassificationConstants;
|
||||
import android.view.textclassifier.TextClassifier;
|
||||
import android.view.textclassifier.TextClassifierImpl;
|
||||
import android.view.textclassifier.TextLinks.TextLinkSpan;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -38,7 +43,11 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* LinkifyTest tests {@link Linkify}.
|
||||
@@ -149,4 +158,55 @@ public class LinkifyTest {
|
||||
assertEquals("android.com should be linkified", 1, spans.length);
|
||||
assertEquals("https://android.com", spans[0].getURL());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLinksAsync_useLegacyIfSmartDisabled_localTextClassifier()
|
||||
throws Exception {
|
||||
final TextClassificationConstants settings =
|
||||
TextClassificationConstants.loadFromString("smart_linkify_enabled=false");
|
||||
final TextClassifier classifier = new TextClassifierImpl(mContext, settings);
|
||||
testAddLinksAsync_useLegacyIfSmartDisabled(classifier);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLinksAsync_useLegacyIfSmartDisabled_systemTextClassifier()
|
||||
throws Exception {
|
||||
final TextClassificationConstants settings =
|
||||
TextClassificationConstants.loadFromString("smart_linkify_enabled=false");
|
||||
final TextClassifier classifier = new SystemTextClassifier(mContext, settings);
|
||||
testAddLinksAsync_useLegacyIfSmartDisabled(classifier);
|
||||
}
|
||||
|
||||
private void testAddLinksAsync_useLegacyIfSmartDisabled(TextClassifier classifier)
|
||||
throws Exception {
|
||||
final int linkMask = Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS | Linkify.WEB_URLS;
|
||||
final String string = "example@android.com\n"
|
||||
+ "(415) 555-1212\n"
|
||||
+ "http://android.com\n"
|
||||
+ "100 Android Rd California";
|
||||
final Spannable expected = new SpannableString(string);
|
||||
final Spannable actual = new SpannableString(string);
|
||||
|
||||
Linkify.addLinks(expected, linkMask); // legacy.
|
||||
Linkify.addLinksAsync(actual, classifier, linkMask).get();
|
||||
|
||||
final URLSpan[] expectedSpans = expected.getSpans(0, expected.length(), URLSpan.class);
|
||||
final TextLinkSpan[] actualSpans = actual.getSpans(0, actual.length(), TextLinkSpan.class);
|
||||
assertEquals(expectedSpans.length, actualSpans.length);
|
||||
final Set<List> expectedLinkSpec = new HashSet<>();
|
||||
final Set<List> actualLinkSpec = new HashSet<>();
|
||||
for (int i = 0; i < expectedSpans.length; i++) {
|
||||
final URLSpan expectedSpan = expectedSpans[i];
|
||||
final TextLinkSpan actualSpan = actualSpans[i];
|
||||
expectedLinkSpec.add(Arrays.asList(
|
||||
expected.getSpanStart(expectedSpan),
|
||||
expected.getSpanEnd(expectedSpan),
|
||||
expectedSpan.getURL()));
|
||||
actualLinkSpec.add(Arrays.asList(
|
||||
actual.getSpanStart(actualSpan),
|
||||
actual.getSpanEnd(actualSpan),
|
||||
actualSpan.getUrl()));
|
||||
}
|
||||
assertEquals(expectedLinkSpec, actualLinkSpec);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user