public final class

SelectionEvent

extends java.lang.Object

 java.lang.Object

↳androidx.textclassifier.SelectionEvent

Gradle dependencies

compile group: 'androidx.textclassifier', name: 'textclassifier', version: '1.0.0-alpha04'

  • groupId: androidx.textclassifier
  • artifactId: textclassifier
  • version: 1.0.0-alpha04

Artifact androidx.textclassifier:textclassifier:1.0.0-alpha04 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.textclassifier:textclassifier com.android.support:textclassifier

Overview

A selection event. Specify index parameters as word token indices.

Summary

Fields
public static final intACTION_ABANDON

User abandoned the selection.

public static final intACTION_COPY

User copied the selection.

public static final intACTION_CUT

User cut the selection.

public static final intACTION_DRAG

User dragged+dropped the selection.

public static final intACTION_OTHER

User performed an action on the selection.

public static final intACTION_OVERTYPE

User typed over the selection.

public static final intACTION_PASTE

User pasted over the selection.

public static final intACTION_RESET

User reset the smart selection.

public static final intACTION_SELECT_ALL

User activated Select All

public static final intACTION_SHARE

User shared the selection.

public static final intACTION_SMART_SHARE

User clicked the textAssist menu item.

public static final intEVENT_AUTO_SELECTION

Something else other than User or the default TextClassifier triggered a selection.

public static final intEVENT_SELECTION_MODIFIED

User modified an existing selection.

public static final intEVENT_SELECTION_STARTED

User started a new selection.

public static final intEVENT_SMART_SELECTION_MULTI

Smart selection triggered spanning multiple tokens (words).

public static final intEVENT_SMART_SELECTION_SINGLE

Smart selection triggered for a single token (word).

public static final intINVOCATION_LINK

Selection was invoked by the user tapping on a link.

public static final intINVOCATION_MANUAL

Selection was invoked by the user long pressing, double tapping, or dragging to select.

public static final intINVOCATION_UNKNOWN

Unknown invocation method

Methods
public static SelectionEventcreateFromBundle(Bundle bundle)

Extracts a SelectionEvent from a bundle that was added using SelectionEvent.toBundle().

public static SelectionEventcreateSelectionActionEvent(int start, int end, int actionType)

Creates an event specifying an action taken on a selection.

public static SelectionEventcreateSelectionActionEvent(int start, int end, int actionType, TextClassification classification)

Creates an event specifying an action taken on a selection.

public static SelectionEventcreateSelectionModifiedEvent(int start, int end)

Creates a "selection modified" event.

public static SelectionEventcreateSelectionModifiedEvent(int start, int end, TextClassification classification)

Creates a "selection modified" event.

public static SelectionEventcreateSelectionModifiedEvent(int start, int end, TextSelection selection)

Creates a "selection modified" event.

public static SelectionEventcreateSelectionStartedEvent(int invocationMethod, int start)

Creates a "selection started" event.

public booleanequals(java.lang.Object obj)

public longgetDurationSincePreviousEvent()

Returns the duration in ms between when this event was triggered and when the previous event in the selection session was triggered.

public longgetDurationSinceSessionStart()

Returns the duration in ms between when this event was triggered and when the first event in the selection session was triggered.

public intgetEnd()

Returns the end index of this events relative to the index of the start selection event in the selection session.

public java.lang.StringgetEntityType()

Returns the type of entity that is associated with this event.

public intgetEventIndex()

Returns the index (e.g.

public longgetEventTime()

Returns the time this event was triggered.

public intgetEventType()

Returns the type of event that was triggered.

public intgetInvocationMethod()

Returns the way the selection mode was invoked.

public java.lang.StringgetPackageName()

Returns the package name of the app that this event originated in.

public java.lang.StringgetResultId()

Returns the id of the text classifier result associated with this event.

public TextClassificationSessionIdgetSessionId()

Returns the selection session id.

public intgetSmartEnd()

Returns the end index of this events relative to the index of the smart selection event in the selection session.

public intgetSmartStart()

Returns the start index of this events relative to the index of the smart selection event in the selection session.

public intgetStart()

Returns the start index of this events relative to the index of the start selection event in the selection session.

public java.lang.StringgetWidgetType()

Returns the type of widget that was involved in triggering this event.

public java.lang.StringgetWidgetVersion()

Returns a string version info for the widget this event was triggered in.

public inthashCode()

public static booleanisTerminal(int eventType)

Returns true if the eventType is a terminal event type.

public BundletoBundle()

Adds this Icon to a Bundle that can be read back with the same parameters to SelectionEvent.createFromBundle(Bundle).

public java.lang.StringtoString()

from java.lang.Objectclone, finalize, getClass, notify, notifyAll, wait, wait, wait

Fields

public static final int ACTION_OVERTYPE

User typed over the selection.

public static final int ACTION_COPY

User copied the selection.

public static final int ACTION_PASTE

User pasted over the selection.

public static final int ACTION_CUT

User cut the selection.

public static final int ACTION_SHARE

User shared the selection.

public static final int ACTION_SMART_SHARE

User clicked the textAssist menu item.

public static final int ACTION_DRAG

User dragged+dropped the selection.

public static final int ACTION_ABANDON

User abandoned the selection.

public static final int ACTION_OTHER

User performed an action on the selection.

public static final int ACTION_SELECT_ALL

User activated Select All

public static final int ACTION_RESET

User reset the smart selection.

public static final int EVENT_SELECTION_STARTED

User started a new selection.

public static final int EVENT_SELECTION_MODIFIED

User modified an existing selection.

public static final int EVENT_SMART_SELECTION_SINGLE

Smart selection triggered for a single token (word).

public static final int EVENT_SMART_SELECTION_MULTI

Smart selection triggered spanning multiple tokens (words).

public static final int EVENT_AUTO_SELECTION

Something else other than User or the default TextClassifier triggered a selection.

public static final int INVOCATION_MANUAL

Selection was invoked by the user long pressing, double tapping, or dragging to select.

public static final int INVOCATION_LINK

Selection was invoked by the user tapping on a link.

public static final int INVOCATION_UNKNOWN

Unknown invocation method

Methods

public Bundle toBundle()

Adds this Icon to a Bundle that can be read back with the same parameters to SelectionEvent.createFromBundle(Bundle).

public static SelectionEvent createFromBundle(Bundle bundle)

Extracts a SelectionEvent from a bundle that was added using SelectionEvent.toBundle().

public static SelectionEvent createSelectionStartedEvent(int invocationMethod, int start)

Creates a "selection started" event.

Parameters:

invocationMethod: the way the selection was triggered
start: the index of the selected text

public static SelectionEvent createSelectionModifiedEvent(int start, int end)

Creates a "selection modified" event. Use when the user modifies the selection.

Parameters:

start: the start (inclusive) index of the selection
end: the end (exclusive) index of the selection

public static SelectionEvent createSelectionModifiedEvent(int start, int end, TextClassification classification)

Creates a "selection modified" event. Use when the user modifies the selection and the selection's entity type is known.

Parameters:

start: the start (inclusive) index of the selection
end: the end (exclusive) index of the selection
classification: the TextClassification object returned by the text classifier that classified the selected text

public static SelectionEvent createSelectionModifiedEvent(int start, int end, TextSelection selection)

Creates a "selection modified" event. Use when a TextClassifier modifies the selection.

Parameters:

start: the start (inclusive) index of the selection
end: the end (exclusive) index of the selection
selection: the TextSelection object returned by the text classifier for the specified selection

public static SelectionEvent createSelectionActionEvent(int start, int end, int actionType)

Creates an event specifying an action taken on a selection. Use when the user clicks on an action to act on the selected text.

Parameters:

start: the start (inclusive) index of the selection
end: the end (exclusive) index of the selection
actionType: the action that was performed on the selection

public static SelectionEvent createSelectionActionEvent(int start, int end, int actionType, TextClassification classification)

Creates an event specifying an action taken on a selection. Use when the user clicks on an action to act on the selected text and the selection's entity type is known.

Parameters:

start: the start (inclusive) index of the selection
end: the end (exclusive) index of the selection
actionType: the action that was performed on the selection
classification: the TextClassification object returned by the text classifier that classified the selected text

public int getEventType()

Returns the type of event that was triggered. e.g. SelectionEvent.ACTION_COPY.

public java.lang.String getEntityType()

Returns the type of entity that is associated with this event. e.g. android.view.textclassifier.TextClassifier.

public java.lang.String getPackageName()

Returns the package name of the app that this event originated in.

public java.lang.String getWidgetType()

Returns the type of widget that was involved in triggering this event.

public java.lang.String getWidgetVersion()

Returns a string version info for the widget this event was triggered in.

public int getInvocationMethod()

Returns the way the selection mode was invoked.

public java.lang.String getResultId()

Returns the id of the text classifier result associated with this event.

public long getEventTime()

Returns the time this event was triggered.

public long getDurationSinceSessionStart()

Returns the duration in ms between when this event was triggered and when the first event in the selection session was triggered.

public long getDurationSincePreviousEvent()

Returns the duration in ms between when this event was triggered and when the previous event in the selection session was triggered.

public int getEventIndex()

Returns the index (e.g. 1st event, 2nd event, etc.) of this event in the selection session.

public TextClassificationSessionId getSessionId()

Returns the selection session id.

public int getStart()

Returns the start index of this events relative to the index of the start selection event in the selection session.

public int getEnd()

Returns the end index of this events relative to the index of the start selection event in the selection session.

public int getSmartStart()

Returns the start index of this events relative to the index of the smart selection event in the selection session.

public int getSmartEnd()

Returns the end index of this events relative to the index of the smart selection event in the selection session.

public static boolean isTerminal(int eventType)

Returns true if the eventType is a terminal event type. Otherwise returns false. A terminal event is an event that ends a selection interaction.

public int hashCode()

public boolean equals(java.lang.Object obj)

public java.lang.String toString()

Source

/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.textclassifier;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.util.ObjectsCompat;
import androidx.core.util.Preconditions;
import androidx.textclassifier.TextClassifier.EntityType;
import androidx.textclassifier.TextClassifier.WidgetType;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;

/**
 * A selection event.
 * Specify index parameters as word token indices.
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class SelectionEvent {
    private static final String EXTRA_ABSOLUTE_START = "extra_absolute_start";
    private static final String EXTRA_ABSOLUTE_END = "extra_absolute_end";
    private static final String EXTRA_ENTITY_TYPE = "extra_entity_type";
    private static final String EXTRA_EVENT_TYPE = "extra_event_type";
    private static final String EXTRA_PACKAGE_NAME = "extra_package_name";
    private static final String EXTRA_WIDGET_TYPE = "extra_widget_type";
    private static final String EXTRA_INVOCATION_METHOD = "extra_invocation_method";
    private static final String EXTRA_WIDGET_VERSION = "extra_widget_version";
    private static final String EXTRA_RESULT_ID = "extra_result_id";
    private static final String EXTRA_EVENT_TIME = "extra_event_time";
    private static final String EXTRA_DURATION_SINCE_SESSION_START =
            "extra_duration_since_session_start";
    private static final String EXTRA_DURATION_SINCE_PREVIOUS_EVENT =
            "extra_duration_since_previous_event";
    private static final String EXTRA_EVENT_INDEX = "extra_event_index";
    private static final String EXTRA_SESSION_ID = "extra_session_id";
    private static final String EXTRA_START = "extra_start";
    private static final String EXTRA_END = "extra_end";
    private static final String EXTRA_SMART_START = "extra_smart_start";
    private static final String EXTRA_SMART_END = "extra_smart_end";

    /** User typed over the selection. */
    public static final int ACTION_OVERTYPE =
            android.view.textclassifier.SelectionEvent.ACTION_OVERTYPE;
    /** User copied the selection. */
    public static final int ACTION_COPY = android.view.textclassifier.SelectionEvent.ACTION_COPY;
    /** User pasted over the selection. */
    public static final int ACTION_PASTE = android.view.textclassifier.SelectionEvent.ACTION_PASTE;
    /** User cut the selection. */
    public static final int ACTION_CUT = android.view.textclassifier.SelectionEvent.ACTION_CUT;
    /** User shared the selection. */
    public static final int ACTION_SHARE = android.view.textclassifier.SelectionEvent.ACTION_SHARE;
    /** User clicked the textAssist menu item. */
    public static final int ACTION_SMART_SHARE =
            android.view.textclassifier.SelectionEvent.ACTION_SMART_SHARE;
    /** User dragged+dropped the selection. */
    public static final int ACTION_DRAG = android.view.textclassifier.SelectionEvent.ACTION_DRAG;
    /** User abandoned the selection. */
    public static final int ACTION_ABANDON =
            android.view.textclassifier.SelectionEvent.ACTION_ABANDON;
    /** User performed an action on the selection. */
    public static final int ACTION_OTHER = android.view.textclassifier.SelectionEvent.ACTION_OTHER;

    // Non-terminal actions.
    /** User activated Select All */
    public static final int ACTION_SELECT_ALL =
            android.view.textclassifier.SelectionEvent.ACTION_SELECT_ALL;
    /** User reset the smart selection. */
    public static final int ACTION_RESET = android.view.textclassifier.SelectionEvent.ACTION_RESET;

    /** @hide */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({ACTION_OVERTYPE, ACTION_COPY, ACTION_PASTE, ACTION_CUT,
            ACTION_SHARE, ACTION_SMART_SHARE, ACTION_DRAG, ACTION_ABANDON,
            ACTION_OTHER, ACTION_SELECT_ALL, ACTION_RESET,
            EVENT_SELECTION_STARTED, EVENT_SELECTION_MODIFIED,
            EVENT_SMART_SELECTION_SINGLE, EVENT_SMART_SELECTION_MULTI,
            EVENT_AUTO_SELECTION})
    // NOTE: EventTypes declared here must be less than 100 to avoid colliding with the
    // ActionTypes declared above.
    public @interface EventType {
        /*
         * Range: 1 -> 99.
         */
    }

    /** @hide */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({ACTION_OVERTYPE, ACTION_COPY, ACTION_PASTE, ACTION_CUT,
            ACTION_SHARE, ACTION_SMART_SHARE, ACTION_DRAG, ACTION_ABANDON,
            ACTION_OTHER, ACTION_SELECT_ALL, ACTION_RESET})
    // NOTE: ActionType values should not be lower than 100 to avoid colliding with the other
    // EventTypes declared below.
    public @interface ActionType {
        /*
         * Terminal event types range: [100,200).
         * Non-terminal event types range: [200,300).
         */
    }

    /** User started a new selection. */
    public static final int EVENT_SELECTION_STARTED =
            android.view.textclassifier.SelectionEvent.EVENT_SELECTION_STARTED;
    /** User modified an existing selection. */
    public static final int EVENT_SELECTION_MODIFIED =
            android.view.textclassifier.SelectionEvent.EVENT_SELECTION_MODIFIED;
    /** Smart selection triggered for a single token (word). */
    public static final int EVENT_SMART_SELECTION_SINGLE =
            android.view.textclassifier.SelectionEvent.EVENT_SMART_SELECTION_SINGLE;
    /** Smart selection triggered spanning multiple tokens (words). */
    public static final int EVENT_SMART_SELECTION_MULTI =
            android.view.textclassifier.SelectionEvent.EVENT_SMART_SELECTION_MULTI;
    /** Something else other than User or the default TextClassifier triggered a selection. */
    public static final int EVENT_AUTO_SELECTION =
            android.view.textclassifier.SelectionEvent.EVENT_AUTO_SELECTION;

    /** @hide */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({INVOCATION_MANUAL, INVOCATION_LINK, INVOCATION_UNKNOWN})
    public @interface InvocationMethod {}

    /** Selection was invoked by the user long pressing, double tapping, or dragging to select. */
    public static final int INVOCATION_MANUAL =
            android.view.textclassifier.SelectionEvent.INVOCATION_MANUAL;
    /** Selection was invoked by the user tapping on a link. */
    public static final int INVOCATION_LINK =
            android.view.textclassifier.SelectionEvent.INVOCATION_LINK;
    /** Unknown invocation method */
    public static final int INVOCATION_UNKNOWN =
            android.view.textclassifier.SelectionEvent.INVOCATION_UNKNOWN;

    private static final String NO_SIGNATURE = "";

    private final int mAbsoluteStart;
    private final int mAbsoluteEnd;
    private final @EntityType String mEntityType;

    private @EventType int mEventType;
    private String mPackageName = "";
    private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN;
    private @InvocationMethod int mInvocationMethod;
    @Nullable private String mWidgetVersion;
    @Nullable private String mResultId;
    private long mEventTime;
    private long mDurationSinceSessionStart;
    private long mDurationSincePreviousEvent;
    private int mEventIndex;
    @Nullable private TextClassificationSessionId mSessionId;
    private int mStart;
    private int mEnd;
    private int mSmartStart;
    private int mSmartEnd;

    // For SL -> Platform conversion.
    @Nullable
    private TextClassification mTextClassification;
    @Nullable
    private TextSelection mTextSelection;

    /* package */ SelectionEvent(
            int start, int end,
            @EventType int eventType, @EntityType String entityType,
            @InvocationMethod int invocationMethod, @Nullable String resultId) {
        Preconditions.checkArgument(end >= start, "end cannot be less than start");
        mAbsoluteStart = start;
        mAbsoluteEnd = end;
        mEventType = eventType;
        mEntityType = Preconditions.checkNotNull(entityType);
        mResultId = resultId;
        mInvocationMethod = invocationMethod;
    }

    private SelectionEvent(int absoluteStart, int absoluteEnd, @EntityType String entityType,
            @EventType int eventType, String packageName, String widgetType,
            @InvocationMethod int invocationMethod, String widgetVersion, String resultId,
            long eventTime, long durationSinceSessionStart, long durationSincePreviousEvent,
            int eventIndex, TextClassificationSessionId sessionId, int start, int end,
            int smartStart, int smartEnd) {
        mAbsoluteStart = absoluteStart;
        mAbsoluteEnd = absoluteEnd;
        mEntityType = entityType;
        mEventType = eventType;
        mPackageName = packageName;
        mWidgetType = widgetType;
        mInvocationMethod = invocationMethod;
        mWidgetVersion = widgetVersion;
        mResultId = resultId;
        mEventTime = eventTime;
        mDurationSinceSessionStart = durationSinceSessionStart;
        mDurationSincePreviousEvent = durationSincePreviousEvent;
        mEventIndex = eventIndex;
        mSessionId = sessionId;
        mStart = start;
        mEnd = end;
        mSmartStart = smartStart;
        mSmartEnd = smartEnd;
    }

    /**
     * Adds this Icon to a Bundle that can be read back with the same parameters
     * to {@link #createFromBundle(Bundle)}.
     */
    @NonNull
    public Bundle toBundle() {
        Bundle bundle = new Bundle();
        bundle.putInt(EXTRA_ABSOLUTE_START, mAbsoluteStart);
        bundle.putInt(EXTRA_ABSOLUTE_END, mAbsoluteEnd);
        bundle.putString(EXTRA_ENTITY_TYPE, mEntityType);
        bundle.putInt(EXTRA_EVENT_TYPE, mEventType);
        bundle.putString(EXTRA_PACKAGE_NAME, mPackageName);
        bundle.putString(EXTRA_WIDGET_TYPE, mWidgetType);
        bundle.putInt(EXTRA_INVOCATION_METHOD, mInvocationMethod);
        bundle.putString(EXTRA_WIDGET_VERSION, mWidgetVersion);
        bundle.putString(EXTRA_RESULT_ID, mResultId);
        bundle.putLong(EXTRA_EVENT_TIME, mEventTime);
        bundle.putLong(EXTRA_DURATION_SINCE_SESSION_START, mDurationSinceSessionStart);
        bundle.putLong(EXTRA_DURATION_SINCE_PREVIOUS_EVENT, mDurationSincePreviousEvent);
        bundle.putInt(EXTRA_EVENT_INDEX, mEventIndex);
        if (mSessionId != null) {
            bundle.putBundle(EXTRA_SESSION_ID, mSessionId.toBundle());
        }
        bundle.putInt(EXTRA_START, mStart);
        bundle.putInt(EXTRA_END, mEnd);
        bundle.putInt(EXTRA_SMART_START, mSmartStart);
        bundle.putInt(EXTRA_SMART_END, mSmartEnd);
        return bundle;
    }

    /**
     * Extracts a {@link SelectionEvent} from a bundle that was added using {@link #toBundle()}.
     */
    @NonNull
    public static SelectionEvent createFromBundle(@NonNull Bundle bundle) {
        final int absoluteStart = bundle.getInt(EXTRA_ABSOLUTE_START);
        final int absoluteEnd = bundle.getInt(EXTRA_ABSOLUTE_END);
        final String entityType = bundle.getString(EXTRA_ENTITY_TYPE);
        final int eventType = bundle.getInt(EXTRA_EVENT_TYPE);
        final String packageName = bundle.getString(EXTRA_PACKAGE_NAME);
        final String widgetType = bundle.getString(EXTRA_WIDGET_TYPE);
        final int invocationMethod = bundle.getInt(EXTRA_INVOCATION_METHOD);
        final String widgetVersion = bundle.getString(EXTRA_WIDGET_VERSION);
        final String resultId = bundle.getString(EXTRA_RESULT_ID);
        final long eventTime = bundle.getLong(EXTRA_EVENT_TIME);
        final long durationSinceSessionStart = bundle.getLong(EXTRA_DURATION_SINCE_SESSION_START);
        final long durationSincePreviousEvent = bundle.getLong(EXTRA_DURATION_SINCE_PREVIOUS_EVENT);
        final int eventIndex = bundle.getInt(EXTRA_EVENT_INDEX);
        final TextClassificationSessionId sessionId = bundle.getBundle(EXTRA_SESSION_ID) == null
                ? null
                : TextClassificationSessionId.createFromBundle(bundle.getBundle(EXTRA_SESSION_ID));
        final int start = bundle.getInt(EXTRA_START);
        final int end = bundle.getInt(EXTRA_END);
        final int smartStart = bundle.getInt(EXTRA_SMART_START);
        final int smartEnd = bundle.getInt(EXTRA_SMART_END);
        return new SelectionEvent(absoluteStart, absoluteEnd, entityType, eventType, packageName,
                widgetType, invocationMethod, widgetVersion, resultId, eventTime,
                durationSinceSessionStart, durationSincePreviousEvent, eventIndex,
                sessionId, start, end, smartStart, smartEnd);
    }

    /**
     * Creates a "selection started" event.
     *
     * @param invocationMethod  the way the selection was triggered
     * @param start  the index of the selected text
     */
    @NonNull
    public static SelectionEvent createSelectionStartedEvent(
            @SelectionEvent.InvocationMethod int invocationMethod, int start) {
        return new SelectionEvent(
                start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
                TextClassifier.TYPE_UNKNOWN, invocationMethod, NO_SIGNATURE);
    }

    /**
     * Creates a "selection modified" event.
     * Use when the user modifies the selection.
     *
     * @param start  the start (inclusive) index of the selection
     * @param end  the end (exclusive) index of the selection
     *
     * @throws IllegalArgumentException if end is less than start
     */
    @NonNull
    public static SelectionEvent createSelectionModifiedEvent(int start, int end) {
        Preconditions.checkArgument(end >= start, "end cannot be less than start");
        return new SelectionEvent(
                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
                TextClassifier.TYPE_UNKNOWN, INVOCATION_UNKNOWN, NO_SIGNATURE);
    }

    /**
     * Creates a "selection modified" event.
     * Use when the user modifies the selection and the selection's entity type is known.
     *
     * @param start  the start (inclusive) index of the selection
     * @param end  the end (exclusive) index of the selection
     * @param classification  the TextClassification object returned by the text classifier that
     *      classified the selected text
     *
     * @throws IllegalArgumentException if end is less than start
     */
    @NonNull
    public static SelectionEvent createSelectionModifiedEvent(
            int start, int end, @NonNull TextClassification classification) {
        Preconditions.checkArgument(end >= start, "end cannot be less than start");
        Preconditions.checkNotNull(classification);
        final String entityType = classification.getEntityTypeCount() > 0
                ? classification.getEntityType(0)
                : TextClassifier.TYPE_UNKNOWN;

        SelectionEvent selectionEvent = new SelectionEvent(
                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
                entityType, INVOCATION_UNKNOWN, classification.getId());
        selectionEvent.mTextClassification = classification;
        return selectionEvent;
    }

    /**
     * Creates a "selection modified" event.
     * Use when a TextClassifier modifies the selection.
     *
     * @param start  the start (inclusive) index of the selection
     * @param end  the end (exclusive) index of the selection
     * @param selection  the TextSelection object returned by the text classifier for the
     *      specified selection
     *
     * @throws IllegalArgumentException if end is less than start
     */
    @NonNull
    public static SelectionEvent createSelectionModifiedEvent(
            int start, int end, @NonNull TextSelection selection) {
        Preconditions.checkArgument(end >= start, "end cannot be less than start");
        Preconditions.checkNotNull(selection);
        final String entityType = selection.getEntityTypeCount() > 0
                ? selection.getEntityType(0)
                : TextClassifier.TYPE_UNKNOWN;
        SelectionEvent selectionEvent = new SelectionEvent(
                start, end, SelectionEvent.EVENT_AUTO_SELECTION,
                entityType, INVOCATION_UNKNOWN, selection.getId());
        selectionEvent.mTextSelection = selection;
        return selectionEvent;
    }

    /**
     * Creates an event specifying an action taken on a selection.
     * Use when the user clicks on an action to act on the selected text.
     *
     * @param start  the start (inclusive) index of the selection
     * @param end  the end (exclusive) index of the selection
     * @param actionType  the action that was performed on the selection
     *
     * @throws IllegalArgumentException if end is less than start
     */
    @NonNull
    public static SelectionEvent createSelectionActionEvent(
            int start, int end, @SelectionEvent.ActionType int actionType) {
        Preconditions.checkArgument(end >= start, "end cannot be less than start");
        checkActionType(actionType);
        return new SelectionEvent(
                start, end, actionType, TextClassifier.TYPE_UNKNOWN, INVOCATION_UNKNOWN,
                NO_SIGNATURE);
    }

    /**
     * Creates an event specifying an action taken on a selection.
     * Use when the user clicks on an action to act on the selected text and the selection's
     * entity type is known.
     *
     * @param start  the start (inclusive) index of the selection
     * @param end  the end (exclusive) index of the selection
     * @param actionType  the action that was performed on the selection
     * @param classification  the TextClassification object returned by the text classifier that
     *      classified the selected text
     *
     * @throws IllegalArgumentException if end is less than start
     * @throws IllegalArgumentException If actionType is not a valid SelectionEvent actionType
     */
    @NonNull
    public static SelectionEvent createSelectionActionEvent(
            int start, int end, @SelectionEvent.ActionType int actionType,
            @NonNull TextClassification classification) {
        Preconditions.checkArgument(end >= start, "end cannot be less than start");
        Preconditions.checkNotNull(classification);
        checkActionType(actionType);
        final String entityType = classification.getEntityTypeCount() > 0
                ? classification.getEntityType(0)
                : TextClassifier.TYPE_UNKNOWN;
        SelectionEvent selectionEvent =
                new SelectionEvent(start, end, actionType, entityType, INVOCATION_UNKNOWN,
                        classification.getId());
        selectionEvent.mTextClassification = classification;
        return selectionEvent;
    }

    /**
     * @throws IllegalArgumentException If eventType is not an {@link SelectionEvent.ActionType}
     */
    private static void checkActionType(@SelectionEvent.EventType int eventType)
            throws IllegalArgumentException {
        switch (eventType) {
            case SelectionEvent.ACTION_OVERTYPE:  // fall through
            case SelectionEvent.ACTION_COPY:  // fall through
            case SelectionEvent.ACTION_PASTE:  // fall through
            case SelectionEvent.ACTION_CUT:  // fall through
            case SelectionEvent.ACTION_SHARE:  // fall through
            case SelectionEvent.ACTION_SMART_SHARE:  // fall through
            case SelectionEvent.ACTION_DRAG:  // fall through
            case SelectionEvent.ACTION_ABANDON:  // fall through
            case SelectionEvent.ACTION_SELECT_ALL:  // fall through
            case SelectionEvent.ACTION_RESET:  // fall through
            case SelectionEvent.ACTION_OTHER:  // fall through
                return;
            default:
                throw new IllegalArgumentException(
                        String.format(Locale.US, "%d is not an eventType", eventType));
        }
    }

    int getAbsoluteStart() {
        return mAbsoluteStart;
    }

    int getAbsoluteEnd() {
        return mAbsoluteEnd;
    }

    /**
     * Returns the type of event that was triggered. e.g. {@link #ACTION_COPY}.
     */
    @EventType
    public int getEventType() {
        return mEventType;
    }

    /**
     * Sets the event type.
     */
    void setEventType(@EventType int eventType) {
        mEventType = eventType;
    }

    /**
     * Returns the type of entity that is associated with this event. e.g.
     * {@link android.view.textclassifier.TextClassifier#TYPE_EMAIL}.
     */
    @EntityType
    @NonNull
    public String getEntityType() {
        return mEntityType;
    }

    /**
     * Returns the package name of the app that this event originated in.
     */
    @NonNull
    public String getPackageName() {
        return mPackageName;
    }

    /**
     * Returns the type of widget that was involved in triggering this event.
     */
    @WidgetType
    @NonNull
    public String getWidgetType() {
        return mWidgetType;
    }

    /**
     * Returns a string version info for the widget this event was triggered in.
     */
    @Nullable
    public String getWidgetVersion() {
        return mWidgetVersion;
    }

    /**
     * Sets the {@link TextClassificationContext} for this event.
     */
    void setTextClassificationSessionContext(TextClassificationContext context) {
        mPackageName = context.getPackageName();
        mWidgetType = context.getWidgetType();
        mWidgetVersion = context.getWidgetVersion();
    }

    /**
     * Returns the way the selection mode was invoked.
     */
    public @InvocationMethod int getInvocationMethod() {
        return mInvocationMethod;
    }

    /**
     * Sets the invocationMethod for this event.
     */
    void setInvocationMethod(@InvocationMethod int invocationMethod) {
        mInvocationMethod = invocationMethod;
    }

    /**
     * Returns the id of the text classifier result associated with this event.
     */
    @Nullable
    public String getResultId() {
        return mResultId;
    }

    SelectionEvent setResultId(@Nullable String resultId) {
        mResultId = resultId;
        return this;
    }

    /**
     * Returns the time this event was triggered.
     */
    public long getEventTime() {
        return mEventTime;
    }

    SelectionEvent setEventTime(long timeMs) {
        mEventTime = timeMs;
        return this;
    }

    /**
     * Returns the duration in ms between when this event was triggered and when the first event in
     * the selection session was triggered.
     */
    public long getDurationSinceSessionStart() {
        return mDurationSinceSessionStart;
    }

    SelectionEvent setDurationSinceSessionStart(long durationMs) {
        mDurationSinceSessionStart = durationMs;
        return this;
    }

    /**
     * Returns the duration in ms between when this event was triggered and when the previous event
     * in the selection session was triggered.
     */
    public long getDurationSincePreviousEvent() {
        return mDurationSincePreviousEvent;
    }

    SelectionEvent setDurationSincePreviousEvent(long durationMs) {
        this.mDurationSincePreviousEvent = durationMs;
        return this;
    }

    /**
     * Returns the index (e.g. 1st event, 2nd event, etc.) of this event in the selection session.
     */
    public int getEventIndex() {
        return mEventIndex;
    }

    SelectionEvent setEventIndex(int index) {
        mEventIndex = index;
        return this;
    }

    /**
     * Returns the selection session id.
     */
    @Nullable
    public TextClassificationSessionId getSessionId() {
        return mSessionId;
    }

    SelectionEvent setSessionId(TextClassificationSessionId id) {
        mSessionId = id;
        return this;
    }

    /**
     * Returns the start index of this events relative to the index of the start selection
     * event in the selection session.
     */
    public int getStart() {
        return mStart;
    }

    SelectionEvent setStart(int start) {
        mStart = start;
        return this;
    }

    /**
     * Returns the end index of this events relative to the index of the start selection
     * event in the selection session.
     */
    public int getEnd() {
        return mEnd;
    }

    SelectionEvent setEnd(int end) {
        mEnd = end;
        return this;
    }

    /**
     * Returns the start index of this events relative to the index of the smart selection
     * event in the selection session.
     */
    public int getSmartStart() {
        return mSmartStart;
    }

    SelectionEvent setSmartStart(int start) {
        this.mSmartStart = start;
        return this;
    }

    /**
     * Returns the end index of this events relative to the index of the smart selection
     * event in the selection session.
     */
    public int getSmartEnd() {
        return mSmartEnd;
    }

    SelectionEvent setSmartEnd(int end) {
        mSmartEnd = end;
        return this;
    }

    boolean isTerminal() {
        return isTerminal(mEventType);
    }

    /**
     * Returns true if the eventType is a terminal event type. Otherwise returns false.
     * A terminal event is an event that ends a selection interaction.
     */
    public static boolean isTerminal(@EventType int eventType) {
        switch (eventType) {
            case ACTION_OVERTYPE:  // fall through
            case ACTION_COPY:  // fall through
            case ACTION_PASTE:  // fall through
            case ACTION_CUT:  // fall through
            case ACTION_SHARE:  // fall through
            case ACTION_SMART_SHARE:  // fall through
            case ACTION_DRAG:  // fall through
            case ACTION_ABANDON:  // fall through
            case ACTION_OTHER:  // fall through
                return true;
            default:
                return false;
        }
    }

    @Override
    public int hashCode() {
        return ObjectsCompat.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
                mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId,
                mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
                mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof SelectionEvent)) {
            return false;
        }

        final SelectionEvent other = (SelectionEvent) obj;
        return mAbsoluteStart == other.mAbsoluteStart
                && mAbsoluteEnd == other.mAbsoluteEnd
                && mEventType == other.mEventType
                && ObjectsCompat.equals(mEntityType, other.mEntityType)
                && ObjectsCompat.equals(mWidgetVersion, other.mWidgetVersion)
                && ObjectsCompat.equals(mPackageName, other.mPackageName)
                && ObjectsCompat.equals(mWidgetType, other.mWidgetType)
                && mInvocationMethod == other.mInvocationMethod
                && ObjectsCompat.equals(mResultId, other.mResultId)
                && mEventTime == other.mEventTime
                && mDurationSinceSessionStart == other.mDurationSinceSessionStart
                && mDurationSincePreviousEvent == other.mDurationSincePreviousEvent
                && mEventIndex == other.mEventIndex
                && ObjectsCompat.equals(mSessionId, other.mSessionId)
                && mStart == other.mStart
                && mEnd == other.mEnd
                && mSmartStart == other.mSmartStart
                && mSmartEnd == other.mSmartEnd;
    }

    @Override
    public String toString() {
        return String.format(Locale.US,
                "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, "
                        + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
                        + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
                        + "durationSincePreviousEvent=%d, eventIndex=%d,"
                        + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}",
                mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
                mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
                mResultId, mEventTime, mDurationSinceSessionStart,
                mDurationSincePreviousEvent, mEventIndex,
                mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
    }

    /**
     * Converts {@link android.view.textclassifier.TextSelection} to
     * {@link android.view.textclassifier.TextSelection}. It can only convert text selection
     * objects created by those factory methods and without further modification.
     */
    @SuppressLint("WrongConstant")
    @RequiresApi(28)
    @NonNull
    // Lint does not know the constants in platform and here are same
    Object toPlatform(@NonNull Context context) {
        Preconditions.checkNotNull(context);

        if (getEventType() == EVENT_SELECTION_STARTED) {
            return android.view.textclassifier.SelectionEvent.createSelectionStartedEvent(
                    getInvocationMethod(),
                    getAbsoluteStart()
            );
        }
        if (getEventType() == EVENT_AUTO_SELECTION && mTextSelection != null) {
            return android.view.textclassifier.SelectionEvent.createSelectionModifiedEvent(
                    getAbsoluteStart(),
                    getAbsoluteEnd(),
                    (android.view.textclassifier.TextSelection) mTextSelection.toPlatform());
        }
        if (getEventType() == EVENT_SELECTION_MODIFIED) {
            return toPlatformSelectionModifiedEvent(context);
        }
        return toPlatformSelectionActionEvent(context);
    }

    @NonNull
    @RequiresApi(28)
    private android.view.textclassifier.SelectionEvent toPlatformSelectionModifiedEvent(
            @NonNull Context context) {
        Preconditions.checkNotNull(context);

        if (mTextClassification != null) {
            return android.view.textclassifier.SelectionEvent.createSelectionModifiedEvent(
                    getAbsoluteStart(),
                    getAbsoluteEnd(),
                    (android.view.textclassifier.TextClassification)
                            mTextClassification.toPlatform(context)
            );
        }
        return android.view.textclassifier.SelectionEvent.createSelectionModifiedEvent(
                getAbsoluteStart(),
                getAbsoluteEnd()
        );
    }

    @NonNull
    @RequiresApi(28)
    @SuppressLint("WrongConstant")
    // Lint does not know the constants in platform and here are same
    private android.view.textclassifier.SelectionEvent toPlatformSelectionActionEvent(
            @NonNull Context context) {
        Preconditions.checkNotNull(context);

        if (mTextClassification != null) {
            return android.view.textclassifier.SelectionEvent.createSelectionActionEvent(
                    getAbsoluteStart(),
                    getAbsoluteEnd(),
                    getEventType(),
                    (android.view.textclassifier.TextClassification)
                            mTextClassification.toPlatform(context)
            );
        }
        return android.view.textclassifier.SelectionEvent.createSelectionActionEvent(
                getAbsoluteStart(),
                getAbsoluteEnd(),
                getEventType()
        );
    }
}