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

This commit is contained in:
TreeHugger Robot
2017-05-12 11:54:32 +00:00
committed by Android (Google) Code Review
5 changed files with 147 additions and 26 deletions

View File

@@ -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) {}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -3925,6 +3925,8 @@ public class Editor {
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
getSelectionActionModeHelper().onSelectionAction();
if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
return true;
}

View File

@@ -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);