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