1 /*
2  * Copyright (C) 2017 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.os.Parcel;
23 import android.os.Parcelable;
24 import android.view.textclassifier.TextClassifier.EntityType;
25 import android.view.textclassifier.TextClassifier.WidgetType;
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.Locale;
33 import java.util.Objects;
34 
35 /**
36  * A selection event.
37  * Specify index parameters as word token indices.
38  */
39 public final class SelectionEvent implements Parcelable {
40 
41     /** @hide */
42     @Retention(RetentionPolicy.SOURCE)
43     @IntDef({ACTION_OVERTYPE, ACTION_COPY, ACTION_PASTE, ACTION_CUT,
44             ACTION_SHARE, ACTION_SMART_SHARE, ACTION_DRAG, ACTION_ABANDON,
45             ACTION_OTHER, ACTION_SELECT_ALL, ACTION_RESET})
46     // NOTE: ActionType values should not be lower than 100 to avoid colliding with the other
47     // EventTypes declared below.
48     public @interface ActionType {
49         /*
50          * Terminal event types range: [100,200).
51          * Non-terminal event types range: [200,300).
52          */
53     }
54 
55     /** User typed over the selection. */
56     public static final int ACTION_OVERTYPE = 100;
57     /** User copied the selection. */
58     public static final int ACTION_COPY = 101;
59     /** User pasted over the selection. */
60     public static final int ACTION_PASTE = 102;
61     /** User cut the selection. */
62     public static final int ACTION_CUT = 103;
63     /** User shared the selection. */
64     public static final int ACTION_SHARE = 104;
65     /** User clicked the textAssist menu item. */
66     public static final int ACTION_SMART_SHARE = 105;
67     /** User dragged+dropped the selection. */
68     public static final int ACTION_DRAG = 106;
69     /** User abandoned the selection. */
70     public static final int ACTION_ABANDON = 107;
71     /** User performed an action on the selection. */
72     public static final int ACTION_OTHER = 108;
73 
74     // Non-terminal actions.
75     /** User activated Select All */
76     public static final int ACTION_SELECT_ALL = 200;
77     /** User reset the smart selection. */
78     public static final int ACTION_RESET = 201;
79 
80     /** @hide */
81     @Retention(RetentionPolicy.SOURCE)
82     @IntDef({ACTION_OVERTYPE, ACTION_COPY, ACTION_PASTE, ACTION_CUT,
83             ACTION_SHARE, ACTION_SMART_SHARE, ACTION_DRAG, ACTION_ABANDON,
84             ACTION_OTHER, ACTION_SELECT_ALL, ACTION_RESET,
85             EVENT_SELECTION_STARTED, EVENT_SELECTION_MODIFIED,
86             EVENT_SMART_SELECTION_SINGLE, EVENT_SMART_SELECTION_MULTI,
87             EVENT_AUTO_SELECTION})
88     // NOTE: EventTypes declared here must be less than 100 to avoid colliding with the
89     // ActionTypes declared above.
90     public @interface EventType {
91         /*
92          * Range: 1 -> 99.
93          */
94     }
95 
96     /** User started a new selection. */
97     public static final int EVENT_SELECTION_STARTED = 1;
98     /** User modified an existing selection. */
99     public static final int EVENT_SELECTION_MODIFIED = 2;
100     /** Smart selection triggered for a single token (word). */
101     public static final int EVENT_SMART_SELECTION_SINGLE = 3;
102     /** Smart selection triggered spanning multiple tokens (words). */
103     public static final int EVENT_SMART_SELECTION_MULTI = 4;
104     /** Something else other than User or the default TextClassifier triggered a selection. */
105     public static final int EVENT_AUTO_SELECTION = 5;
106 
107     /** @hide */
108     @Retention(RetentionPolicy.SOURCE)
109     @IntDef({INVOCATION_MANUAL, INVOCATION_LINK, INVOCATION_UNKNOWN})
110     public @interface InvocationMethod {}
111 
112     /** Selection was invoked by the user long pressing, double tapping, or dragging to select. */
113     public static final int INVOCATION_MANUAL = 1;
114     /** Selection was invoked by the user tapping on a link. */
115     public static final int INVOCATION_LINK = 2;
116     /** Unknown invocation method */
117     public static final int INVOCATION_UNKNOWN = 0;
118 
119     static final String NO_SIGNATURE = "";
120 
121     private final int mAbsoluteStart;
122     private final int mAbsoluteEnd;
123 
124     private @EntityType String mEntityType;
125     private @EventType int mEventType;
126     private String mPackageName = "";
127     private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN;
128     private @InvocationMethod int mInvocationMethod;
129     @Nullable private String mWidgetVersion;
130     @Nullable private String mResultId;
131     private long mEventTime;
132     private long mDurationSinceSessionStart;
133     private long mDurationSincePreviousEvent;
134     private int mEventIndex;
135     @Nullable private TextClassificationSessionId mSessionId;
136     private int mStart;
137     private int mEnd;
138     private int mSmartStart;
139     private int mSmartEnd;
140     @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
141 
SelectionEvent( int start, int end, @EventType int eventType, @EntityType String entityType, @InvocationMethod int invocationMethod, @Nullable String resultId)142     SelectionEvent(
143             int start, int end,
144             @EventType int eventType, @EntityType String entityType,
145             @InvocationMethod int invocationMethod, @Nullable String resultId) {
146         Preconditions.checkArgument(end >= start, "end cannot be less than start");
147         mAbsoluteStart = start;
148         mAbsoluteEnd = end;
149         mEventType = eventType;
150         mEntityType = Objects.requireNonNull(entityType);
151         mResultId = resultId;
152         mInvocationMethod = invocationMethod;
153     }
154 
SelectionEvent(Parcel in)155     private SelectionEvent(Parcel in) {
156         mAbsoluteStart = in.readInt();
157         mAbsoluteEnd = in.readInt();
158         mEventType = in.readInt();
159         mEntityType = in.readString();
160         mWidgetVersion = in.readInt() > 0 ? in.readString() : null;
161         mPackageName = in.readString();
162         mWidgetType = in.readString();
163         mInvocationMethod = in.readInt();
164         mResultId = in.readString();
165         mEventTime = in.readLong();
166         mDurationSinceSessionStart = in.readLong();
167         mDurationSincePreviousEvent = in.readLong();
168         mEventIndex = in.readInt();
169         mSessionId = in.readInt() > 0
170                 ? TextClassificationSessionId.CREATOR.createFromParcel(in) : null;
171         mStart = in.readInt();
172         mEnd = in.readInt();
173         mSmartStart = in.readInt();
174         mSmartEnd = in.readInt();
175         mSystemTcMetadata = in.readParcelable(null);
176     }
177 
178     @Override
writeToParcel(Parcel dest, int flags)179     public void writeToParcel(Parcel dest, int flags) {
180         dest.writeInt(mAbsoluteStart);
181         dest.writeInt(mAbsoluteEnd);
182         dest.writeInt(mEventType);
183         dest.writeString(mEntityType);
184         dest.writeInt(mWidgetVersion != null ? 1 : 0);
185         if (mWidgetVersion != null) {
186             dest.writeString(mWidgetVersion);
187         }
188         dest.writeString(mPackageName);
189         dest.writeString(mWidgetType);
190         dest.writeInt(mInvocationMethod);
191         dest.writeString(mResultId);
192         dest.writeLong(mEventTime);
193         dest.writeLong(mDurationSinceSessionStart);
194         dest.writeLong(mDurationSincePreviousEvent);
195         dest.writeInt(mEventIndex);
196         dest.writeInt(mSessionId != null ? 1 : 0);
197         if (mSessionId != null) {
198             mSessionId.writeToParcel(dest, flags);
199         }
200         dest.writeInt(mStart);
201         dest.writeInt(mEnd);
202         dest.writeInt(mSmartStart);
203         dest.writeInt(mSmartEnd);
204         dest.writeParcelable(mSystemTcMetadata, flags);
205     }
206 
207     @Override
describeContents()208     public int describeContents() {
209         return 0;
210     }
211 
212     /**
213      * Creates a "selection started" event.
214      *
215      * @param invocationMethod  the way the selection was triggered
216      * @param start  the index of the selected text
217      */
218     @NonNull
createSelectionStartedEvent( @electionEvent.InvocationMethod int invocationMethod, int start)219     public static SelectionEvent createSelectionStartedEvent(
220             @SelectionEvent.InvocationMethod int invocationMethod, int start) {
221         return new SelectionEvent(
222                 start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
223                 TextClassifier.TYPE_UNKNOWN, invocationMethod, NO_SIGNATURE);
224     }
225 
226     /**
227      * Creates a "selection modified" event.
228      * Use when the user modifies the selection.
229      *
230      * @param start  the start (inclusive) index of the selection
231      * @param end  the end (exclusive) index of the selection
232      *
233      * @throws IllegalArgumentException if end is less than start
234      */
235     @NonNull
createSelectionModifiedEvent(int start, int end)236     public static SelectionEvent createSelectionModifiedEvent(int start, int end) {
237         Preconditions.checkArgument(end >= start, "end cannot be less than start");
238         return new SelectionEvent(
239                 start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
240                 TextClassifier.TYPE_UNKNOWN, INVOCATION_UNKNOWN, NO_SIGNATURE);
241     }
242 
243     /**
244      * Creates a "selection modified" event.
245      * Use when the user modifies the selection and the selection's entity type is known.
246      *
247      * @param start  the start (inclusive) index of the selection
248      * @param end  the end (exclusive) index of the selection
249      * @param classification  the TextClassification object returned by the TextClassifier that
250      *      classified the selected text
251      *
252      * @throws IllegalArgumentException if end is less than start
253      */
254     @NonNull
createSelectionModifiedEvent( int start, int end, @NonNull TextClassification classification)255     public static SelectionEvent createSelectionModifiedEvent(
256             int start, int end, @NonNull TextClassification classification) {
257         Preconditions.checkArgument(end >= start, "end cannot be less than start");
258         Objects.requireNonNull(classification);
259         final String entityType = classification.getEntityCount() > 0
260                 ? classification.getEntity(0)
261                 : TextClassifier.TYPE_UNKNOWN;
262         return new SelectionEvent(
263                 start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
264                 entityType, INVOCATION_UNKNOWN, classification.getId());
265     }
266 
267     /**
268      * Creates a "selection modified" event.
269      * Use when a TextClassifier modifies the selection.
270      *
271      * @param start  the start (inclusive) index of the selection
272      * @param end  the end (exclusive) index of the selection
273      * @param selection  the TextSelection object returned by the TextClassifier for the
274      *      specified selection
275      *
276      * @throws IllegalArgumentException if end is less than start
277      */
278     @NonNull
createSelectionModifiedEvent( int start, int end, @NonNull TextSelection selection)279     public static SelectionEvent createSelectionModifiedEvent(
280             int start, int end, @NonNull TextSelection selection) {
281         Preconditions.checkArgument(end >= start, "end cannot be less than start");
282         Objects.requireNonNull(selection);
283         final String entityType = selection.getEntityCount() > 0
284                 ? selection.getEntity(0)
285                 : TextClassifier.TYPE_UNKNOWN;
286         return new SelectionEvent(
287                 start, end, SelectionEvent.EVENT_AUTO_SELECTION,
288                 entityType, INVOCATION_UNKNOWN, selection.getId());
289     }
290 
291     /**
292      * Creates an event specifying an action taken on a selection.
293      * Use when the user clicks on an action to act on the selected text.
294      *
295      * @param start  the start (inclusive) index of the selection
296      * @param end  the end (exclusive) index of the selection
297      * @param actionType  the action that was performed on the selection
298      *
299      * @throws IllegalArgumentException if end is less than start
300      */
301     @NonNull
createSelectionActionEvent( int start, int end, @SelectionEvent.ActionType int actionType)302     public static SelectionEvent createSelectionActionEvent(
303             int start, int end, @SelectionEvent.ActionType int actionType) {
304         Preconditions.checkArgument(end >= start, "end cannot be less than start");
305         checkActionType(actionType);
306         return new SelectionEvent(
307                 start, end, actionType, TextClassifier.TYPE_UNKNOWN, INVOCATION_UNKNOWN,
308                 NO_SIGNATURE);
309     }
310 
311     /**
312      * Creates an event specifying an action taken on a selection.
313      * Use when the user clicks on an action to act on the selected text and the selection's
314      * entity type is known.
315      *
316      * @param start  the start (inclusive) index of the selection
317      * @param end  the end (exclusive) index of the selection
318      * @param actionType  the action that was performed on the selection
319      * @param classification  the TextClassification object returned by the TextClassifier that
320      *      classified the selected text
321      *
322      * @throws IllegalArgumentException if end is less than start
323      * @throws IllegalArgumentException If actionType is not a valid SelectionEvent actionType
324      */
325     @NonNull
createSelectionActionEvent( int start, int end, @SelectionEvent.ActionType int actionType, @NonNull TextClassification classification)326     public static SelectionEvent createSelectionActionEvent(
327             int start, int end, @SelectionEvent.ActionType int actionType,
328             @NonNull TextClassification classification) {
329         Preconditions.checkArgument(end >= start, "end cannot be less than start");
330         Objects.requireNonNull(classification);
331         checkActionType(actionType);
332         final String entityType = classification.getEntityCount() > 0
333                 ? classification.getEntity(0)
334                 : TextClassifier.TYPE_UNKNOWN;
335         return new SelectionEvent(start, end, actionType, entityType, INVOCATION_UNKNOWN,
336                 classification.getId());
337     }
338 
339     /**
340      * @throws IllegalArgumentException If eventType is not an {@link SelectionEvent.ActionType}
341      */
checkActionType(@electionEvent.EventType int eventType)342     private static void checkActionType(@SelectionEvent.EventType int eventType)
343             throws IllegalArgumentException {
344         switch (eventType) {
345             case SelectionEvent.ACTION_OVERTYPE:  // fall through
346             case SelectionEvent.ACTION_COPY:  // fall through
347             case SelectionEvent.ACTION_PASTE:  // fall through
348             case SelectionEvent.ACTION_CUT:  // fall through
349             case SelectionEvent.ACTION_SHARE:  // fall through
350             case SelectionEvent.ACTION_SMART_SHARE:  // fall through
351             case SelectionEvent.ACTION_DRAG:  // fall through
352             case SelectionEvent.ACTION_ABANDON:  // fall through
353             case SelectionEvent.ACTION_SELECT_ALL:  // fall through
354             case SelectionEvent.ACTION_RESET:  // fall through
355             case SelectionEvent.ACTION_OTHER:  // fall through
356                 return;
357             default:
358                 throw new IllegalArgumentException(
359                         String.format(Locale.US, "%d is not an eventType", eventType));
360         }
361     }
362 
getAbsoluteStart()363     int getAbsoluteStart() {
364         return mAbsoluteStart;
365     }
366 
getAbsoluteEnd()367     int getAbsoluteEnd() {
368         return mAbsoluteEnd;
369     }
370 
371     /**
372      * Returns the type of event that was triggered. e.g. {@link #ACTION_COPY}.
373      */
374     @EventType
getEventType()375     public int getEventType() {
376         return mEventType;
377     }
378 
379     /**
380      * Sets the event type.
381      * @hide
382      */
383     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setEventType(@ventType int eventType)384     public void setEventType(@EventType int eventType) {
385         mEventType = eventType;
386     }
387 
388     /**
389      * Returns the type of entity that is associated with this event. e.g.
390      * {@link android.view.textclassifier.TextClassifier#TYPE_EMAIL}.
391      */
392     @EntityType
393     @NonNull
getEntityType()394     public String getEntityType() {
395         return mEntityType;
396     }
397 
setEntityType(@ntityType String entityType)398     void setEntityType(@EntityType String entityType) {
399         mEntityType = Objects.requireNonNull(entityType);
400     }
401 
402     /**
403      * Returns the package name of the app that this event originated in.
404      */
405     @NonNull
getPackageName()406     public String getPackageName() {
407         return mPackageName;
408     }
409 
410     /**
411      * Sets the information about the {@link SystemTextClassifier} that sent this request.
412      *
413      * @hide
414      */
setSystemTextClassifierMetadata(@ullable SystemTextClassifierMetadata systemTcMetadata)415     void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcMetadata) {
416         mSystemTcMetadata = systemTcMetadata;
417     }
418 
419     /**
420      * Returns the information about the {@link SystemTextClassifier} that sent this request.
421      *
422      * @hide
423      */
424     @Nullable
getSystemTextClassifierMetadata()425     public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
426         return mSystemTcMetadata;
427     }
428 
429     /**
430      * Returns the type of widget that was involved in triggering this event.
431      */
432     @WidgetType
433     @NonNull
getWidgetType()434     public String getWidgetType() {
435         return mWidgetType;
436     }
437 
438     /**
439      * Returns a string version info for the widget this event was triggered in.
440      */
441     @Nullable
getWidgetVersion()442     public String getWidgetVersion() {
443         return mWidgetVersion;
444     }
445 
446     /**
447      * Sets the {@link TextClassificationContext} for this event.
448      * @hide
449      */
450     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setTextClassificationSessionContext(TextClassificationContext context)451     public void setTextClassificationSessionContext(TextClassificationContext context) {
452         mPackageName = context.getPackageName();
453         mWidgetType = context.getWidgetType();
454         mWidgetVersion = context.getWidgetVersion();
455         mSystemTcMetadata = context.getSystemTextClassifierMetadata();
456     }
457 
458     /**
459      * Returns the way the selection mode was invoked.
460      */
getInvocationMethod()461     public @InvocationMethod int getInvocationMethod() {
462         return mInvocationMethod;
463     }
464 
465     /**
466      * Sets the invocationMethod for this event.
467      * @hide
468      */
469     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setInvocationMethod(@nvocationMethod int invocationMethod)470     public void setInvocationMethod(@InvocationMethod int invocationMethod) {
471         mInvocationMethod = invocationMethod;
472     }
473 
474     /**
475      * Returns the id of the text classifier result associated with this event.
476      */
477     @Nullable
getResultId()478     public String getResultId() {
479         return mResultId;
480     }
481 
setResultId(@ullable String resultId)482     SelectionEvent setResultId(@Nullable String resultId) {
483         mResultId = resultId;
484         return this;
485     }
486 
487     /**
488      * Returns the time this event was triggered.
489      */
getEventTime()490     public long getEventTime() {
491         return mEventTime;
492     }
493 
setEventTime(long timeMs)494     SelectionEvent setEventTime(long timeMs) {
495         mEventTime = timeMs;
496         return this;
497     }
498 
499     /**
500      * Returns the duration in ms between when this event was triggered and when the first event in
501      * the selection session was triggered.
502      */
getDurationSinceSessionStart()503     public long getDurationSinceSessionStart() {
504         return mDurationSinceSessionStart;
505     }
506 
setDurationSinceSessionStart(long durationMs)507     SelectionEvent setDurationSinceSessionStart(long durationMs) {
508         mDurationSinceSessionStart = durationMs;
509         return this;
510     }
511 
512     /**
513      * Returns the duration in ms between when this event was triggered and when the previous event
514      * in the selection session was triggered.
515      */
getDurationSincePreviousEvent()516     public long getDurationSincePreviousEvent() {
517         return mDurationSincePreviousEvent;
518     }
519 
setDurationSincePreviousEvent(long durationMs)520     SelectionEvent setDurationSincePreviousEvent(long durationMs) {
521         this.mDurationSincePreviousEvent = durationMs;
522         return this;
523     }
524 
525     /**
526      * Returns the index (e.g. 1st event, 2nd event, etc.) of this event in the selection session.
527      */
getEventIndex()528     public int getEventIndex() {
529         return mEventIndex;
530     }
531 
532     /** @hide */
533     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setEventIndex(int index)534     public SelectionEvent setEventIndex(int index) {
535         mEventIndex = index;
536         return this;
537     }
538 
539     /**
540      * Returns the selection session id.
541      */
542     @Nullable
getSessionId()543     public TextClassificationSessionId getSessionId() {
544         return mSessionId;
545     }
546 
547     /** @hide */
548     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setSessionId(@ullable TextClassificationSessionId id)549     public SelectionEvent setSessionId(@Nullable TextClassificationSessionId id) {
550         mSessionId = id;
551         return this;
552     }
553 
554     /**
555      * Returns the start index of this events relative to the index of the start selection
556      * event in the selection session.
557      */
getStart()558     public int getStart() {
559         return mStart;
560     }
561 
562     /** @hide */
563     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setStart(int start)564     public SelectionEvent setStart(int start) {
565         mStart = start;
566         return this;
567     }
568 
569     /**
570      * Returns the end index of this events relative to the index of the start selection
571      * event in the selection session.
572      */
getEnd()573     public int getEnd() {
574         return mEnd;
575     }
576 
577     /** @hide */
578     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setEnd(int end)579     public SelectionEvent setEnd(int end) {
580         mEnd = end;
581         return this;
582     }
583 
584     /**
585      * Returns the start index of this events relative to the index of the smart selection
586      * event in the selection session.
587      */
getSmartStart()588     public int getSmartStart() {
589         return mSmartStart;
590     }
591 
592     /** @hide */
593     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setSmartStart(int start)594     public SelectionEvent setSmartStart(int start) {
595         this.mSmartStart = start;
596         return this;
597     }
598 
599     /**
600      * Returns the end index of this events relative to the index of the smart selection
601      * event in the selection session.
602      */
getSmartEnd()603     public int getSmartEnd() {
604         return mSmartEnd;
605     }
606 
607     /** @hide */
608     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setSmartEnd(int end)609     public SelectionEvent setSmartEnd(int end) {
610         mSmartEnd = end;
611         return this;
612     }
613 
isTerminal()614     boolean isTerminal() {
615         return isTerminal(mEventType);
616     }
617 
618     /**
619      * Returns true if the eventType is a terminal event type. Otherwise returns false.
620      * A terminal event is an event that ends a selection interaction.
621      */
isTerminal(@ventType int eventType)622     public static boolean isTerminal(@EventType int eventType) {
623         switch (eventType) {
624             case ACTION_OVERTYPE:  // fall through
625             case ACTION_COPY:  // fall through
626             case ACTION_PASTE:  // fall through
627             case ACTION_CUT:  // fall through
628             case ACTION_SHARE:  // fall through
629             case ACTION_SMART_SHARE:  // fall through
630             case ACTION_DRAG:  // fall through
631             case ACTION_ABANDON:  // fall through
632             case ACTION_OTHER:  // fall through
633                 return true;
634             default:
635                 return false;
636         }
637     }
638 
639     @Override
hashCode()640     public int hashCode() {
641         return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
642                 mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId,
643                 mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
644                 mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mSystemTcMetadata);
645     }
646 
647     @Override
equals(Object obj)648     public boolean equals(Object obj) {
649         if (this == obj) {
650             return true;
651         }
652         if (!(obj instanceof SelectionEvent)) {
653             return false;
654         }
655 
656         final SelectionEvent other = (SelectionEvent) obj;
657         return mAbsoluteStart == other.mAbsoluteStart
658                 && mAbsoluteEnd == other.mAbsoluteEnd
659                 && mEventType == other.mEventType
660                 && Objects.equals(mEntityType, other.mEntityType)
661                 && Objects.equals(mWidgetVersion, other.mWidgetVersion)
662                 && Objects.equals(mPackageName, other.mPackageName)
663                 && Objects.equals(mWidgetType, other.mWidgetType)
664                 && mInvocationMethod == other.mInvocationMethod
665                 && Objects.equals(mResultId, other.mResultId)
666                 && mEventTime == other.mEventTime
667                 && mDurationSinceSessionStart == other.mDurationSinceSessionStart
668                 && mDurationSincePreviousEvent == other.mDurationSincePreviousEvent
669                 && mEventIndex == other.mEventIndex
670                 && Objects.equals(mSessionId, other.mSessionId)
671                 && mStart == other.mStart
672                 && mEnd == other.mEnd
673                 && mSmartStart == other.mSmartStart
674                 && mSmartEnd == other.mSmartEnd
675                 && mSystemTcMetadata == other.mSystemTcMetadata;
676     }
677 
678     @Override
toString()679     public String toString() {
680         return String.format(Locale.US,
681                 "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, "
682                         + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
683                         + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
684                         + "durationSincePreviousEvent=%d, eventIndex=%d,"
685                         + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d, "
686                         + "systemTcMetadata=%s}",
687                 mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
688                 mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
689                 mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
690                 mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mSystemTcMetadata);
691     }
692 
693     public static final @android.annotation.NonNull Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() {
694         @Override
695         public SelectionEvent createFromParcel(Parcel in) {
696             return new SelectionEvent(in);
697         }
698 
699         @Override
700         public SelectionEvent[] newArray(int size) {
701             return new SelectionEvent[size];
702         }
703     };
704 }
705