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