Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aeb8a3b1 authored by Abodunrinwa Toki's avatar Abodunrinwa Toki Committed by android-build-merger
Browse files

Merge "Convert TextClassifierEvent to SelectionEvent for logging in the default TC." into qt-dev

am: a578ecce

Change-Id: Icadad2e0afaa3355b260f95d011f0dd577115439
parents ea2bdcd3 a578ecce
Loading
Loading
Loading
Loading
+29 −10
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.os.Parcelable;
import android.view.textclassifier.TextClassifier.EntityType;
import android.view.textclassifier.TextClassifier.WidgetType;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
@@ -115,7 +116,7 @@ public final class SelectionEvent implements Parcelable {
    /** Unknown invocation method */
    public static final int INVOCATION_UNKNOWN = 0;

    private static final String NO_SIGNATURE = "";
    static final String NO_SIGNATURE = "";

    private final int mAbsoluteStart;
    private final int mAbsoluteEnd;
@@ -374,8 +375,10 @@ public final class SelectionEvent implements Parcelable {

    /**
     * Sets the event type.
     * @hide
     */
    void setEventType(@EventType int eventType) {
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void setEventType(@EventType int eventType) {
        mEventType = eventType;
    }

@@ -416,8 +419,10 @@ public final class SelectionEvent implements Parcelable {

    /**
     * Sets the {@link TextClassificationContext} for this event.
     * @hide
     */
    void setTextClassificationSessionContext(TextClassificationContext context) {
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void setTextClassificationSessionContext(TextClassificationContext context) {
        mPackageName = context.getPackageName();
        mWidgetType = context.getWidgetType();
        mWidgetVersion = context.getWidgetVersion();
@@ -432,8 +437,10 @@ public final class SelectionEvent implements Parcelable {

    /**
     * Sets the invocationMethod for this event.
     * @hide
     */
    void setInvocationMethod(@InvocationMethod int invocationMethod) {
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void setInvocationMethod(@InvocationMethod int invocationMethod) {
        mInvocationMethod = invocationMethod;
    }

@@ -495,7 +502,9 @@ public final class SelectionEvent implements Parcelable {
        return mEventIndex;
    }

    SelectionEvent setEventIndex(int index) {
    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public SelectionEvent setEventIndex(int index) {
        mEventIndex = index;
        return this;
    }
@@ -508,7 +517,9 @@ public final class SelectionEvent implements Parcelable {
        return mSessionId;
    }

    SelectionEvent setSessionId(TextClassificationSessionId id) {
    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public SelectionEvent setSessionId(@Nullable TextClassificationSessionId id) {
        mSessionId = id;
        return this;
    }
@@ -521,7 +532,9 @@ public final class SelectionEvent implements Parcelable {
        return mStart;
    }

    SelectionEvent setStart(int start) {
    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public SelectionEvent setStart(int start) {
        mStart = start;
        return this;
    }
@@ -534,7 +547,9 @@ public final class SelectionEvent implements Parcelable {
        return mEnd;
    }

    SelectionEvent setEnd(int end) {
    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public SelectionEvent setEnd(int end) {
        mEnd = end;
        return this;
    }
@@ -547,7 +562,9 @@ public final class SelectionEvent implements Parcelable {
        return mSmartStart;
    }

    SelectionEvent setSmartStart(int start) {
    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public SelectionEvent setSmartStart(int start) {
        this.mSmartStart = start;
        return this;
    }
@@ -560,7 +577,9 @@ public final class SelectionEvent implements Parcelable {
        return mSmartEnd;
    }

    SelectionEvent setSmartEnd(int end) {
    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public SelectionEvent setSmartEnd(int end) {
        mSmartEnd = end;
        return this;
    }
+1 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ final class TextClassificationSession implements TextClassifier {
    @Override
    public void onTextClassifierEvent(TextClassifierEvent event) {
        try {
            event.mHiddenTempSessionId = mSessionId;
            mDelegate.onTextClassifierEvent(event);
        } catch (Exception e) {
            // Avoid crashing for event reporting.
+123 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
@@ -150,6 +151,14 @@ public abstract class TextClassifierEvent implements Parcelable {
    private final ULocale mLocale;
    private final Bundle mExtras;

    /**
     * Session id holder to help with converting this event to the legacy SelectionEvent.
     * @hide
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    @Nullable
    public TextClassificationSessionId mHiddenTempSessionId;

    private TextClassifierEvent(Builder builder) {
        mEventCategory = builder.mEventCategory;
        mEventType = builder.mEventType;
@@ -359,6 +368,120 @@ public abstract class TextClassifierEvent implements Parcelable {
        return out.toString();
    }

    /**
     * Returns a {@link SelectionEvent} equivalent of this event; or {@code null} if it can not be
     * converted to a {@link SelectionEvent}.
     * @hide
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    @Nullable
    public final SelectionEvent toSelectionEvent() {
        final int invocationMethod;
        switch (getEventCategory()) {
            case TextClassifierEvent.CATEGORY_SELECTION:
                invocationMethod = SelectionEvent.INVOCATION_MANUAL;
                break;
            case TextClassifierEvent.CATEGORY_LINKIFY:
                invocationMethod = SelectionEvent.INVOCATION_LINK;
                break;
            default:
                // Cannot be converted to a SelectionEvent.
                return null;
        }

        final String entityType = getEntityTypes().length > 0
                ? getEntityTypes()[0] : TextClassifier.TYPE_UNKNOWN;
        final SelectionEvent out = new SelectionEvent(
                /* absoluteStart= */ 0,
                /* absoluteEnd= */ 0,
                /* eventType= */0,
                entityType,
                SelectionEvent.INVOCATION_UNKNOWN,
                SelectionEvent.NO_SIGNATURE);
        out.setInvocationMethod(invocationMethod);

        final TextClassificationContext eventContext = getEventContext();
        if (eventContext != null) {
            out.setTextClassificationSessionContext(getEventContext());
        }
        out.setSessionId(mHiddenTempSessionId);
        final String resultId = getResultId();
        out.setResultId(resultId == null ? SelectionEvent.NO_SIGNATURE : resultId);
        out.setEventIndex(getEventIndex());


        final int eventType;
        switch (getEventType()) {
            case TextClassifierEvent.TYPE_SELECTION_STARTED:
                eventType = SelectionEvent.EVENT_SELECTION_STARTED;
                break;
            case TextClassifierEvent.TYPE_SELECTION_MODIFIED:
                eventType = SelectionEvent.EVENT_SELECTION_MODIFIED;
                break;
            case TextClassifierEvent.TYPE_SMART_SELECTION_SINGLE:
                eventType = SelectionEvent.EVENT_SMART_SELECTION_SINGLE;
                break;
            case TextClassifierEvent.TYPE_SMART_SELECTION_MULTI:
                eventType = SelectionEvent.EVENT_SMART_SELECTION_MULTI;
                break;
            case TextClassifierEvent.TYPE_AUTO_SELECTION:
                eventType = SelectionEvent.EVENT_AUTO_SELECTION;
                break;
            case TextClassifierEvent.TYPE_OVERTYPE:
                eventType = SelectionEvent.ACTION_OVERTYPE;
                break;
            case TextClassifierEvent.TYPE_COPY_ACTION:
                eventType = SelectionEvent.ACTION_COPY;
                break;
            case TextClassifierEvent.TYPE_PASTE_ACTION:
                eventType = SelectionEvent.ACTION_PASTE;
                break;
            case TextClassifierEvent.TYPE_CUT_ACTION:
                eventType = SelectionEvent.ACTION_CUT;
                break;
            case TextClassifierEvent.TYPE_SHARE_ACTION:
                eventType = SelectionEvent.ACTION_SHARE;
                break;
            case TextClassifierEvent.TYPE_SMART_ACTION:
                eventType = SelectionEvent.ACTION_SMART_SHARE;
                break;
            case TextClassifierEvent.TYPE_SELECTION_DRAG:
                eventType = SelectionEvent.ACTION_DRAG;
                break;
            case TextClassifierEvent.TYPE_SELECTION_DESTROYED:
                eventType = SelectionEvent.ACTION_ABANDON;
                break;
            case TextClassifierEvent.TYPE_OTHER_ACTION:
                eventType = SelectionEvent.ACTION_OTHER;
                break;
            case TextClassifierEvent.TYPE_SELECT_ALL:
                eventType = SelectionEvent.ACTION_SELECT_ALL;
                break;
            case TextClassifierEvent.TYPE_SELECTION_RESET:
                eventType = SelectionEvent.ACTION_RESET;
                break;
            default:
                eventType = 0;
                break;
        }
        out.setEventType(eventType);

        if (this instanceof TextClassifierEvent.TextSelectionEvent) {
            final TextClassifierEvent.TextSelectionEvent selEvent =
                    (TextClassifierEvent.TextSelectionEvent) this;
            // TODO: Ideally, we should have these fields in events of type
            // TextClassifierEvent.TextLinkifyEvent events too but we're now past the API deadline
            // and will have to do with these fields being set only in TextSelectionEvent events.
            // Fix this at the next API bump.
            out.setStart(selEvent.getRelativeWordStartIndex());
            out.setEnd(selEvent.getRelativeWordEndIndex());
            out.setSmartStart(selEvent.getRelativeSuggestedWordStartIndex());
            out.setSmartEnd(selEvent.getRelativeSuggestedWordEndIndex());
        }

        return out;
    }

    /**
     * Builder to build a text classifier event.
     *
+6 −2
Original line number Diff line number Diff line
@@ -376,7 +376,6 @@ public final class TextClassifierImpl implements TextClassifier {
    /** @inheritDoc */
    @Override
    public void onSelectionEvent(SelectionEvent event) {
        Preconditions.checkNotNull(event);
        mSessionLogger.writeEvent(event);
    }

@@ -386,7 +385,12 @@ public final class TextClassifierImpl implements TextClassifier {
            Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]");
        }
        try {
            final SelectionEvent selEvent = event.toSelectionEvent();
            if (selEvent != null) {
                mSessionLogger.writeEvent(selEvent);
            } else {
                mTextClassifierEventTronLogger.writeEvent(event);
            }
        } catch (Exception e) {
            Log.e(LOG_TAG, "Error writing event", e);
        }
+294 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.view.textclassifier;

import static com.google.common.truth.Truth.assertWithMessage;

import android.annotation.Nullable;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public final class TextClassifierEventTest {

    private static final TextClassificationContext TC_CONTEXT =
            new TextClassificationContext.Builder("pkg", TextClassifier.WIDGET_TYPE_TEXTVIEW)
                    .build();

    private static final TextSelection TEXT_SELECTION = new TextSelection.Builder(10, 20)
            .setEntityType(TextClassifier.TYPE_ADDRESS, 1)
            .setId("id1")
            .build();

    private static final TextClassification TEXT_CLASSIFICATION = new TextClassification.Builder()
            .setEntityType(TextClassifier.TYPE_DATE, 1)
            .setId("id2")
            .build();

    @Test
    public void toSelectionEvent_selectionStarted() {
        final TextClassificationSessionId sessionId = new TextClassificationSessionId();
        final SelectionEvent expected = SelectionEvent.createSelectionStartedEvent(
                SelectionEvent.INVOCATION_MANUAL, 0);
        expected.setTextClassificationSessionContext(TC_CONTEXT);
        expected.setSessionId(sessionId);

        final TextClassifierEvent event = new TextClassifierEvent.TextSelectionEvent.Builder(
                TextClassifierEvent.TYPE_SELECTION_STARTED)
                .setEventContext(TC_CONTEXT)
                .build();
        event.mHiddenTempSessionId = sessionId;

        assertEquals(expected, event.toSelectionEvent());
    }

    @Test
    public void toSelectionEvent_smartSelectionMulti() {
        final int start = -1;
        final int end = 2;
        final int eventIndex = 1;
        final SelectionEvent expected = SelectionEvent.createSelectionModifiedEvent(
                0, 3, TEXT_SELECTION);
        expected.setInvocationMethod(SelectionEvent.INVOCATION_MANUAL);
        expected.setEventType(SelectionEvent.EVENT_SMART_SELECTION_MULTI);
        expected.setStart(start);
        expected.setEnd(end);
        expected.setEventIndex(eventIndex);
        expected.setTextClassificationSessionContext(TC_CONTEXT);

        final String entityType = TEXT_SELECTION.getEntity(0);
        final TextClassifierEvent event = new TextClassifierEvent.TextSelectionEvent.Builder(
                TextClassifierEvent.TYPE_SMART_SELECTION_MULTI)
                .setEventContext(TC_CONTEXT)
                .setResultId(TEXT_SELECTION.getId())
                .setRelativeWordStartIndex(start)
                .setRelativeWordEndIndex(end)
                .setEntityTypes(entityType)
                .setScores(TEXT_SELECTION.getConfidenceScore(entityType))
                .setEventIndex(eventIndex)
                .build();

        assertEquals(expected, event.toSelectionEvent());
    }

    @Test
    public void toSelectionEvent_smartSelectionSingle() {
        final int start = 0;
        final int end = 1;
        final int eventIndex = 2;
        final SelectionEvent expected = SelectionEvent.createSelectionModifiedEvent(
                0, 1, TEXT_SELECTION);
        expected.setInvocationMethod(SelectionEvent.INVOCATION_MANUAL);
        expected.setEventType(SelectionEvent.EVENT_SMART_SELECTION_SINGLE);
        expected.setStart(start);
        expected.setEnd(end);
        expected.setEventIndex(eventIndex);
        expected.setTextClassificationSessionContext(TC_CONTEXT);

        final String entityType = TEXT_SELECTION.getEntity(0);
        final TextClassifierEvent event = new TextClassifierEvent.TextSelectionEvent.Builder(
                TextClassifierEvent.TYPE_SMART_SELECTION_SINGLE)
                .setEventContext(TC_CONTEXT)
                .setResultId(TEXT_SELECTION.getId())
                .setRelativeWordStartIndex(start)
                .setRelativeWordEndIndex(end)
                .setEntityTypes(entityType)
                .setScores(TEXT_SELECTION.getConfidenceScore(entityType))
                .setEventIndex(eventIndex)
                .build();

        assertEquals(expected, event.toSelectionEvent());
    }

    @Test
    public void toSelectionEvent_resetSelection() {
        final int start = 0;
        final int end = 1;
        final int smartStart = -1;
        final int smartEnd = 2;
        final int eventIndex = 3;
        final SelectionEvent expected = SelectionEvent.createSelectionActionEvent(
                0, 1, SelectionEvent.ACTION_RESET, TEXT_CLASSIFICATION);
        expected.setInvocationMethod(SelectionEvent.INVOCATION_MANUAL);
        expected.setStart(start);
        expected.setEnd(end);
        expected.setSmartStart(smartStart);
        expected.setSmartEnd(smartEnd);
        expected.setEventIndex(eventIndex);
        expected.setTextClassificationSessionContext(TC_CONTEXT);

        final String entityType = TEXT_CLASSIFICATION.getEntity(0);
        final TextClassifierEvent event = new TextClassifierEvent.TextSelectionEvent.Builder(
                TextClassifierEvent.TYPE_SELECTION_RESET)
                .setEventContext(TC_CONTEXT)
                .setResultId(TEXT_CLASSIFICATION.getId())
                .setRelativeSuggestedWordStartIndex(smartStart)
                .setRelativeSuggestedWordEndIndex(smartEnd)
                .setRelativeWordStartIndex(start)
                .setRelativeWordEndIndex(end)
                .setEntityTypes(TEXT_CLASSIFICATION.getEntity(0))
                .setScores(TEXT_CLASSIFICATION.getConfidenceScore(entityType))
                .setEventIndex(eventIndex)
                .build();

        assertEquals(expected, event.toSelectionEvent());
    }

    @Test
    public void toSelectionEvent_modifySelection() {
        final int start = -1;
        final int end = 5;
        final int eventIndex = 4;
        final SelectionEvent expected = SelectionEvent.createSelectionModifiedEvent(0, 1);
        expected.setInvocationMethod(SelectionEvent.INVOCATION_MANUAL);
        expected.setStart(start);
        expected.setEnd(end);
        expected.setEventIndex(eventIndex);
        expected.setTextClassificationSessionContext(TC_CONTEXT);

        final TextClassifierEvent event = new TextClassifierEvent.TextSelectionEvent.Builder(
                TextClassifierEvent.TYPE_SELECTION_MODIFIED)
                .setEventContext(TC_CONTEXT)
                .setRelativeWordStartIndex(start)
                .setRelativeWordEndIndex(end)
                .setEventIndex(eventIndex)
                .build();

        assertEquals(expected, event.toSelectionEvent());
    }

    @Test
    public void toSelectionEvent_copyAction() {
        final int start = 3;
        final int end = 4;
        final int eventIndex = 5;
        final SelectionEvent expected = SelectionEvent.createSelectionActionEvent(
                5, 6, SelectionEvent.ACTION_COPY);
        expected.setInvocationMethod(SelectionEvent.INVOCATION_MANUAL);
        expected.setStart(start);
        expected.setEnd(end);
        expected.setEventIndex(eventIndex);
        expected.setTextClassificationSessionContext(TC_CONTEXT);

        final TextClassifierEvent event = new TextClassifierEvent.TextSelectionEvent.Builder(
                TextClassifierEvent.TYPE_COPY_ACTION)
                .setEventContext(TC_CONTEXT)
                .setRelativeWordStartIndex(start)
                .setRelativeWordEndIndex(end)
                .setEventIndex(eventIndex)
                .build();

        assertEquals(expected, event.toSelectionEvent());
    }

    @Test
    public void toSelectionEvent_selectionDismissed() {
        final int eventIndex = 6;
        final SelectionEvent expected = SelectionEvent.createSelectionActionEvent(
                0, 1, SelectionEvent.ACTION_ABANDON);
        expected.setInvocationMethod(SelectionEvent.INVOCATION_MANUAL);
        expected.setEventIndex(eventIndex);

        final TextClassifierEvent event = new TextClassifierEvent.TextSelectionEvent.Builder(
                TextClassifierEvent.TYPE_SELECTION_DESTROYED)
                .setEventIndex(eventIndex)
                .build();

        assertEquals(expected, event.toSelectionEvent());
    }

    @Test
    public void toSelectionEvent_link_smartAction() {
        final int eventIndex = 2;
        final SelectionEvent expected = SelectionEvent.createSelectionActionEvent(
                1, 9, SelectionEvent.ACTION_SMART_SHARE, TEXT_CLASSIFICATION);
        expected.setInvocationMethod(SelectionEvent.INVOCATION_LINK);
        // TODO: TextLinkifyEvent API is missing APIs to set text indices. See related comment in
        // TextClassifierEvent.
        expected.setEventIndex(eventIndex);
        expected.setTextClassificationSessionContext(TC_CONTEXT);

        final String entityType = TEXT_CLASSIFICATION.getEntity(0);
        final TextClassifierEvent event = new TextClassifierEvent.TextLinkifyEvent.Builder(
                TextClassifierEvent.TYPE_SMART_ACTION)
                .setEventContext(TC_CONTEXT)
                .setResultId(TEXT_CLASSIFICATION.getId())
                .setEntityTypes(entityType)
                .setScores(TEXT_CLASSIFICATION.getConfidenceScore(entityType))
                .setActionIndices(0)
                .setEventIndex(eventIndex)
                .build();

        assertEquals(expected, event.toSelectionEvent());
    }

    @Test
    public void toSelectionEvent_nonSelectionOrLinkifyEvent() {
        final TextClassifierEvent convActionEvent =
                new TextClassifierEvent.ConversationActionsEvent.Builder(
                        TextClassifierEvent.TYPE_ACTIONS_GENERATED)
                        .build();
        assertWithMessage("conversation action event")
                .that(convActionEvent.toSelectionEvent()).isNull();

        final TextClassifierEvent langDetEvent =
                new TextClassifierEvent.ConversationActionsEvent.Builder(
                        TextClassifierEvent.TYPE_ACTIONS_GENERATED)
                        .setEventContext(TC_CONTEXT)
                        .build();
        assertWithMessage("language detection event")
                .that(langDetEvent.toSelectionEvent()).isNull();
    }

    private static void assertEquals(
            @Nullable SelectionEvent expected, @Nullable SelectionEvent actual) {
        if (expected == null && actual == null) return;
        if (expected == actual) return;
        assertWithMessage("actual").that(actual).isNotNull();
        assertWithMessage("expected").that(expected).isNotNull();
        assertWithMessage("eventType")
                .that(actual.getEventType()).isEqualTo(expected.getEventType());
        assertWithMessage("packageName")
                .that(actual.getPackageName()).isEqualTo(expected.getPackageName());
        assertWithMessage("widgetType")
                .that(actual.getWidgetType()).isEqualTo(expected.getWidgetType());
        assertWithMessage("widgetVersion")
                .that(actual.getWidgetVersion()).isEqualTo(expected.getWidgetVersion());
        assertWithMessage("invocationMethod")
                .that(actual.getInvocationMethod()).isEqualTo(expected.getInvocationMethod());
        assertWithMessage("resultId")
                .that(actual.getResultId()).isEqualTo(expected.getResultId());
        assertWithMessage("sessionId")
                .that(actual.getSessionId()).isEqualTo(expected.getSessionId());
        assertWithMessage("entityType")
                .that(actual.getEntityType()).isEqualTo(expected.getEntityType());
        assertWithMessage("eventIndex")
                .that(actual.getEventIndex()).isEqualTo(expected.getEventIndex());
        assertWithMessage("start")
                .that(actual.getStart()).isEqualTo(expected.getStart());
        assertWithMessage("end")
                .that(actual.getEnd()).isEqualTo(expected.getEnd());
        assertWithMessage("smartStart")
                .that(actual.getSmartStart()).isEqualTo(expected.getSmartStart());
        assertWithMessage("smartEnd")
                .that(actual.getSmartEnd()).isEqualTo(expected.getSmartEnd());
    }
}