Merge "TRON: Count smart selection events." into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6bf9b9a5f3
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3925,6 +3925,8 @@ public class Editor {
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
getSelectionActionModeHelper().onSelectionAction();
|
||||
|
||||
if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user