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

Commit 6bf9b9a5 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "TRON: Count smart selection events." into oc-dev

parents 152f6c8e 1d775575
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -140,4 +140,14 @@ public interface TextClassifier {
    @WorkerThread
    LinksInfo getLinks(
            @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales);

    /**
     * Logs a TextClassifier event.
     *
     * @param source the text classifier used to generate this event
     * @param event the text classifier related event
     * @hide
     */
    @WorkerThread
    default void logEvent(String source, String event) {}
}
+12 −1
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.view.View;
import android.widget.TextViewMetrics;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.Preconditions;

import java.io.File;
@@ -77,6 +78,8 @@ final class TextClassifierImpl implements TextClassifier {

    private final Context mContext;

    private final MetricsLogger mMetricsLogger = new MetricsLogger();

    private final Object mSmartSelectionLock = new Object();
    @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
    private Map<Locale, String> mModelFilePaths;
@@ -105,7 +108,8 @@ final class TextClassifierImpl implements TextClassifier {
                if (start <= end
                        && start >= 0 && end <= string.length()
                        && start <= selectionStartIndex && end >= selectionEndIndex) {
                    final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
                    final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end)
                            .setLogSource(LOG_TAG);
                    final SmartSelection.ClassificationResult[] results =
                            smartSelection.classifyText(
                                    string, start, end,
@@ -173,6 +177,13 @@ final class TextClassifierImpl implements TextClassifier {
        return TextClassifier.NO_OP.getLinks(text, linkMask, defaultLocales);
    }

    @Override
    public void logEvent(String source, String event) {
        if (LOG_TAG.equals(source)) {
            mMetricsLogger.count(event, 1);
        }
    }

    private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
        synchronized (mSmartSelectionLock) {
            localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
+23 −2
Original line number Diff line number Diff line
@@ -34,13 +34,16 @@ public final class TextSelection {
    private final int mEndIndex;
    @NonNull private final EntityConfidence<String> mEntityConfidence;
    @NonNull private final List<String> mEntities;
    @NonNull private final String mLogSource;

    private TextSelection(
            int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence) {
            int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence,
            @NonNull String logSource) {
        mStartIndex = startIndex;
        mEndIndex = endIndex;
        mEntityConfidence = new EntityConfidence<>(entityConfidence);
        mEntities = mEntityConfidence.getEntities();
        mLogSource = logSource;
    }

    /**
@@ -87,6 +90,14 @@ public final class TextSelection {
        return mEntityConfidence.getConfidenceScore(entity);
    }

    /**
     * Returns a tag for the source classifier used to generate this result.
     * @hide
     */
    public String getSourceClassifier() {
        return mLogSource;
    }

    @Override
    public String toString() {
        return String.format("TextSelection {%d, %d, %s}",
@@ -102,6 +113,7 @@ public final class TextSelection {
        private final int mEndIndex;
        @NonNull private final EntityConfidence<String> mEntityConfidence =
                new EntityConfidence<>();
        @NonNull private String mLogSource = "";

        /**
         * Creates a builder used to build {@link TextSelection} objects.
@@ -130,11 +142,20 @@ public final class TextSelection {
            return this;
        }

        /**
         * Sets a tag for the source classifier used to generate this result.
         * @hide
         */
        Builder setLogSource(@NonNull String logSource) {
            mLogSource = Preconditions.checkNotNull(logSource);
            return this;
        }

        /**
         * Builds and returns {@link TextSelection} object.
         */
        public TextSelection build() {
            return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence);
            return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence, mLogSource);
        }
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -3925,6 +3925,8 @@ public class Editor {

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            getSelectionActionModeHelper().onSelectionAction();

            if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
                return true;
            }
+100 −23
Original line number Diff line number Diff line
@@ -56,13 +56,14 @@ final class SelectionActionModeHelper {
    private TextClassification mTextClassification;
    private AsyncTask mTextClassificationAsyncTask;

    private final SelectionInfo mSelectionInfo = new SelectionInfo();
    private final SelectionTracker mSelectionTracker;

    SelectionActionModeHelper(@NonNull Editor editor) {
        mEditor = Preconditions.checkNotNull(editor);
        final TextView textView = mEditor.getTextView();
        mTextClassificationHelper = new TextClassificationHelper(
                textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales());
        mSelectionTracker = new SelectionTracker(textView.getTextClassifier());
    }

    public void startActionModeAsync(boolean adjustSelection) {
@@ -99,8 +100,13 @@ final class SelectionActionModeHelper {
        }
    }

    public void onSelectionAction() {
        mSelectionTracker.onSelectionAction(mTextClassificationHelper.getClassifierTag());
    }

    public boolean resetSelection(int textIndex) {
        if (mSelectionInfo.resetSelection(textIndex, mEditor)) {
        if (mSelectionTracker.resetSelection(
                textIndex, mEditor, mTextClassificationHelper.getClassifierTag())) {
            invalidateActionModeAsync();
            return true;
        }
@@ -113,7 +119,7 @@ final class SelectionActionModeHelper {
    }

    public void onDestroyActionMode() {
        mSelectionInfo.onSelectionDestroyed();
        mSelectionTracker.onSelectionDestroyed();
        cancelAsyncTask();
    }

@@ -137,7 +143,7 @@ final class SelectionActionModeHelper {
    private void startActionMode(@Nullable SelectionResult result) {
        final TextView textView = mEditor.getTextView();
        final CharSequence text = textView.getText();
        mSelectionInfo.setOriginalSelection(
        mSelectionTracker.setOriginalSelection(
                textView.getSelectionStart(), textView.getSelectionEnd());
        if (result != null && text instanceof Spannable) {
            Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
@@ -151,7 +157,8 @@ final class SelectionActionModeHelper {
                controller.show();
            }
            if (result != null) {
                mSelectionInfo.onSelectionStarted(result.mStart, result.mEnd);
                mSelectionTracker.onSelectionStarted(
                        result.mStart, result.mEnd, mTextClassificationHelper.getClassifierTag());
            }
        }
        mEditor.setRestartActionModeOnNextRefresh(false);
@@ -165,7 +172,9 @@ final class SelectionActionModeHelper {
            actionMode.invalidate();
        }
        final TextView textView = mEditor.getTextView();
        mSelectionInfo.onSelectionUpdated(textView.getSelectionStart(), textView.getSelectionEnd());
        mSelectionTracker.onSelectionUpdated(
                textView.getSelectionStart(), textView.getSelectionEnd(),
                mTextClassificationHelper.getClassifierTag());
        mTextClassificationAsyncTask = null;
    }

@@ -177,49 +186,111 @@ final class SelectionActionModeHelper {
    }

    /**
     * Holds information about the selection and uses it to decide on whether or not to update
     * the selection when resetSelection is called.
     * The expected UX here is to allow the user to select a word inside of the "smart selection" on
     * a single tap.
     * Tracks and logs smart selection changes.
     * It is important to trigger this object's methods at the appropriate event so that it tracks
     * smart selection events appropriately.
     */
    private static final class SelectionInfo {
    private static final class SelectionTracker {

        // Log event: Smart selection happened.
        private static final String LOG_EVENT_MULTI_SELECTION =
                "textClassifier_multiSelection";

        // Log event: Smart selection acted upon.
        private static final String LOG_EVENT_MULTI_SELECTION_ACTION =
                "textClassifier_multiSelection_action";

        // Log event: Smart selection was reset to original selection.
        private static final String LOG_EVENT_MULTI_SELECTION_RESET =
                "textClassifier_multiSelection_reset";

        // Log event: Smart selection was user modified.
        private static final String LOG_EVENT_MULTI_SELECTION_MODIFIED =
                "textClassifier_multiSelection_modified";

        private final TextClassifier mClassifier;

        private int mOriginalStart;
        private int mOriginalEnd;
        private int mSelectionStart;
        private int mSelectionEnd;

        private boolean mResetOriginal;
        private boolean mSmartSelectionActive;

        SelectionTracker(TextClassifier classifier) {
            mClassifier = classifier;
        }

        /**
         * Called to initialize the original selection before smart selection is triggered.
         */
        public void setOriginalSelection(int selectionStart, int selectionEnd) {
            mOriginalStart = selectionStart;
            mOriginalEnd = selectionEnd;
            mResetOriginal = false;
            mSmartSelectionActive = false;
        }

        public void onSelectionStarted(int selectionStart, int selectionEnd) {
            // Set the reset flag to true if the selection changed.
        /**
         * Called when selection action mode is started.
         * If the selection indices are different from the original selection indices, we have a
         * smart selection.
         */
        public void onSelectionStarted(int selectionStart, int selectionEnd, String logTag) {
            mSelectionStart = selectionStart;
            mSelectionEnd = selectionEnd;
            mResetOriginal = mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
            // If the started selection is different from the original selection, we have a
            // smart selection.
            mSmartSelectionActive =
                    mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
            if (mSmartSelectionActive) {
                mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION);
            }
        }

        public void onSelectionUpdated(int selectionStart, int selectionEnd) {
            // If the selection did not change, maintain the reset state. Otherwise, disable reset.
            mResetOriginal &= selectionStart == mSelectionStart && selectionEnd == mSelectionEnd;
        /**
         * Called when selection bounds change.
         */
        public void onSelectionUpdated(int selectionStart, int selectionEnd, String logTag) {
            final boolean selectionChanged =
                    selectionStart != mSelectionStart || selectionEnd != mSelectionEnd;
            if (selectionChanged) {
                if (mSmartSelectionActive) {
                    mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_MODIFIED);
                }
                mSmartSelectionActive = false;
            }
        }

        /**
         * Called when the selection action mode is destroyed.
         */
        public void onSelectionDestroyed() {
            mResetOriginal = false;
            mSmartSelectionActive = false;
        }

        public boolean resetSelection(int textIndex, Editor editor) {
        /**
         * Logs if the action was taken on a smart selection.
         */
        public void onSelectionAction(String logTag) {
            if (mSmartSelectionActive) {
                mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_ACTION);
            }
        }

        /**
         * Returns true if the current smart selection should be reset to normal selection based on
         * information that has been recorded about the original selection and the smart selection.
         * The expected UX here is to allow the user to select a word inside of the smart selection
         * on a single tap.
         */
        public boolean resetSelection(int textIndex, Editor editor, String logTag) {
            final CharSequence text = editor.getTextView().getText();
            if (mResetOriginal
            if (mSmartSelectionActive
                    && textIndex >= mSelectionStart && textIndex <= mSelectionEnd
                    && text instanceof Spannable) {
                // Only allow a reset once.
                mResetOriginal = false;
                mSmartSelectionActive = false;
                mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_RESET);
                return editor.selectCurrentWord();
            }
            return false;
@@ -301,6 +372,7 @@ final class SelectionActionModeHelper {
        /** End index relative to mText. */
        private int mSelectionEnd;
        private LocaleList mLocales;
        private String mClassifierTag = "";

        /** Trimmed text starting from mTrimStart in mText. */
        private CharSequence mTrimmedText;
@@ -364,9 +436,14 @@ final class SelectionActionModeHelper {
                    mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
            mSelectionStart = Math.max(0, sel.getSelectionStartIndex() + mTrimStart);
            mSelectionEnd = Math.min(mText.length(), sel.getSelectionEndIndex() + mTrimStart);
            mClassifierTag = sel.getSourceClassifier();
            return classifyText();
        }

        String getClassifierTag() {
            return mClassifierTag;
        }

        private void trimText() {
            mTrimStart = Math.max(0, mSelectionStart - TRIM_DELTA);
            final int referenceEnd = Math.min(mText.length(), mSelectionEnd + TRIM_DELTA);