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