1 /* 2 * Copyright 2018 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.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.icu.util.ULocale; 23 import android.os.Bundle; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.util.Arrays; 32 import java.util.Objects; 33 34 /** 35 * This class represents events that are sent by components to the {@link TextClassifier} to report 36 * something of note that relates to a feature powered by the TextClassifier. The TextClassifier may 37 * log these events or use them to improve future responses to queries. 38 * <p> 39 * Each category of events has its their own subclass. Events of each type have an associated 40 * set of related properties. You can find their specification in the subclasses. 41 */ 42 public abstract class TextClassifierEvent implements Parcelable { 43 44 private static final int PARCEL_TOKEN_TEXT_SELECTION_EVENT = 1; 45 private static final int PARCEL_TOKEN_TEXT_LINKIFY_EVENT = 2; 46 private static final int PARCEL_TOKEN_CONVERSATION_ACTION_EVENT = 3; 47 private static final int PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT = 4; 48 49 /** @hide **/ 50 @Retention(RetentionPolicy.SOURCE) 51 @IntDef({CATEGORY_SELECTION, CATEGORY_LINKIFY, 52 CATEGORY_CONVERSATION_ACTIONS, CATEGORY_LANGUAGE_DETECTION}) 53 public @interface Category { 54 // For custom event categories, use range 1000+. 55 } 56 57 /** 58 * Smart selection 59 * 60 * @see TextSelectionEvent 61 */ 62 public static final int CATEGORY_SELECTION = 1; 63 /** 64 * Linkify 65 * 66 * @see TextLinkifyEvent 67 */ 68 public static final int CATEGORY_LINKIFY = 2; 69 /** 70 * Conversation actions 71 * 72 * @see ConversationActionsEvent 73 */ 74 public static final int CATEGORY_CONVERSATION_ACTIONS = 3; 75 /** 76 * Language detection 77 * 78 * @see LanguageDetectionEvent 79 */ 80 public static final int CATEGORY_LANGUAGE_DETECTION = 4; 81 82 /** @hide */ 83 @Retention(RetentionPolicy.SOURCE) 84 @IntDef({TYPE_SELECTION_STARTED, TYPE_SELECTION_MODIFIED, 85 TYPE_SMART_SELECTION_SINGLE, TYPE_SMART_SELECTION_MULTI, TYPE_AUTO_SELECTION, 86 TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION, 87 TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION, 88 TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL, 89 TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED, TYPE_LINKS_GENERATED}) 90 public @interface Type { 91 // For custom event types, use range 1,000,000+. 92 } 93 94 // All these event type constants are required to match with those defined in 95 // textclassifier_enums.proto. 96 /** User started a new selection. */ 97 public static final int TYPE_SELECTION_STARTED = 1; 98 /** User modified an existing selection. */ 99 public static final int TYPE_SELECTION_MODIFIED = 2; 100 /** Smart selection triggered for a single token (word). */ 101 public static final int TYPE_SMART_SELECTION_SINGLE = 3; 102 /** Smart selection triggered spanning multiple tokens (words). */ 103 public static final int TYPE_SMART_SELECTION_MULTI = 4; 104 /** Something else other than user or the default TextClassifier triggered a selection. */ 105 public static final int TYPE_AUTO_SELECTION = 5; 106 /** Smart actions shown to the user. */ 107 public static final int TYPE_ACTIONS_SHOWN = 6; 108 /** User clicked a link. */ 109 public static final int TYPE_LINK_CLICKED = 7; 110 /** User typed over the selection. */ 111 public static final int TYPE_OVERTYPE = 8; 112 /** User clicked on Copy action. */ 113 public static final int TYPE_COPY_ACTION = 9; 114 /** User clicked on Paste action. */ 115 public static final int TYPE_PASTE_ACTION = 10; 116 /** User clicked on Cut action. */ 117 public static final int TYPE_CUT_ACTION = 11; 118 /** User clicked on Share action. */ 119 public static final int TYPE_SHARE_ACTION = 12; 120 /** User clicked on a Smart action. */ 121 public static final int TYPE_SMART_ACTION = 13; 122 /** User dragged+dropped the selection. */ 123 public static final int TYPE_SELECTION_DRAG = 14; 124 /** Selection is destroyed. */ 125 public static final int TYPE_SELECTION_DESTROYED = 15; 126 /** User clicked on a custom action. */ 127 public static final int TYPE_OTHER_ACTION = 16; 128 /** User clicked on Select All action */ 129 public static final int TYPE_SELECT_ALL = 17; 130 /** User reset the smart selection. */ 131 public static final int TYPE_SELECTION_RESET = 18; 132 /** User composed a reply. */ 133 public static final int TYPE_MANUAL_REPLY = 19; 134 /** TextClassifier generated some actions */ 135 public static final int TYPE_ACTIONS_GENERATED = 20; 136 /** Some text links were generated.*/ 137 public static final int TYPE_LINKS_GENERATED = 21; 138 139 @Category 140 private final int mEventCategory; 141 @Type 142 private final int mEventType; 143 @Nullable 144 private final String[] mEntityTypes; 145 @Nullable 146 private TextClassificationContext mEventContext; 147 @Nullable 148 private final String mResultId; 149 private final int mEventIndex; 150 private final float[] mScores; 151 @Nullable 152 private final String mModelName; 153 private final int[] mActionIndices; 154 @Nullable 155 private final ULocale mLocale; 156 private final Bundle mExtras; 157 158 /** 159 * Session id holder to help with converting this event to the legacy SelectionEvent. 160 * @hide 161 */ 162 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 163 @Nullable 164 public TextClassificationSessionId mHiddenTempSessionId; 165 TextClassifierEvent(Builder builder)166 private TextClassifierEvent(Builder builder) { 167 mEventCategory = builder.mEventCategory; 168 mEventType = builder.mEventType; 169 mEntityTypes = builder.mEntityTypes; 170 mEventContext = builder.mEventContext; 171 mResultId = builder.mResultId; 172 mEventIndex = builder.mEventIndex; 173 mScores = builder.mScores; 174 mModelName = builder.mModelName; 175 mActionIndices = builder.mActionIndices; 176 mLocale = builder.mLocale; 177 mExtras = builder.mExtras == null ? Bundle.EMPTY : builder.mExtras; 178 } 179 TextClassifierEvent(Parcel in)180 private TextClassifierEvent(Parcel in) { 181 mEventCategory = in.readInt(); 182 mEventType = in.readInt(); 183 mEntityTypes = in.readStringArray(); 184 mEventContext = in.readParcelable(null); 185 mResultId = in.readString(); 186 mEventIndex = in.readInt(); 187 int scoresLength = in.readInt(); 188 mScores = new float[scoresLength]; 189 in.readFloatArray(mScores); 190 mModelName = in.readString(); 191 mActionIndices = in.createIntArray(); 192 final String languageTag = in.readString(); 193 mLocale = languageTag == null ? null : ULocale.forLanguageTag(languageTag); 194 mExtras = in.readBundle(); 195 } 196 197 @Override describeContents()198 public int describeContents() { 199 return 0; 200 } 201 202 @NonNull 203 public static final Creator<TextClassifierEvent> CREATOR = new Creator<TextClassifierEvent>() { 204 @Override 205 public TextClassifierEvent createFromParcel(Parcel in) { 206 int token = in.readInt(); 207 if (token == PARCEL_TOKEN_TEXT_SELECTION_EVENT) { 208 return new TextSelectionEvent(in); 209 } 210 if (token == PARCEL_TOKEN_TEXT_LINKIFY_EVENT) { 211 return new TextLinkifyEvent(in); 212 } 213 if (token == PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT) { 214 return new LanguageDetectionEvent(in); 215 } 216 if (token == PARCEL_TOKEN_CONVERSATION_ACTION_EVENT) { 217 return new ConversationActionsEvent(in); 218 } 219 throw new IllegalStateException("Unexpected input event type token in parcel."); 220 } 221 222 @Override 223 public TextClassifierEvent[] newArray(int size) { 224 return new TextClassifierEvent[size]; 225 } 226 }; 227 228 @Override writeToParcel(Parcel dest, int flags)229 public void writeToParcel(Parcel dest, int flags) { 230 dest.writeInt(getParcelToken()); 231 dest.writeInt(mEventCategory); 232 dest.writeInt(mEventType); 233 dest.writeStringArray(mEntityTypes); 234 dest.writeParcelable(mEventContext, flags); 235 dest.writeString(mResultId); 236 dest.writeInt(mEventIndex); 237 dest.writeInt(mScores.length); 238 dest.writeFloatArray(mScores); 239 dest.writeString(mModelName); 240 dest.writeIntArray(mActionIndices); 241 dest.writeString(mLocale == null ? null : mLocale.toLanguageTag()); 242 dest.writeBundle(mExtras); 243 } 244 getParcelToken()245 private int getParcelToken() { 246 if (this instanceof TextSelectionEvent) { 247 return PARCEL_TOKEN_TEXT_SELECTION_EVENT; 248 } 249 if (this instanceof TextLinkifyEvent) { 250 return PARCEL_TOKEN_TEXT_LINKIFY_EVENT; 251 } 252 if (this instanceof LanguageDetectionEvent) { 253 return PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT; 254 } 255 if (this instanceof ConversationActionsEvent) { 256 return PARCEL_TOKEN_CONVERSATION_ACTION_EVENT; 257 } 258 throw new IllegalArgumentException("Unexpected type: " + this.getClass().getSimpleName()); 259 } 260 261 /** 262 * Returns the event category. e.g. {@link #CATEGORY_SELECTION}. 263 */ 264 @Category getEventCategory()265 public int getEventCategory() { 266 return mEventCategory; 267 } 268 269 /** 270 * Returns the event type. e.g. {@link #TYPE_SELECTION_STARTED}. 271 */ 272 @Type getEventType()273 public int getEventType() { 274 return mEventType; 275 } 276 277 /** 278 * Returns an array of entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}. 279 * 280 * @see Builder#setEntityTypes(String...) for supported types. 281 */ 282 @NonNull getEntityTypes()283 public String[] getEntityTypes() { 284 return mEntityTypes; 285 } 286 287 /** 288 * Returns the event context. 289 */ 290 @Nullable getEventContext()291 public TextClassificationContext getEventContext() { 292 return mEventContext; 293 } 294 295 /** 296 * Sets the event context. 297 * <p> 298 * Package-private for SystemTextClassifier's use. 299 */ setEventContext(@ullable TextClassificationContext eventContext)300 void setEventContext(@Nullable TextClassificationContext eventContext) { 301 mEventContext = eventContext; 302 } 303 304 /** 305 * Returns the id of the text classifier result related to this event. 306 */ 307 @Nullable getResultId()308 public String getResultId() { 309 return mResultId; 310 } 311 312 /** 313 * Returns the index of this event in the series of event it belongs to. 314 */ getEventIndex()315 public int getEventIndex() { 316 return mEventIndex; 317 } 318 319 /** 320 * Returns the scores of the suggestions. 321 */ 322 @NonNull getScores()323 public float[] getScores() { 324 return mScores; 325 } 326 327 /** 328 * Returns the model name. 329 */ 330 @Nullable getModelName()331 public String getModelName() { 332 return mModelName; 333 } 334 335 /** 336 * Returns the indices of the actions relating to this event. 337 * Actions are usually returned by the text classifier in priority order with the most 338 * preferred action at index 0. This list gives an indication of the position of the actions 339 * that are being reported. 340 * 341 * @see Builder#setActionIndices(int...) 342 */ 343 @NonNull getActionIndices()344 public int[] getActionIndices() { 345 return mActionIndices; 346 } 347 348 /** 349 * Returns the detected locale. 350 */ 351 @Nullable getLocale()352 public ULocale getLocale() { 353 return mLocale; 354 } 355 356 /** 357 * Returns a bundle containing non-structured extra information about this event. 358 * 359 * <p><b>NOTE: </b>Do not modify this bundle. 360 */ 361 @NonNull getExtras()362 public Bundle getExtras() { 363 return mExtras; 364 } 365 366 @Override toString()367 public String toString() { 368 StringBuilder out = new StringBuilder(128); 369 out.append(this.getClass().getSimpleName()); 370 out.append("{"); 371 out.append("mEventCategory=").append(mEventCategory); 372 out.append(", mEventType=").append(mEventType); 373 out.append(", mEntityTypes=").append(Arrays.toString(mEntityTypes)); 374 out.append(", mEventContext=").append(mEventContext); 375 out.append(", mResultId=").append(mResultId); 376 out.append(", mEventIndex=").append(mEventIndex); 377 out.append(", mExtras=").append(mExtras); 378 out.append(", mScores=").append(Arrays.toString(mScores)); 379 out.append(", mModelName=").append(mModelName); 380 out.append(", mActionIndices=").append(Arrays.toString(mActionIndices)); 381 toString(out); 382 out.append("}"); 383 return out.toString(); 384 } 385 386 /** 387 * Overrides this to append extra fields to the output of {@link #toString()}. 388 * <p> 389 * Extra fields should be formatted like this: ", {field_name}={field_value}". 390 */ toString(StringBuilder out)391 void toString(StringBuilder out) {} 392 393 /** 394 * Returns a {@link SelectionEvent} equivalent of this event; or {@code null} if it can not be 395 * converted to a {@link SelectionEvent}. 396 * @hide 397 */ 398 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 399 @Nullable toSelectionEvent()400 public final SelectionEvent toSelectionEvent() { 401 final int invocationMethod; 402 switch (getEventCategory()) { 403 case TextClassifierEvent.CATEGORY_SELECTION: 404 invocationMethod = SelectionEvent.INVOCATION_MANUAL; 405 break; 406 case TextClassifierEvent.CATEGORY_LINKIFY: 407 invocationMethod = SelectionEvent.INVOCATION_LINK; 408 break; 409 default: 410 // Cannot be converted to a SelectionEvent. 411 return null; 412 } 413 414 final String entityType = getEntityTypes().length > 0 415 ? getEntityTypes()[0] : TextClassifier.TYPE_UNKNOWN; 416 final SelectionEvent out = new SelectionEvent( 417 /* absoluteStart= */ 0, 418 /* absoluteEnd= */ 0, 419 /* eventType= */0, 420 entityType, 421 SelectionEvent.INVOCATION_UNKNOWN, 422 SelectionEvent.NO_SIGNATURE); 423 out.setInvocationMethod(invocationMethod); 424 425 final TextClassificationContext eventContext = getEventContext(); 426 if (eventContext != null) { 427 out.setTextClassificationSessionContext(getEventContext()); 428 } 429 out.setSessionId(mHiddenTempSessionId); 430 final String resultId = getResultId(); 431 out.setResultId(resultId == null ? SelectionEvent.NO_SIGNATURE : resultId); 432 out.setEventIndex(getEventIndex()); 433 434 435 final int eventType; 436 switch (getEventType()) { 437 case TextClassifierEvent.TYPE_SELECTION_STARTED: 438 eventType = SelectionEvent.EVENT_SELECTION_STARTED; 439 break; 440 case TextClassifierEvent.TYPE_SELECTION_MODIFIED: 441 eventType = SelectionEvent.EVENT_SELECTION_MODIFIED; 442 break; 443 case TextClassifierEvent.TYPE_SMART_SELECTION_SINGLE: 444 eventType = SelectionEvent.EVENT_SMART_SELECTION_SINGLE; 445 break; 446 case TextClassifierEvent.TYPE_SMART_SELECTION_MULTI: 447 eventType = SelectionEvent.EVENT_SMART_SELECTION_MULTI; 448 break; 449 case TextClassifierEvent.TYPE_AUTO_SELECTION: 450 eventType = SelectionEvent.EVENT_AUTO_SELECTION; 451 break; 452 case TextClassifierEvent.TYPE_OVERTYPE: 453 eventType = SelectionEvent.ACTION_OVERTYPE; 454 break; 455 case TextClassifierEvent.TYPE_COPY_ACTION: 456 eventType = SelectionEvent.ACTION_COPY; 457 break; 458 case TextClassifierEvent.TYPE_PASTE_ACTION: 459 eventType = SelectionEvent.ACTION_PASTE; 460 break; 461 case TextClassifierEvent.TYPE_CUT_ACTION: 462 eventType = SelectionEvent.ACTION_CUT; 463 break; 464 case TextClassifierEvent.TYPE_SHARE_ACTION: 465 eventType = SelectionEvent.ACTION_SHARE; 466 break; 467 case TextClassifierEvent.TYPE_SMART_ACTION: 468 eventType = SelectionEvent.ACTION_SMART_SHARE; 469 break; 470 case TextClassifierEvent.TYPE_SELECTION_DRAG: 471 eventType = SelectionEvent.ACTION_DRAG; 472 break; 473 case TextClassifierEvent.TYPE_SELECTION_DESTROYED: 474 eventType = SelectionEvent.ACTION_ABANDON; 475 break; 476 case TextClassifierEvent.TYPE_OTHER_ACTION: 477 eventType = SelectionEvent.ACTION_OTHER; 478 break; 479 case TextClassifierEvent.TYPE_SELECT_ALL: 480 eventType = SelectionEvent.ACTION_SELECT_ALL; 481 break; 482 case TextClassifierEvent.TYPE_SELECTION_RESET: 483 eventType = SelectionEvent.ACTION_RESET; 484 break; 485 default: 486 eventType = 0; 487 break; 488 } 489 out.setEventType(eventType); 490 491 if (this instanceof TextClassifierEvent.TextSelectionEvent) { 492 final TextClassifierEvent.TextSelectionEvent selEvent = 493 (TextClassifierEvent.TextSelectionEvent) this; 494 // TODO: Ideally, we should have these fields in events of type 495 // TextClassifierEvent.TextLinkifyEvent events too but we're now past the API deadline 496 // and will have to do with these fields being set only in TextSelectionEvent events. 497 // Fix this at the next API bump. 498 out.setStart(selEvent.getRelativeWordStartIndex()); 499 out.setEnd(selEvent.getRelativeWordEndIndex()); 500 out.setSmartStart(selEvent.getRelativeSuggestedWordStartIndex()); 501 out.setSmartEnd(selEvent.getRelativeSuggestedWordEndIndex()); 502 } 503 504 return out; 505 } 506 507 /** 508 * Builder to build a text classifier event. 509 * 510 * @param <T> The subclass to be built. 511 */ 512 public abstract static class Builder<T extends Builder<T>> { 513 514 private final int mEventCategory; 515 private final int mEventType; 516 private String[] mEntityTypes = new String[0]; 517 @Nullable 518 private TextClassificationContext mEventContext; 519 @Nullable 520 private String mResultId; 521 private int mEventIndex; 522 private float[] mScores = new float[0]; 523 @Nullable 524 private String mModelName; 525 private int[] mActionIndices = new int[0]; 526 @Nullable 527 private ULocale mLocale; 528 @Nullable 529 private Bundle mExtras; 530 531 /** 532 * Creates a builder for building {@link TextClassifierEvent}s. 533 * 534 * @param eventCategory The event category. e.g. {@link #CATEGORY_SELECTION} 535 * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED} 536 */ Builder(@ategory int eventCategory, @Type int eventType)537 private Builder(@Category int eventCategory, @Type int eventType) { 538 mEventCategory = eventCategory; 539 mEventType = eventType; 540 } 541 542 /** 543 * Sets the entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}. 544 * <p> 545 * Supported types: 546 * <p>See {@link TextClassifier.EntityType} 547 * <p>See {@link ConversationAction.ActionType} 548 * <p>See {@link ULocale#toLanguageTag()} 549 */ 550 @NonNull setEntityTypes(@onNull String... entityTypes)551 public T setEntityTypes(@NonNull String... entityTypes) { 552 Objects.requireNonNull(entityTypes); 553 mEntityTypes = new String[entityTypes.length]; 554 System.arraycopy(entityTypes, 0, mEntityTypes, 0, entityTypes.length); 555 return self(); 556 } 557 558 /** 559 * Sets the event context. 560 */ 561 @NonNull setEventContext(@ullable TextClassificationContext eventContext)562 public T setEventContext(@Nullable TextClassificationContext eventContext) { 563 mEventContext = eventContext; 564 return self(); 565 } 566 567 /** 568 * Sets the id of the text classifier result related to this event. 569 */ 570 @NonNull setResultId(@ullable String resultId)571 public T setResultId(@Nullable String resultId) { 572 mResultId = resultId; 573 return self(); 574 } 575 576 /** 577 * Sets the index of this event in the series of events it belongs to. 578 */ 579 @NonNull setEventIndex(int eventIndex)580 public T setEventIndex(int eventIndex) { 581 mEventIndex = eventIndex; 582 return self(); 583 } 584 585 /** 586 * Sets the scores of the suggestions. 587 */ 588 @NonNull setScores(@onNull float... scores)589 public T setScores(@NonNull float... scores) { 590 Objects.requireNonNull(scores); 591 mScores = new float[scores.length]; 592 System.arraycopy(scores, 0, mScores, 0, scores.length); 593 return self(); 594 } 595 596 /** 597 * Sets the model name string. 598 */ 599 @NonNull setModelName(@ullable String modelVersion)600 public T setModelName(@Nullable String modelVersion) { 601 mModelName = modelVersion; 602 return self(); 603 } 604 605 /** 606 * Sets the indices of the actions involved in this event. Actions are usually returned by 607 * the text classifier in priority order with the most preferred action at index 0. 608 * These indices give an indication of the position of the actions that are being reported. 609 * <p> 610 * E.g. 611 * <pre> 612 * // 3 smart actions are shown at index 0, 1, 2 respectively in response to a link click. 613 * new TextClassifierEvent.Builder(CATEGORY_LINKIFY, TYPE_ACTIONS_SHOWN) 614 * .setEventIndex(0, 1, 2) 615 * ... 616 * .build(); 617 * 618 * ... 619 * 620 * // Smart action at index 1 is activated. 621 * new TextClassifierEvent.Builder(CATEGORY_LINKIFY, TYPE_SMART_ACTION) 622 * .setEventIndex(1) 623 * ... 624 * .build(); 625 * </pre> 626 * 627 * @see TextClassification#getActions() 628 */ 629 @NonNull setActionIndices(@onNull int... actionIndices)630 public T setActionIndices(@NonNull int... actionIndices) { 631 mActionIndices = new int[actionIndices.length]; 632 System.arraycopy(actionIndices, 0, mActionIndices, 0, actionIndices.length); 633 return self(); 634 } 635 636 /** 637 * Sets the detected locale. 638 */ 639 @NonNull setLocale(@ullable ULocale locale)640 public T setLocale(@Nullable ULocale locale) { 641 mLocale = locale; 642 return self(); 643 } 644 645 /** 646 * Sets a bundle containing non-structured extra information about the event. 647 * 648 * <p><b>NOTE: </b>Prefer to set only immutable values on the bundle otherwise, avoid 649 * updating the internals of this bundle as it may have unexpected consequences on the 650 * clients of the built event object. For similar reasons, avoid depending on mutable 651 * objects in this bundle. 652 */ 653 @NonNull setExtras(@onNull Bundle extras)654 public T setExtras(@NonNull Bundle extras) { 655 mExtras = Objects.requireNonNull(extras); 656 return self(); 657 } 658 self()659 abstract T self(); 660 } 661 662 /** 663 * This class represents events that are related to the smart text selection feature. 664 * <p> 665 * <pre> 666 * // User started a selection. e.g. "York" in text "New York City, NY". 667 * new TextSelectionEvent.Builder(TYPE_SELECTION_STARTED) 668 * .setEventContext(classificationContext) 669 * .setEventIndex(0) 670 * .build(); 671 * 672 * // System smart-selects a recognized entity. e.g. "New York City". 673 * new TextSelectionEvent.Builder(TYPE_SMART_SELECTION_MULTI) 674 * .setEventContext(classificationContext) 675 * .setResultId(textSelection.getId()) 676 * .setRelativeWordStartIndex(-1) // Goes back one word to "New" from "York". 677 * .setRelativeWordEndIndex(2) // Goes forward 2 words from "York" to start of ",". 678 * .setEntityTypes(textClassification.getEntity(0)) 679 * .setScore(textClassification.getConfidenceScore(entityType)) 680 * .setEventIndex(1) 681 * .build(); 682 * 683 * // User resets the selection to the original selection. i.e. "York". 684 * new TextSelectionEvent.Builder(TYPE_SELECTION_RESET) 685 * .setEventContext(classificationContext) 686 * .setResultId(textSelection.getId()) 687 * .setRelativeSuggestedWordStartIndex(-1) // Repeated from above. 688 * .setRelativeSuggestedWordEndIndex(2) // Repeated from above. 689 * .setRelativeWordStartIndex(0) // Original selection is always at (0, 1]. 690 * .setRelativeWordEndIndex(1) 691 * .setEntityTypes(textClassification.getEntity(0)) 692 * .setScore(textClassification.getConfidenceScore(entityType)) 693 * .setEventIndex(2) 694 * .build(); 695 * 696 * // User modified the selection. e.g. "New". 697 * new TextSelectionEvent.Builder(TYPE_SELECTION_MODIFIED) 698 * .setEventContext(classificationContext) 699 * .setResultId(textSelection.getId()) 700 * .setRelativeSuggestedWordStartIndex(-1) // Repeated from above. 701 * .setRelativeSuggestedWordEndIndex(2) // Repeated from above. 702 * .setRelativeWordStartIndex(-1) // Goes backward one word from "York" to 703 * "New". 704 * .setRelativeWordEndIndex(0) // Goes backward one word to exclude "York". 705 * .setEntityTypes(textClassification.getEntity(0)) 706 * .setScore(textClassification.getConfidenceScore(entityType)) 707 * .setEventIndex(3) 708 * .build(); 709 * 710 * // Smart (contextual) actions (at indices, 0, 1, 2) presented to the user. 711 * // e.g. "Map", "Ride share", "Explore". 712 * new TextSelectionEvent.Builder(TYPE_ACTIONS_SHOWN) 713 * .setEventContext(classificationContext) 714 * .setResultId(textClassification.getId()) 715 * .setEntityTypes(textClassification.getEntity(0)) 716 * .setScore(textClassification.getConfidenceScore(entityType)) 717 * .setActionIndices(0, 1, 2) 718 * .setEventIndex(4) 719 * .build(); 720 * 721 * // User chooses the "Copy" action. 722 * new TextSelectionEvent.Builder(TYPE_COPY_ACTION) 723 * .setEventContext(classificationContext) 724 * .setResultId(textClassification.getId()) 725 * .setEntityTypes(textClassification.getEntity(0)) 726 * .setScore(textClassification.getConfidenceScore(entityType)) 727 * .setEventIndex(5) 728 * .build(); 729 * 730 * // User chooses smart action at index 1. i.e. "Ride share". 731 * new TextSelectionEvent.Builder(TYPE_SMART_ACTION) 732 * .setEventContext(classificationContext) 733 * .setResultId(textClassification.getId()) 734 * .setEntityTypes(textClassification.getEntity(0)) 735 * .setScore(textClassification.getConfidenceScore(entityType)) 736 * .setActionIndices(1) 737 * .setEventIndex(5) 738 * .build(); 739 * 740 * // Selection dismissed. 741 * new TextSelectionEvent.Builder(TYPE_SELECTION_DESTROYED) 742 * .setEventContext(classificationContext) 743 * .setResultId(textClassification.getId()) 744 * .setEntityTypes(textClassification.getEntity(0)) 745 * .setScore(textClassification.getConfidenceScore(entityType)) 746 * .setEventIndex(6) 747 * .build(); 748 * </pre> 749 * <p> 750 */ 751 public static final class TextSelectionEvent extends TextClassifierEvent implements Parcelable { 752 753 @NonNull 754 public static final Creator<TextSelectionEvent> CREATOR = 755 new Creator<TextSelectionEvent>() { 756 @Override 757 public TextSelectionEvent createFromParcel(Parcel in) { 758 in.readInt(); // skip token, we already know this is a TextSelectionEvent 759 return new TextSelectionEvent(in); 760 } 761 762 @Override 763 public TextSelectionEvent[] newArray(int size) { 764 return new TextSelectionEvent[size]; 765 } 766 }; 767 768 final int mRelativeWordStartIndex; 769 final int mRelativeWordEndIndex; 770 final int mRelativeSuggestedWordStartIndex; 771 final int mRelativeSuggestedWordEndIndex; 772 TextSelectionEvent(TextSelectionEvent.Builder builder)773 private TextSelectionEvent(TextSelectionEvent.Builder builder) { 774 super(builder); 775 mRelativeWordStartIndex = builder.mRelativeWordStartIndex; 776 mRelativeWordEndIndex = builder.mRelativeWordEndIndex; 777 mRelativeSuggestedWordStartIndex = builder.mRelativeSuggestedWordStartIndex; 778 mRelativeSuggestedWordEndIndex = builder.mRelativeSuggestedWordEndIndex; 779 } 780 TextSelectionEvent(Parcel in)781 private TextSelectionEvent(Parcel in) { 782 super(in); 783 mRelativeWordStartIndex = in.readInt(); 784 mRelativeWordEndIndex = in.readInt(); 785 mRelativeSuggestedWordStartIndex = in.readInt(); 786 mRelativeSuggestedWordEndIndex = in.readInt(); 787 } 788 789 @Override writeToParcel(Parcel dest, int flags)790 public void writeToParcel(Parcel dest, int flags) { 791 super.writeToParcel(dest, flags); 792 dest.writeInt(mRelativeWordStartIndex); 793 dest.writeInt(mRelativeWordEndIndex); 794 dest.writeInt(mRelativeSuggestedWordStartIndex); 795 dest.writeInt(mRelativeSuggestedWordEndIndex); 796 } 797 798 /** 799 * Returns the relative word index of the start of the selection. 800 */ getRelativeWordStartIndex()801 public int getRelativeWordStartIndex() { 802 return mRelativeWordStartIndex; 803 } 804 805 /** 806 * Returns the relative word (exclusive) index of the end of the selection. 807 */ getRelativeWordEndIndex()808 public int getRelativeWordEndIndex() { 809 return mRelativeWordEndIndex; 810 } 811 812 /** 813 * Returns the relative word index of the start of the smart selection. 814 */ getRelativeSuggestedWordStartIndex()815 public int getRelativeSuggestedWordStartIndex() { 816 return mRelativeSuggestedWordStartIndex; 817 } 818 819 /** 820 * Returns the relative word (exclusive) index of the end of the 821 * smart selection. 822 */ getRelativeSuggestedWordEndIndex()823 public int getRelativeSuggestedWordEndIndex() { 824 return mRelativeSuggestedWordEndIndex; 825 } 826 827 @Override toString(StringBuilder out)828 void toString(StringBuilder out) { 829 out.append(", getRelativeWordStartIndex=").append(mRelativeWordStartIndex); 830 out.append(", getRelativeWordEndIndex=").append(mRelativeWordEndIndex); 831 out.append(", getRelativeSuggestedWordStartIndex=") 832 .append(mRelativeSuggestedWordStartIndex); 833 out.append(", getRelativeSuggestedWordEndIndex=") 834 .append(mRelativeSuggestedWordEndIndex); 835 } 836 837 /** 838 * Builder class for {@link TextSelectionEvent}. 839 */ 840 public static final class Builder extends 841 TextClassifierEvent.Builder<TextSelectionEvent.Builder> { 842 int mRelativeWordStartIndex; 843 int mRelativeWordEndIndex; 844 int mRelativeSuggestedWordStartIndex; 845 int mRelativeSuggestedWordEndIndex; 846 847 /** 848 * Creates a builder for building {@link TextSelectionEvent}s. 849 * 850 * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED} 851 */ Builder(@ype int eventType)852 public Builder(@Type int eventType) { 853 super(CATEGORY_SELECTION, eventType); 854 } 855 856 /** 857 * Sets the relative word index of the start of the selection. 858 */ 859 @NonNull setRelativeWordStartIndex(int relativeWordStartIndex)860 public Builder setRelativeWordStartIndex(int relativeWordStartIndex) { 861 mRelativeWordStartIndex = relativeWordStartIndex; 862 return this; 863 } 864 865 /** 866 * Sets the relative word (exclusive) index of the end of the 867 * selection. 868 */ 869 @NonNull setRelativeWordEndIndex(int relativeWordEndIndex)870 public Builder setRelativeWordEndIndex(int relativeWordEndIndex) { 871 mRelativeWordEndIndex = relativeWordEndIndex; 872 return this; 873 } 874 875 /** 876 * Sets the relative word index of the start of the smart 877 * selection. 878 */ 879 @NonNull setRelativeSuggestedWordStartIndex(int relativeSuggestedWordStartIndex)880 public Builder setRelativeSuggestedWordStartIndex(int relativeSuggestedWordStartIndex) { 881 mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex; 882 return this; 883 } 884 885 /** 886 * Sets the relative word (exclusive) index of the end of the 887 * smart selection. 888 */ 889 @NonNull setRelativeSuggestedWordEndIndex(int relativeSuggestedWordEndIndex)890 public Builder setRelativeSuggestedWordEndIndex(int relativeSuggestedWordEndIndex) { 891 mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex; 892 return this; 893 } 894 895 @Override self()896 TextSelectionEvent.Builder self() { 897 return this; 898 } 899 900 /** 901 * Builds and returns a {@link TextSelectionEvent}. 902 */ 903 @NonNull build()904 public TextSelectionEvent build() { 905 return new TextSelectionEvent(this); 906 } 907 } 908 } 909 910 /** 911 * This class represents events that are related to the smart linkify feature. 912 * <p> 913 * <pre> 914 * // User clicked on a link. 915 * new TextLinkifyEvent.Builder(TYPE_LINK_CLICKED) 916 * .setEventContext(classificationContext) 917 * .setResultId(textClassification.getId()) 918 * .setEntityTypes(textClassification.getEntity(0)) 919 * .setScore(textClassification.getConfidenceScore(entityType)) 920 * .setEventIndex(0) 921 * .build(); 922 * 923 * // Smart (contextual) actions presented to the user in response to a link click. 924 * new TextLinkifyEvent.Builder(TYPE_ACTIONS_SHOWN) 925 * .setEventContext(classificationContext) 926 * .setResultId(textClassification.getId()) 927 * .setEntityTypes(textClassification.getEntity(0)) 928 * .setScore(textClassification.getConfidenceScore(entityType)) 929 * .setActionIndices(range(textClassification.getActions().size())) 930 * .setEventIndex(1) 931 * .build(); 932 * 933 * // User chooses smart action at index 0. 934 * new TextLinkifyEvent.Builder(TYPE_SMART_ACTION) 935 * .setEventContext(classificationContext) 936 * .setResultId(textClassification.getId()) 937 * .setEntityTypes(textClassification.getEntity(0)) 938 * .setScore(textClassification.getConfidenceScore(entityType)) 939 * .setActionIndices(0) 940 * .setEventIndex(2) 941 * .build(); 942 * </pre> 943 */ 944 public static final class TextLinkifyEvent extends TextClassifierEvent implements Parcelable { 945 946 @NonNull 947 public static final Creator<TextLinkifyEvent> CREATOR = 948 new Creator<TextLinkifyEvent>() { 949 @Override 950 public TextLinkifyEvent createFromParcel(Parcel in) { 951 in.readInt(); // skip token, we already know this is a TextLinkifyEvent 952 return new TextLinkifyEvent(in); 953 } 954 955 @Override 956 public TextLinkifyEvent[] newArray(int size) { 957 return new TextLinkifyEvent[size]; 958 } 959 }; 960 TextLinkifyEvent(Parcel in)961 private TextLinkifyEvent(Parcel in) { 962 super(in); 963 } 964 TextLinkifyEvent(TextLinkifyEvent.Builder builder)965 private TextLinkifyEvent(TextLinkifyEvent.Builder builder) { 966 super(builder); 967 } 968 969 /** 970 * Builder class for {@link TextLinkifyEvent}. 971 */ 972 public static final class Builder 973 extends TextClassifierEvent.Builder<TextLinkifyEvent.Builder> { 974 /** 975 * Creates a builder for building {@link TextLinkifyEvent}s. 976 * 977 * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION} 978 */ Builder(@ype int eventType)979 public Builder(@Type int eventType) { 980 super(TextClassifierEvent.CATEGORY_LINKIFY, eventType); 981 } 982 983 @Override self()984 Builder self() { 985 return this; 986 } 987 988 /** 989 * Builds and returns a {@link TextLinkifyEvent}. 990 */ 991 @NonNull build()992 public TextLinkifyEvent build() { 993 return new TextLinkifyEvent(this); 994 } 995 } 996 } 997 998 /** 999 * This class represents events that are related to the language detection feature. 1000 * <p> 1001 * <pre> 1002 * // Translate action shown for foreign text. 1003 * new LanguageDetectionEvent.Builder(TYPE_ACTIONS_SHOWN) 1004 * .setEventContext(classificationContext) 1005 * .setResultId(textClassification.getId()) 1006 * .setEntityTypes(language) 1007 * .setScore(score) 1008 * .setActionIndices(textClassification.getActions().indexOf(translateAction)) 1009 * .setEventIndex(0) 1010 * .build(); 1011 * 1012 * // Translate action selected. 1013 * new LanguageDetectionEvent.Builder(TYPE_SMART_ACTION) 1014 * .setEventContext(classificationContext) 1015 * .setResultId(textClassification.getId()) 1016 * .setEntityTypes(language) 1017 * .setScore(score) 1018 * .setActionIndices(textClassification.getActions().indexOf(translateAction)) 1019 * .setEventIndex(1) 1020 * .build(); 1021 */ 1022 public static final class LanguageDetectionEvent extends TextClassifierEvent 1023 implements Parcelable { 1024 1025 @NonNull 1026 public static final Creator<LanguageDetectionEvent> CREATOR = 1027 new Creator<LanguageDetectionEvent>() { 1028 @Override 1029 public LanguageDetectionEvent createFromParcel(Parcel in) { 1030 // skip token, we already know this is a LanguageDetectionEvent. 1031 in.readInt(); 1032 return new LanguageDetectionEvent(in); 1033 } 1034 1035 @Override 1036 public LanguageDetectionEvent[] newArray(int size) { 1037 return new LanguageDetectionEvent[size]; 1038 } 1039 }; 1040 LanguageDetectionEvent(Parcel in)1041 private LanguageDetectionEvent(Parcel in) { 1042 super(in); 1043 } 1044 LanguageDetectionEvent(LanguageDetectionEvent.Builder builder)1045 private LanguageDetectionEvent(LanguageDetectionEvent.Builder builder) { 1046 super(builder); 1047 } 1048 1049 /** 1050 * Builder class for {@link LanguageDetectionEvent}. 1051 */ 1052 public static final class Builder 1053 extends TextClassifierEvent.Builder<LanguageDetectionEvent.Builder> { 1054 1055 /** 1056 * Creates a builder for building {@link TextSelectionEvent}s. 1057 * 1058 * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION} 1059 */ Builder(@ype int eventType)1060 public Builder(@Type int eventType) { 1061 super(TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION, eventType); 1062 } 1063 1064 @Override self()1065 Builder self() { 1066 return this; 1067 } 1068 1069 /** 1070 * Builds and returns a {@link LanguageDetectionEvent}. 1071 */ 1072 @NonNull build()1073 public LanguageDetectionEvent build() { 1074 return new LanguageDetectionEvent(this); 1075 } 1076 } 1077 } 1078 1079 /** 1080 * This class represents events that are related to the conversation actions feature. 1081 * <p> 1082 * <pre> 1083 * // Conversation (contextual) actions/replies generated. 1084 * new ConversationActionsEvent.Builder(TYPE_ACTIONS_GENERATED) 1085 * .setEventContext(classificationContext) 1086 * .setResultId(conversationActions.getId()) 1087 * .setEntityTypes(getTypes(conversationActions)) 1088 * .setActionIndices(range(conversationActions.getActions().size())) 1089 * .setEventIndex(0) 1090 * .build(); 1091 * 1092 * // Conversation actions/replies presented to user. 1093 * new ConversationActionsEvent.Builder(TYPE_ACTIONS_SHOWN) 1094 * .setEventContext(classificationContext) 1095 * .setResultId(conversationActions.getId()) 1096 * .setEntityTypes(getTypes(conversationActions)) 1097 * .setActionIndices(range(conversationActions.getActions().size())) 1098 * .setEventIndex(1) 1099 * .build(); 1100 * 1101 * // User clicked the "Reply" button to compose their custom reply. 1102 * new ConversationActionsEvent.Builder(TYPE_MANUAL_REPLY) 1103 * .setEventContext(classificationContext) 1104 * .setResultId(conversationActions.getId()) 1105 * .setEventIndex(2) 1106 * .build(); 1107 * 1108 * // User selected a smart (contextual) action/reply. 1109 * new ConversationActionsEvent.Builder(TYPE_SMART_ACTION) 1110 * .setEventContext(classificationContext) 1111 * .setResultId(conversationActions.getId()) 1112 * .setEntityTypes(conversationActions.get(1).getType()) 1113 * .setScore(conversationAction.get(1).getConfidenceScore()) 1114 * .setActionIndices(1) 1115 * .setEventIndex(2) 1116 * .build(); 1117 * </pre> 1118 */ 1119 public static final class ConversationActionsEvent extends TextClassifierEvent 1120 implements Parcelable { 1121 1122 @NonNull 1123 public static final Creator<ConversationActionsEvent> CREATOR = 1124 new Creator<ConversationActionsEvent>() { 1125 @Override 1126 public ConversationActionsEvent createFromParcel(Parcel in) { 1127 // skip token, we already know this is a ConversationActionsEvent. 1128 in.readInt(); 1129 return new ConversationActionsEvent(in); 1130 } 1131 1132 @Override 1133 public ConversationActionsEvent[] newArray(int size) { 1134 return new ConversationActionsEvent[size]; 1135 } 1136 }; 1137 ConversationActionsEvent(Parcel in)1138 private ConversationActionsEvent(Parcel in) { 1139 super(in); 1140 } 1141 ConversationActionsEvent(ConversationActionsEvent.Builder builder)1142 private ConversationActionsEvent(ConversationActionsEvent.Builder builder) { 1143 super(builder); 1144 } 1145 1146 /** 1147 * Builder class for {@link ConversationActionsEvent}. 1148 */ 1149 public static final class Builder 1150 extends TextClassifierEvent.Builder<ConversationActionsEvent.Builder> { 1151 /** 1152 * Creates a builder for building {@link TextSelectionEvent}s. 1153 * 1154 * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION} 1155 */ Builder(@ype int eventType)1156 public Builder(@Type int eventType) { 1157 super(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType); 1158 } 1159 1160 @Override self()1161 Builder self() { 1162 return this; 1163 } 1164 1165 /** 1166 * Builds and returns a {@link ConversationActionsEvent}. 1167 */ 1168 @NonNull build()1169 public ConversationActionsEvent build() { 1170 return new ConversationActionsEvent(this); 1171 } 1172 } 1173 } 1174 } 1175