1 /* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view.textclassifier; 18 19 import android.annotation.FloatRange; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.os.LocaleList; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.text.Spannable; 28 import android.text.method.MovementMethod; 29 import android.text.style.ClickableSpan; 30 import android.text.style.URLSpan; 31 import android.text.util.Linkify; 32 import android.text.util.Linkify.LinkifyMask; 33 import android.view.View; 34 import android.view.textclassifier.TextClassifier.EntityType; 35 import android.widget.TextView; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.internal.annotations.VisibleForTesting.Visibility; 39 import com.android.internal.util.Preconditions; 40 41 import java.lang.annotation.Retention; 42 import java.lang.annotation.RetentionPolicy; 43 import java.util.ArrayList; 44 import java.util.Collection; 45 import java.util.Collections; 46 import java.util.List; 47 import java.util.Locale; 48 import java.util.Map; 49 import java.util.function.Function; 50 51 /** 52 * A collection of links, representing subsequences of text and the entity types (phone number, 53 * address, url, etc) they may be. 54 */ 55 public final class TextLinks implements Parcelable { 56 57 /** 58 * Return status of an attempt to apply TextLinks to text. 59 * @hide 60 */ 61 @Retention(RetentionPolicy.SOURCE) 62 @IntDef({STATUS_LINKS_APPLIED, STATUS_NO_LINKS_FOUND, STATUS_NO_LINKS_APPLIED, 63 STATUS_DIFFERENT_TEXT}) 64 public @interface Status {} 65 66 /** Links were successfully applied to the text. */ 67 public static final int STATUS_LINKS_APPLIED = 0; 68 69 /** No links exist to apply to text. Links count is zero. */ 70 public static final int STATUS_NO_LINKS_FOUND = 1; 71 72 /** No links applied to text. The links were filtered out. */ 73 public static final int STATUS_NO_LINKS_APPLIED = 2; 74 75 /** The specified text does not match the text used to generate the links. */ 76 public static final int STATUS_DIFFERENT_TEXT = 3; 77 78 /** @hide */ 79 @Retention(RetentionPolicy.SOURCE) 80 @IntDef({APPLY_STRATEGY_IGNORE, APPLY_STRATEGY_REPLACE}) 81 public @interface ApplyStrategy {} 82 83 /** 84 * Do not replace {@link ClickableSpan}s that exist where the {@link TextLinkSpan} needs to 85 * be applied to. Do not apply the TextLinkSpan. 86 */ 87 public static final int APPLY_STRATEGY_IGNORE = 0; 88 89 /** 90 * Replace any {@link ClickableSpan}s that exist where the {@link TextLinkSpan} needs to be 91 * applied to. 92 */ 93 public static final int APPLY_STRATEGY_REPLACE = 1; 94 95 private final String mFullText; 96 private final List<TextLink> mLinks; 97 TextLinks(String fullText, ArrayList<TextLink> links)98 private TextLinks(String fullText, ArrayList<TextLink> links) { 99 mFullText = fullText; 100 mLinks = Collections.unmodifiableList(links); 101 } 102 103 /** 104 * Returns the text that was used to generate these links. 105 * @hide 106 */ 107 @NonNull getText()108 public String getText() { 109 return mFullText; 110 } 111 112 /** 113 * Returns an unmodifiable Collection of the links. 114 */ 115 @NonNull getLinks()116 public Collection<TextLink> getLinks() { 117 return mLinks; 118 } 119 120 /** 121 * Annotates the given text with the generated links. It will fail if the provided text doesn't 122 * match the original text used to create the TextLinks. 123 * 124 * <p><strong>NOTE: </strong>It may be necessary to set a LinkMovementMethod on the TextView 125 * widget to properly handle links. See {@link TextView#setMovementMethod(MovementMethod)} 126 * 127 * @param text the text to apply the links to. Must match the original text 128 * @param applyStrategy the apply strategy used to determine how to apply links to text. 129 * e.g {@link TextLinks#APPLY_STRATEGY_IGNORE} 130 * @param spanFactory a custom span factory for converting TextLinks to TextLinkSpans. 131 * Set to {@code null} to use the default span factory. 132 * 133 * @return a status code indicating whether or not the links were successfully applied 134 * e.g. {@link #STATUS_LINKS_APPLIED} 135 */ 136 @Status apply( @onNull Spannable text, @ApplyStrategy int applyStrategy, @Nullable Function<TextLink, TextLinkSpan> spanFactory)137 public int apply( 138 @NonNull Spannable text, 139 @ApplyStrategy int applyStrategy, 140 @Nullable Function<TextLink, TextLinkSpan> spanFactory) { 141 Preconditions.checkNotNull(text); 142 return new TextLinksParams.Builder() 143 .setApplyStrategy(applyStrategy) 144 .setSpanFactory(spanFactory) 145 .build() 146 .apply(text, this); 147 } 148 149 @Override toString()150 public String toString() { 151 return String.format(Locale.US, "TextLinks{fullText=%s, links=%s}", mFullText, mLinks); 152 } 153 154 @Override describeContents()155 public int describeContents() { 156 return 0; 157 } 158 159 @Override writeToParcel(Parcel dest, int flags)160 public void writeToParcel(Parcel dest, int flags) { 161 dest.writeString(mFullText); 162 dest.writeTypedList(mLinks); 163 } 164 165 public static final Parcelable.Creator<TextLinks> CREATOR = 166 new Parcelable.Creator<TextLinks>() { 167 @Override 168 public TextLinks createFromParcel(Parcel in) { 169 return new TextLinks(in); 170 } 171 172 @Override 173 public TextLinks[] newArray(int size) { 174 return new TextLinks[size]; 175 } 176 }; 177 TextLinks(Parcel in)178 private TextLinks(Parcel in) { 179 mFullText = in.readString(); 180 mLinks = in.createTypedArrayList(TextLink.CREATOR); 181 } 182 183 /** 184 * A link, identifying a substring of text and possible entity types for it. 185 */ 186 public static final class TextLink implements Parcelable { 187 private final EntityConfidence mEntityScores; 188 private final int mStart; 189 private final int mEnd; 190 @Nullable final URLSpan mUrlSpan; 191 192 /** 193 * Create a new TextLink. 194 * 195 * @param start The start index of the identified subsequence 196 * @param end The end index of the identified subsequence 197 * @param entityScores A mapping of entity type to confidence score 198 * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled 199 * 200 * @throws IllegalArgumentException if entityScores is null or empty 201 */ TextLink(int start, int end, Map<String, Float> entityScores, @Nullable URLSpan urlSpan)202 TextLink(int start, int end, Map<String, Float> entityScores, 203 @Nullable URLSpan urlSpan) { 204 Preconditions.checkNotNull(entityScores); 205 Preconditions.checkArgument(!entityScores.isEmpty()); 206 Preconditions.checkArgument(start <= end); 207 mStart = start; 208 mEnd = end; 209 mEntityScores = new EntityConfidence(entityScores); 210 mUrlSpan = urlSpan; 211 } 212 213 /** 214 * Returns the start index of this link in the original text. 215 * 216 * @return the start index 217 */ getStart()218 public int getStart() { 219 return mStart; 220 } 221 222 /** 223 * Returns the end index of this link in the original text. 224 * 225 * @return the end index 226 */ getEnd()227 public int getEnd() { 228 return mEnd; 229 } 230 231 /** 232 * Returns the number of entity types that have confidence scores. 233 * 234 * @return the entity count 235 */ getEntityCount()236 public int getEntityCount() { 237 return mEntityScores.getEntities().size(); 238 } 239 240 /** 241 * Returns the entity type at a given index. Entity types are sorted by confidence. 242 * 243 * @return the entity type at the provided index 244 */ getEntity(int index)245 @NonNull public @EntityType String getEntity(int index) { 246 return mEntityScores.getEntities().get(index); 247 } 248 249 /** 250 * Returns the confidence score for a particular entity type. 251 * 252 * @param entityType the entity type 253 */ getConfidenceScore( @ntityType String entityType)254 public @FloatRange(from = 0.0, to = 1.0) float getConfidenceScore( 255 @EntityType String entityType) { 256 return mEntityScores.getConfidenceScore(entityType); 257 } 258 259 @Override toString()260 public String toString() { 261 return String.format(Locale.US, 262 "TextLink{start=%s, end=%s, entityScores=%s, urlSpan=%s}", 263 mStart, mEnd, mEntityScores, mUrlSpan); 264 } 265 266 @Override describeContents()267 public int describeContents() { 268 return 0; 269 } 270 271 @Override writeToParcel(Parcel dest, int flags)272 public void writeToParcel(Parcel dest, int flags) { 273 mEntityScores.writeToParcel(dest, flags); 274 dest.writeInt(mStart); 275 dest.writeInt(mEnd); 276 } 277 278 public static final Parcelable.Creator<TextLink> CREATOR = 279 new Parcelable.Creator<TextLink>() { 280 @Override 281 public TextLink createFromParcel(Parcel in) { 282 return new TextLink(in); 283 } 284 285 @Override 286 public TextLink[] newArray(int size) { 287 return new TextLink[size]; 288 } 289 }; 290 TextLink(Parcel in)291 private TextLink(Parcel in) { 292 mEntityScores = EntityConfidence.CREATOR.createFromParcel(in); 293 mStart = in.readInt(); 294 mEnd = in.readInt(); 295 mUrlSpan = null; 296 } 297 } 298 299 /** 300 * A request object for generating TextLinks. 301 */ 302 public static final class Request implements Parcelable { 303 304 private final CharSequence mText; 305 @Nullable private final LocaleList mDefaultLocales; 306 @Nullable private final TextClassifier.EntityConfig mEntityConfig; 307 private final boolean mLegacyFallback; 308 private String mCallingPackageName; 309 Request( CharSequence text, LocaleList defaultLocales, TextClassifier.EntityConfig entityConfig, boolean legacyFallback, String callingPackageName)310 private Request( 311 CharSequence text, 312 LocaleList defaultLocales, 313 TextClassifier.EntityConfig entityConfig, 314 boolean legacyFallback, 315 String callingPackageName) { 316 mText = text; 317 mDefaultLocales = defaultLocales; 318 mEntityConfig = entityConfig; 319 mLegacyFallback = legacyFallback; 320 mCallingPackageName = callingPackageName; 321 } 322 323 /** 324 * Returns the text to generate links for. 325 */ 326 @NonNull getText()327 public CharSequence getText() { 328 return mText; 329 } 330 331 /** 332 * @return ordered list of locale preferences that can be used to disambiguate 333 * the provided text 334 */ 335 @Nullable getDefaultLocales()336 public LocaleList getDefaultLocales() { 337 return mDefaultLocales; 338 } 339 340 /** 341 * @return The config representing the set of entities to look for 342 * @see Builder#setEntityConfig(TextClassifier.EntityConfig) 343 */ 344 @Nullable getEntityConfig()345 public TextClassifier.EntityConfig getEntityConfig() { 346 return mEntityConfig; 347 } 348 349 /** 350 * Returns whether the TextClassifier can fallback to legacy links if smart linkify is 351 * disabled. 352 * <strong>Note: </strong>This is not parcelled. 353 * @hide 354 */ isLegacyFallback()355 public boolean isLegacyFallback() { 356 return mLegacyFallback; 357 } 358 359 /** 360 * Sets the name of the package that requested the links to get generated. 361 */ setCallingPackageName(@ullable String callingPackageName)362 void setCallingPackageName(@Nullable String callingPackageName) { 363 mCallingPackageName = callingPackageName; 364 } 365 366 /** 367 * A builder for building TextLinks requests. 368 */ 369 public static final class Builder { 370 371 private final CharSequence mText; 372 373 @Nullable private LocaleList mDefaultLocales; 374 @Nullable private TextClassifier.EntityConfig mEntityConfig; 375 private boolean mLegacyFallback = true; // Use legacy fall back by default. 376 private String mCallingPackageName; 377 Builder(@onNull CharSequence text)378 public Builder(@NonNull CharSequence text) { 379 mText = Preconditions.checkNotNull(text); 380 } 381 382 /** 383 * @param defaultLocales ordered list of locale preferences that may be used to 384 * disambiguate the provided text. If no locale preferences exist, 385 * set this to null or an empty locale list. 386 * @return this builder 387 */ 388 @NonNull setDefaultLocales(@ullable LocaleList defaultLocales)389 public Builder setDefaultLocales(@Nullable LocaleList defaultLocales) { 390 mDefaultLocales = defaultLocales; 391 return this; 392 } 393 394 /** 395 * Sets the entity configuration to use. This determines what types of entities the 396 * TextClassifier will look for. 397 * Set to {@code null} for the default entity config and teh TextClassifier will 398 * automatically determine what links to generate. 399 * 400 * @return this builder 401 */ 402 @NonNull setEntityConfig(@ullable TextClassifier.EntityConfig entityConfig)403 public Builder setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) { 404 mEntityConfig = entityConfig; 405 return this; 406 } 407 408 /** 409 * Sets whether the TextClassifier can fallback to legacy links if smart linkify is 410 * disabled. 411 * 412 * <p><strong>Note: </strong>This is not parcelled. 413 * 414 * @return this builder 415 * @hide 416 */ 417 @NonNull setLegacyFallback(boolean legacyFallback)418 public Builder setLegacyFallback(boolean legacyFallback) { 419 mLegacyFallback = legacyFallback; 420 return this; 421 } 422 423 /** 424 * Sets the name of the package that requested the links to get generated. 425 * 426 * @return this builder 427 * @hide 428 */ 429 @NonNull setCallingPackageName(@ullable String callingPackageName)430 public Builder setCallingPackageName(@Nullable String callingPackageName) { 431 mCallingPackageName = callingPackageName; 432 return this; 433 } 434 435 /** 436 * Builds and returns the request object. 437 */ 438 @NonNull build()439 public Request build() { 440 return new Request( 441 mText, mDefaultLocales, mEntityConfig, 442 mLegacyFallback, mCallingPackageName); 443 } 444 445 } 446 447 /** 448 * @return the name of the package that requested the links to get generated. 449 * TODO: make available as system API 450 * @hide 451 */ 452 @Nullable getCallingPackageName()453 public String getCallingPackageName() { 454 return mCallingPackageName; 455 } 456 457 @Override describeContents()458 public int describeContents() { 459 return 0; 460 } 461 462 @Override writeToParcel(Parcel dest, int flags)463 public void writeToParcel(Parcel dest, int flags) { 464 dest.writeString(mText.toString()); 465 dest.writeInt(mDefaultLocales != null ? 1 : 0); 466 if (mDefaultLocales != null) { 467 mDefaultLocales.writeToParcel(dest, flags); 468 } 469 dest.writeInt(mEntityConfig != null ? 1 : 0); 470 if (mEntityConfig != null) { 471 mEntityConfig.writeToParcel(dest, flags); 472 } 473 dest.writeString(mCallingPackageName); 474 } 475 476 public static final Parcelable.Creator<Request> CREATOR = 477 new Parcelable.Creator<Request>() { 478 @Override 479 public Request createFromParcel(Parcel in) { 480 return new Request(in); 481 } 482 483 @Override 484 public Request[] newArray(int size) { 485 return new Request[size]; 486 } 487 }; 488 Request(Parcel in)489 private Request(Parcel in) { 490 mText = in.readString(); 491 mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in); 492 mEntityConfig = in.readInt() == 0 493 ? null : TextClassifier.EntityConfig.CREATOR.createFromParcel(in); 494 mLegacyFallback = true; 495 mCallingPackageName = in.readString(); 496 } 497 } 498 499 /** 500 * A ClickableSpan for a TextLink. 501 * 502 * <p>Applies only to TextViews. 503 */ 504 public static class TextLinkSpan extends ClickableSpan { 505 506 /** 507 * How the clickspan is triggered. 508 * @hide 509 */ 510 @Retention(RetentionPolicy.SOURCE) 511 @IntDef({INVOCATION_METHOD_UNSPECIFIED, INVOCATION_METHOD_TOUCH, 512 INVOCATION_METHOD_KEYBOARD}) 513 public @interface InvocationMethod {} 514 515 /** @hide */ 516 public static final int INVOCATION_METHOD_UNSPECIFIED = -1; 517 /** @hide */ 518 public static final int INVOCATION_METHOD_TOUCH = 0; 519 /** @hide */ 520 public static final int INVOCATION_METHOD_KEYBOARD = 1; 521 522 private final TextLink mTextLink; 523 TextLinkSpan(@onNull TextLink textLink)524 public TextLinkSpan(@NonNull TextLink textLink) { 525 mTextLink = textLink; 526 } 527 528 @Override onClick(View widget)529 public void onClick(View widget) { 530 onClick(widget, INVOCATION_METHOD_UNSPECIFIED); 531 } 532 533 /** @hide */ onClick(View widget, @InvocationMethod int invocationMethod)534 public final void onClick(View widget, @InvocationMethod int invocationMethod) { 535 if (widget instanceof TextView) { 536 final TextView textView = (TextView) widget; 537 final Context context = textView.getContext(); 538 if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) { 539 switch (invocationMethod) { 540 case INVOCATION_METHOD_TOUCH: 541 textView.requestActionMode(this); 542 break; 543 case INVOCATION_METHOD_KEYBOARD:// fall though 544 case INVOCATION_METHOD_UNSPECIFIED: // fall through 545 default: 546 textView.handleClick(this); 547 break; 548 } 549 } else { 550 if (mTextLink.mUrlSpan != null) { 551 mTextLink.mUrlSpan.onClick(textView); 552 } else { 553 textView.handleClick(this); 554 } 555 } 556 } 557 } 558 getTextLink()559 public final TextLink getTextLink() { 560 return mTextLink; 561 } 562 563 /** @hide */ 564 @VisibleForTesting(visibility = Visibility.PRIVATE) 565 @Nullable getUrl()566 public final String getUrl() { 567 if (mTextLink.mUrlSpan != null) { 568 return mTextLink.mUrlSpan.getURL(); 569 } 570 return null; 571 } 572 } 573 574 /** 575 * A builder to construct a TextLinks instance. 576 */ 577 public static final class Builder { 578 private final String mFullText; 579 private final ArrayList<TextLink> mLinks; 580 581 /** 582 * Create a new TextLinks.Builder. 583 * 584 * @param fullText The full text to annotate with links 585 */ Builder(@onNull String fullText)586 public Builder(@NonNull String fullText) { 587 mFullText = Preconditions.checkNotNull(fullText); 588 mLinks = new ArrayList<>(); 589 } 590 591 /** 592 * Adds a TextLink. 593 * 594 * @param start The start index of the identified subsequence 595 * @param end The end index of the identified subsequence 596 * @param entityScores A mapping of entity type to confidence score 597 * 598 * @throws IllegalArgumentException if entityScores is null or empty. 599 */ 600 @NonNull addLink(int start, int end, Map<String, Float> entityScores)601 public Builder addLink(int start, int end, Map<String, Float> entityScores) { 602 mLinks.add(new TextLink(start, end, entityScores, null)); 603 return this; 604 } 605 606 /** 607 * @see #addLink(int, int, Map) 608 * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled. 609 */ 610 @NonNull addLink(int start, int end, Map<String, Float> entityScores, @Nullable URLSpan urlSpan)611 Builder addLink(int start, int end, Map<String, Float> entityScores, 612 @Nullable URLSpan urlSpan) { 613 mLinks.add(new TextLink(start, end, entityScores, urlSpan)); 614 return this; 615 } 616 617 /** 618 * Removes all {@link TextLink}s. 619 */ 620 @NonNull clearTextLinks()621 public Builder clearTextLinks() { 622 mLinks.clear(); 623 return this; 624 } 625 626 /** 627 * Constructs a TextLinks instance. 628 * 629 * @return the constructed TextLinks 630 */ 631 @NonNull build()632 public TextLinks build() { 633 return new TextLinks(mFullText, mLinks); 634 } 635 } 636 637 // TODO: Remove once apps can build against the latest sdk. 638 /** 639 * Optional input parameters for generating TextLinks. 640 * @hide 641 */ 642 public static final class Options { 643 644 @Nullable private final TextClassificationSessionId mSessionId; 645 @Nullable private final Request mRequest; 646 @Nullable private LocaleList mDefaultLocales; 647 @Nullable private TextClassifier.EntityConfig mEntityConfig; 648 private boolean mLegacyFallback; 649 650 private @ApplyStrategy int mApplyStrategy; 651 private Function<TextLink, TextLinkSpan> mSpanFactory; 652 653 private String mCallingPackageName; 654 Options()655 public Options() { 656 this(null, null); 657 } 658 Options( @ullable TextClassificationSessionId sessionId, @Nullable Request request)659 private Options( 660 @Nullable TextClassificationSessionId sessionId, @Nullable Request request) { 661 mSessionId = sessionId; 662 mRequest = request; 663 } 664 665 /** Helper to create Options from a Request. */ from(TextClassificationSessionId sessionId, Request request)666 public static Options from(TextClassificationSessionId sessionId, Request request) { 667 final Options options = new Options(sessionId, request); 668 options.setDefaultLocales(request.getDefaultLocales()); 669 options.setEntityConfig(request.getEntityConfig()); 670 return options; 671 } 672 673 /** Returns a new options object based on the specified link mask. */ fromLinkMask(@inkifyMask int mask)674 public static Options fromLinkMask(@LinkifyMask int mask) { 675 final List<String> entitiesToFind = new ArrayList<>(); 676 677 if ((mask & Linkify.WEB_URLS) != 0) { 678 entitiesToFind.add(TextClassifier.TYPE_URL); 679 } 680 if ((mask & Linkify.EMAIL_ADDRESSES) != 0) { 681 entitiesToFind.add(TextClassifier.TYPE_EMAIL); 682 } 683 if ((mask & Linkify.PHONE_NUMBERS) != 0) { 684 entitiesToFind.add(TextClassifier.TYPE_PHONE); 685 } 686 if ((mask & Linkify.MAP_ADDRESSES) != 0) { 687 entitiesToFind.add(TextClassifier.TYPE_ADDRESS); 688 } 689 690 return new Options().setEntityConfig( 691 TextClassifier.EntityConfig.createWithEntityList(entitiesToFind)); 692 } 693 694 /** @param defaultLocales ordered list of locale preferences. */ setDefaultLocales(@ullable LocaleList defaultLocales)695 public Options setDefaultLocales(@Nullable LocaleList defaultLocales) { 696 mDefaultLocales = defaultLocales; 697 return this; 698 } 699 700 /** @param entityConfig definition of which entity types to look for. */ setEntityConfig(@ullable TextClassifier.EntityConfig entityConfig)701 public Options setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) { 702 mEntityConfig = entityConfig; 703 return this; 704 } 705 706 /** @param applyStrategy strategy to use when resolving conflicts. */ setApplyStrategy(@pplyStrategy int applyStrategy)707 public Options setApplyStrategy(@ApplyStrategy int applyStrategy) { 708 checkValidApplyStrategy(applyStrategy); 709 mApplyStrategy = applyStrategy; 710 return this; 711 } 712 713 /** @param spanFactory factory for converting TextLink to TextLinkSpan. */ setSpanFactory(@ullable Function<TextLink, TextLinkSpan> spanFactory)714 public Options setSpanFactory(@Nullable Function<TextLink, TextLinkSpan> spanFactory) { 715 mSpanFactory = spanFactory; 716 return this; 717 } 718 719 @Nullable getDefaultLocales()720 public LocaleList getDefaultLocales() { 721 return mDefaultLocales; 722 } 723 724 @Nullable getEntityConfig()725 public TextClassifier.EntityConfig getEntityConfig() { 726 return mEntityConfig; 727 } 728 729 @ApplyStrategy getApplyStrategy()730 public int getApplyStrategy() { 731 return mApplyStrategy; 732 } 733 734 @Nullable getSpanFactory()735 public Function<TextLink, TextLinkSpan> getSpanFactory() { 736 return mSpanFactory; 737 } 738 739 @Nullable getRequest()740 public Request getRequest() { 741 return mRequest; 742 } 743 744 @Nullable getSessionId()745 public TextClassificationSessionId getSessionId() { 746 return mSessionId; 747 } 748 checkValidApplyStrategy(int applyStrategy)749 private static void checkValidApplyStrategy(int applyStrategy) { 750 if (applyStrategy != APPLY_STRATEGY_IGNORE && applyStrategy != APPLY_STRATEGY_REPLACE) { 751 throw new IllegalArgumentException( 752 "Invalid apply strategy. See TextLinks.ApplyStrategy for options."); 753 } 754 } 755 } 756 } 757