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