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