TextView text selection interaction logging.
TODO: Log typing over selection
Bug: 64914512
Test: bit FrameworksCoreTests:android.widget.TextViewActivityTest
Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest
Test: bit CtsAccessibilityServiceTestCases:android.accessibilityservice.cts.AccessibilityTextTraversalTest
Merged-In: Icc9d5b542140bdb01b2ad4211048bd83eae2ce83
Change-Id: Icc9d5b542140bdb01b2ad4211048bd83eae2ce83
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 04a8265..a20ce4e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1066,6 +1066,8 @@
}
private void startDragAndDrop() {
+ getSelectionActionModeHelper().onSelectionDrag();
+
// TODO: Fix drag and drop in full screen extracted mode.
if (mTextView.isInExtractedMode()) {
return;
@@ -3940,7 +3942,7 @@
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- getSelectionActionModeHelper().onSelectionAction();
+ getSelectionActionModeHelper().onSelectionAction(item.getItemId());
if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
return true;
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 3f4ce44..1285c10 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -20,19 +20,24 @@
import android.annotation.Nullable;
import android.annotation.UiThread;
import android.annotation.WorkerThread;
+import android.content.Context;
import android.os.AsyncTask;
import android.os.LocaleList;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
+import android.util.Log;
import android.view.ActionMode;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextSelection;
+import android.view.textclassifier.logging.SmartSelectionEventTracker;
+import android.view.textclassifier.logging.SmartSelectionEventTracker.SelectionEvent;
import android.widget.Editor.SelectionModifierCursorController;
import com.android.internal.util.Preconditions;
+import java.text.BreakIterator;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -51,6 +56,7 @@
private static final int TIMEOUT_DURATION = 200;
private final Editor mEditor;
+ private final TextView mTextView;
private final TextClassificationHelper mTextClassificationHelper;
private TextClassification mTextClassification;
@@ -60,21 +66,27 @@
SelectionActionModeHelper(@NonNull Editor editor) {
mEditor = Preconditions.checkNotNull(editor);
- final TextView textView = mEditor.getTextView();
+ mTextView = mEditor.getTextView();
mTextClassificationHelper = new TextClassificationHelper(
- textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales());
- mSelectionTracker = new SelectionTracker(textView.getTextClassifier());
+ mTextView.getTextClassifier(), mTextView.getText(),
+ 0, 1, mTextView.getTextLocales());
+ mSelectionTracker =
+ new SelectionTracker(mTextView.getContext(), mTextView.isTextEditable());
}
public void startActionModeAsync(boolean adjustSelection) {
+ mSelectionTracker.onOriginalSelection(
+ mTextView.getText(),
+ mTextView.getSelectionStart(),
+ mTextView.getSelectionEnd(),
+ mTextView.isTextEditable());
cancelAsyncTask();
if (skipTextClassification()) {
startActionMode(null);
} else {
- resetTextClassificationHelper(true /* resetSelectionTag */);
- final TextView tv = mEditor.getTextView();
+ resetTextClassificationHelper();
mTextClassificationAsyncTask = new TextClassificationAsyncTask(
- tv,
+ mTextView,
TIMEOUT_DURATION,
adjustSelection
? mTextClassificationHelper::suggestSelection
@@ -89,21 +101,36 @@
if (skipTextClassification()) {
invalidateActionMode(null);
} else {
- resetTextClassificationHelper(false /* resetSelectionTag */);
+ resetTextClassificationHelper();
mTextClassificationAsyncTask = new TextClassificationAsyncTask(
- mEditor.getTextView(), TIMEOUT_DURATION,
- mTextClassificationHelper::classifyText, this::invalidateActionMode)
+ mTextView,
+ TIMEOUT_DURATION,
+ mTextClassificationHelper::classifyText,
+ this::invalidateActionMode)
.execute();
}
}
- public void onSelectionAction() {
- mSelectionTracker.onSelectionAction(mTextClassificationHelper.getSelectionTag());
+ public void onSelectionAction(int menuItemId) {
+ mSelectionTracker.onSelectionAction(
+ mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
+ getActionType(menuItemId), mTextClassification);
+ }
+
+ public void onSelectionDrag() {
+ mSelectionTracker.onSelectionAction(
+ mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
+ SelectionEvent.ActionType.DRAG, mTextClassification);
+ }
+
+ public void onTypeOverSelection() {
+ mSelectionTracker.onSelectionAction(
+ mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
+ SelectionEvent.ActionType.OVERTYPE, mTextClassification);
}
public boolean resetSelection(int textIndex) {
- if (mSelectionTracker.resetSelection(
- textIndex, mEditor, mTextClassificationHelper.getSelectionTag())) {
+ if (mSelectionTracker.resetSelection(textIndex, mEditor)) {
invalidateActionModeAsync();
return true;
}
@@ -129,22 +156,18 @@
}
private boolean skipTextClassification() {
- final TextView textView = mEditor.getTextView();
// No need to make an async call for a no-op TextClassifier.
- final boolean noOpTextClassifier = textView.getTextClassifier() == TextClassifier.NO_OP;
+ final boolean noOpTextClassifier = mTextView.getTextClassifier() == TextClassifier.NO_OP;
// Do not call the TextClassifier if there is no selection.
- final boolean noSelection = textView.getSelectionEnd() == textView.getSelectionStart();
+ final boolean noSelection = mTextView.getSelectionEnd() == mTextView.getSelectionStart();
// Do not call the TextClassifier if this is a password field.
- final boolean password = textView.hasPasswordTransformationMethod()
- || TextView.isPasswordInputType(textView.getInputType());
+ final boolean password = mTextView.hasPasswordTransformationMethod()
+ || TextView.isPasswordInputType(mTextView.getInputType());
return noOpTextClassifier || noSelection || password;
}
private void startActionMode(@Nullable SelectionResult result) {
- final TextView textView = mEditor.getTextView();
- final CharSequence text = textView.getText();
- mSelectionTracker.setOriginalSelection(
- textView.getSelectionStart(), textView.getSelectionEnd());
+ final CharSequence text = mTextView.getText();
if (result != null && text instanceof Spannable) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
mTextClassification = result.mClassification;
@@ -157,8 +180,7 @@
controller.show();
}
if (result != null) {
- mSelectionTracker.onSelectionStarted(
- result.mStart, result.mEnd, mTextClassificationHelper.getSelectionTag());
+ mSelectionTracker.onSmartSelection(result);
}
}
mEditor.setRestartActionModeOnNextRefresh(false);
@@ -171,18 +193,15 @@
if (actionMode != null) {
actionMode.invalidate();
}
- final TextView textView = mEditor.getTextView();
mSelectionTracker.onSelectionUpdated(
- textView.getSelectionStart(), textView.getSelectionEnd(),
- mTextClassificationHelper.getSelectionTag());
+ mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mTextClassification);
mTextClassificationAsyncTask = null;
}
- private void resetTextClassificationHelper(boolean resetSelectionTag) {
- final TextView textView = mEditor.getTextView();
- mTextClassificationHelper.reset(textView.getTextClassifier(), textView.getText(),
- textView.getSelectionStart(), textView.getSelectionEnd(),
- resetSelectionTag, textView.getTextLocales());
+ private void resetTextClassificationHelper() {
+ mTextClassificationHelper.reset(mTextView.getTextClassifier(), mTextView.getText(),
+ mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
+ mTextView.getTextLocales());
}
/**
@@ -192,84 +211,56 @@
*/
private static final class SelectionTracker {
- // Log event: Smart selection happened.
- private static final String LOG_EVENT_MULTI_SELECTION =
- "textClassifier_multiSelection";
- private static final String LOG_EVENT_SINGLE_SELECTION =
- "textClassifier_singleSelection";
-
- // Log event: Smart selection acted upon.
- private static final String LOG_EVENT_MULTI_SELECTION_ACTION =
- "textClassifier_multiSelection_action";
- private static final String LOG_EVENT_SINGLE_SELECTION_ACTION =
- "textClassifier_singleSelection_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 static final String LOG_EVENT_SINGLE_SELECTION_MODIFIED =
- "textClassifier_singleSelection_modified";
-
- private final TextClassifier mClassifier;
+ private final Context mContext;
+ private SelectionMetricsLogger mLogger;
private int mOriginalStart;
private int mOriginalEnd;
private int mSelectionStart;
private int mSelectionEnd;
+ private boolean mSelectionStarted;
+ private boolean mAllowReset;
- private boolean mMultiSelection;
- private boolean mClassifierSelection;
-
- SelectionTracker(TextClassifier classifier) {
- mClassifier = classifier;
+ SelectionTracker(Context context, boolean editable) {
+ mContext = Preconditions.checkNotNull(context);
+ mLogger = new SelectionMetricsLogger(context, editable);
}
/**
- * Called to initialize the original selection before smart selection is triggered.
+ * Called when the original selection happens, before smart selection is triggered.
*/
- public void setOriginalSelection(int selectionStart, int selectionEnd) {
+ public void onOriginalSelection(
+ CharSequence text, int selectionStart, int selectionEnd, boolean editableText) {
mOriginalStart = selectionStart;
mOriginalEnd = selectionEnd;
- resetSelectionFlags();
+ mSelectionStarted = true;
+ mAllowReset = false;
+ maybeInvalidateLogger(editableText);
+ mLogger.logSelectionStarted(text, selectionStart);
}
/**
* Called when selection action mode is started and the results come from a classifier.
- * 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) {
- mClassifierSelection = !logTag.isEmpty();
- mSelectionStart = selectionStart;
- mSelectionEnd = selectionEnd;
- // If the started selection is different from the original selection, we have a
- // smart selection.
- mMultiSelection =
- mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
- if (mMultiSelection) {
- mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION);
- } else if (mClassifierSelection) {
- mClassifier.logEvent(logTag, LOG_EVENT_SINGLE_SELECTION);
+ public void onSmartSelection(SelectionResult result) {
+ if (mSelectionStarted) {
+ mSelectionStart = result.mStart;
+ mSelectionEnd = result.mEnd;
+ mAllowReset = mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
+ mLogger.logSelectionModified(
+ result.mStart, result.mEnd, result.mClassification, result.mSelection);
}
}
/**
* Called when selection bounds change.
*/
- public void onSelectionUpdated(int selectionStart, int selectionEnd, String logTag) {
- final boolean selectionChanged =
- selectionStart != mSelectionStart || selectionEnd != mSelectionEnd;
- if (selectionChanged) {
- if (mMultiSelection) {
- mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_MODIFIED);
- } else if (mClassifierSelection) {
- mClassifier.logEvent(logTag, LOG_EVENT_SINGLE_SELECTION_MODIFIED);
- }
- resetSelectionFlags();
+ public void onSelectionUpdated(
+ int selectionStart, int selectionEnd,
+ @Nullable TextClassification classification) {
+ if (mSelectionStarted) {
+ mAllowReset = false;
+ mLogger.logSelectionModified(selectionStart, selectionEnd, classification, null);
}
}
@@ -277,17 +268,23 @@
* Called when the selection action mode is destroyed.
*/
public void onSelectionDestroyed() {
- resetSelectionFlags();
+ mAllowReset = false;
+ mSelectionStarted = false;
+ mLogger.logSelectionAction(
+ mSelectionStart, mSelectionEnd,
+ SelectionEvent.ActionType.ABANDON, null /* classification */);
}
/**
- * Logs if the action was taken on a smart selection.
+ * Called when an action is taken on a smart selection.
*/
- public void onSelectionAction(String logTag) {
- if (mMultiSelection) {
- mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_ACTION);
- } else if (mClassifierSelection) {
- mClassifier.logEvent(logTag, LOG_EVENT_SINGLE_SELECTION_ACTION);
+ public void onSelectionAction(
+ int selectionStart, int selectionEnd,
+ @SelectionEvent.ActionType int action,
+ @Nullable TextClassification classification) {
+ if (mSelectionStarted) {
+ mAllowReset = false;
+ mLogger.logSelectionAction(selectionStart, selectionEnd, action, classification);
}
}
@@ -297,22 +294,186 @@
* 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 (mMultiSelection
+ public boolean resetSelection(int textIndex, Editor editor) {
+ final TextView textView = editor.getTextView();
+ if (mSelectionStarted
+ && mAllowReset
&& textIndex >= mSelectionStart && textIndex <= mSelectionEnd
- && text instanceof Spannable) {
- // Only allow a reset once.
- resetSelectionFlags();
- mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_RESET);
- return editor.selectCurrentWord();
+ && textView.getText() instanceof Spannable) {
+ mAllowReset = false;
+ boolean selected = editor.selectCurrentWord();
+ if (selected) {
+ mLogger.logSelectionAction(
+ textView.getSelectionStart(), textView.getSelectionEnd(),
+ SelectionEvent.ActionType.RESET, null /* classification */);
+ }
+ return selected;
}
return false;
}
- private void resetSelectionFlags() {
- mMultiSelection = false;
- mClassifierSelection = false;
+ private void maybeInvalidateLogger(boolean editableText) {
+ if (mLogger.isEditTextLogger() != editableText) {
+ mLogger = new SelectionMetricsLogger(mContext, editableText);
+ }
+ }
+ }
+
+ // TODO: Write tests
+ /**
+ * Metrics logging helper.
+ *
+ * This logger logs selection by word indices. The initial (start) single word selection is
+ * logged at [0, 1) -- end index is exclusive. Other word indices are logged relative to the
+ * initial single word selection.
+ * e.g. New York city, NY. Suppose the initial selection is "York" in
+ * "New York city, NY", then "York" is at [0, 1), "New" is at [-1, 0], and "city" is at [1, 2).
+ * "New York" is at [-1, 1).
+ * Part selection of a word e.g. "or" is counted as selecting the
+ * entire word i.e. equivalent to "York", and each special character is counted as a word, e.g.
+ * "," is at [2, 3). Whitespaces are ignored.
+ */
+ private static final class SelectionMetricsLogger {
+
+ private static final String LOG_TAG = "SelectionMetricsLogger";
+
+ private final SmartSelectionEventTracker mDelegate;
+ private final boolean mEditTextLogger;
+ private final BreakIterator mWordIterator = BreakIterator.getWordInstance();
+ private int mStartIndex;
+ private int mEndIndex;
+ private String mText;
+
+ SelectionMetricsLogger(Context context, boolean editable) {
+ final @SmartSelectionEventTracker.WidgetType int widgetType = editable
+ ? SmartSelectionEventTracker.WidgetType.EDITTEXT
+ : SmartSelectionEventTracker.WidgetType.TEXTVIEW;
+ mDelegate = new SmartSelectionEventTracker(context, widgetType);
+ mEditTextLogger = editable;
+ }
+
+ public void logSelectionStarted(CharSequence text, int index) {
+ try {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkArgumentInRange(index, 0, text.length(), "index");
+ if (mText == null || !mText.contentEquals(text)) {
+ mText = text.toString();
+ }
+ mWordIterator.setText(mText);
+ mStartIndex = index;
+ mEndIndex = mWordIterator.following(index);
+ mDelegate.logEvent(SelectionEvent.selectionStarted(0));
+ } catch (Exception e) {
+ // Avoid crashes due to logging.
+ Log.d(LOG_TAG, e.getMessage());
+ }
+ }
+
+ public void logSelectionModified(int start, int end,
+ @Nullable TextClassification classification, @Nullable TextSelection selection) {
+ try {
+ Preconditions.checkArgumentInRange(start, 0, mText.length(), "start");
+ Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
+ int[] wordIndices = getWordDelta(start, end);
+ if (selection != null) {
+ mDelegate.logEvent(SelectionEvent.selectionModified(
+ wordIndices[0], wordIndices[1], selection));
+ } else if (classification != null) {
+ mDelegate.logEvent(SelectionEvent.selectionModified(
+ wordIndices[0], wordIndices[1], classification));
+ } else {
+ mDelegate.logEvent(SelectionEvent.selectionModified(
+ wordIndices[0], wordIndices[1]));
+ }
+ } catch (Exception e) {
+ // Avoid crashes due to logging.
+ Log.d(LOG_TAG, e.getMessage());
+ }
+ }
+
+ public void logSelectionAction(
+ int start, int end,
+ @SelectionEvent.ActionType int action,
+ @Nullable TextClassification classification) {
+ try {
+ Preconditions.checkArgumentInRange(start, 0, mText.length(), "start");
+ Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
+ int[] wordIndices = getWordDelta(start, end);
+ if (classification != null) {
+ mDelegate.logEvent(SelectionEvent.selectionAction(
+ wordIndices[0], wordIndices[1], action, classification));
+ } else {
+ mDelegate.logEvent(SelectionEvent.selectionAction(
+ wordIndices[0], wordIndices[1], action));
+ }
+ } catch (Exception e) {
+ // Avoid crashes due to logging.
+ Log.d(LOG_TAG, e.getMessage());
+ }
+ }
+
+ public boolean isEditTextLogger() {
+ return mEditTextLogger;
+ }
+
+ private int[] getWordDelta(int start, int end) {
+ int[] wordIndices = new int[2];
+
+ if (start == mStartIndex) {
+ wordIndices[0] = 0;
+ } else if (start < mStartIndex) {
+ wordIndices[0] = -countWordsForward(start);
+ } else { // start > mStartIndex
+ if (mStartIndex < start && start < mEndIndex) {
+ // If the new selection did not move past the original word,
+ // assume it has not moved.
+ wordIndices[0] = 0;
+ } else {
+ wordIndices[0] = countWordsBackward(start);
+ }
+ }
+
+ if (end == mStartIndex) {
+ wordIndices[1] = 0;
+ } else if (end < mStartIndex) {
+ wordIndices[1] = -countWordsForward(end);
+ } else { // end > mStartIndex
+ wordIndices[1] = countWordsBackward(end);
+ }
+
+ return wordIndices;
+ }
+
+ private int countWordsBackward(int from) {
+ Preconditions.checkArgument(from >= mStartIndex);
+ int wordCount = 0;
+ int offset = from;
+ while (offset > mStartIndex) {
+ int start = mWordIterator.preceding(offset);
+ if (!isWhitespace(start, offset)) {
+ wordCount++;
+ }
+ offset = start;
+ }
+ return wordCount;
+ }
+
+ private int countWordsForward(int from) {
+ Preconditions.checkArgument(from <= mStartIndex);
+ int wordCount = 0;
+ int offset = from;
+ while (offset < mStartIndex) {
+ int end = mWordIterator.following(offset);
+ if (!isWhitespace(offset, end)) {
+ wordCount++;
+ }
+ offset = end;
+ }
+ return wordCount;
+ }
+
+ private boolean isWhitespace(int start, int end) {
+ return mText.substring(start, end).trim().isEmpty();
}
}
@@ -392,8 +553,6 @@
/** End index relative to mText. */
private int mSelectionEnd;
private LocaleList mLocales;
- /** A tag for the classifier that returned the latest smart selection. */
- private String mSelectionTag = "";
/** Trimmed text starting from mTrimStart in mText. */
private CharSequence mTrimmedText;
@@ -413,13 +572,12 @@
TextClassificationHelper(TextClassifier textClassifier,
CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
- reset(textClassifier, text, selectionStart, selectionEnd, true, locales);
+ reset(textClassifier, text, selectionStart, selectionEnd, locales);
}
@UiThread
public void reset(TextClassifier textClassifier,
- CharSequence text, int selectionStart, int selectionEnd,
- boolean resetSelectionTag, LocaleList locales) {
+ CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
mTextClassifier = Preconditions.checkNotNull(textClassifier);
mText = Preconditions.checkNotNull(text).toString();
mLastClassificationText = null; // invalidate.
@@ -427,13 +585,24 @@
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
mLocales = locales;
- if (resetSelectionTag) {
- mSelectionTag = "";
- }
}
@WorkerThread
public SelectionResult classifyText() {
+ return performClassification(null);
+ }
+
+ @WorkerThread
+ public SelectionResult suggestSelection() {
+ trimText();
+ final TextSelection selection = mTextClassifier.suggestSelection(
+ mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
+ mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart);
+ mSelectionEnd = Math.min(mText.length(), selection.getSelectionEndIndex() + mTrimStart);
+ return performClassification(selection);
+ }
+
+ private SelectionResult performClassification(@Nullable TextSelection selection) {
if (!Objects.equals(mText, mLastClassificationText)
|| mSelectionStart != mLastClassificationSelectionStart
|| mSelectionEnd != mLastClassificationSelectionEnd
@@ -449,27 +618,13 @@
mSelectionStart,
mSelectionEnd,
mTextClassifier.classifyText(
- mTrimmedText, mRelativeStart, mRelativeEnd, mLocales));
+ mTrimmedText, mRelativeStart, mRelativeEnd, mLocales),
+ selection);
}
return mLastClassificationResult;
}
- @WorkerThread
- public SelectionResult suggestSelection() {
- trimText();
- final TextSelection sel = mTextClassifier.suggestSelection(
- mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
- mSelectionStart = Math.max(0, sel.getSelectionStartIndex() + mTrimStart);
- mSelectionEnd = Math.min(mText.length(), sel.getSelectionEndIndex() + mTrimStart);
- mSelectionTag = sel.getSourceClassifier();
- return classifyText();
- }
-
- String getSelectionTag() {
- return mSelectionTag;
- }
-
private void trimText() {
mTrimStart = Math.max(0, mSelectionStart - TRIM_DELTA);
final int referenceEnd = Math.min(mText.length(), mSelectionEnd + TRIM_DELTA);
@@ -486,11 +641,37 @@
private final int mStart;
private final int mEnd;
private final TextClassification mClassification;
+ @Nullable private final TextSelection mSelection;
- SelectionResult(int start, int end, TextClassification classification) {
+ SelectionResult(int start, int end,
+ TextClassification classification, @Nullable TextSelection selection) {
mStart = start;
mEnd = end;
mClassification = Preconditions.checkNotNull(classification);
+ mSelection = selection;
+ }
+ }
+
+
+
+ @SelectionEvent.ActionType
+ private static int getActionType(int menuItemId) {
+ switch (menuItemId) {
+ case TextView.ID_SELECT_ALL:
+ return SelectionEvent.ActionType.SELECT_ALL;
+ case TextView.ID_CUT:
+ return SelectionEvent.ActionType.CUT;
+ case TextView.ID_COPY:
+ return SelectionEvent.ActionType.COPY;
+ case TextView.ID_PASTE: // fall through
+ case TextView.ID_PASTE_AS_PLAIN_TEXT:
+ return SelectionEvent.ActionType.PASTE;
+ case TextView.ID_SHARE:
+ return SelectionEvent.ActionType.SHARE;
+ case TextView.ID_ASSIST:
+ return SelectionEvent.ActionType.SMART_SHARE;
+ default:
+ return SelectionEvent.ActionType.OTHER;
}
}
}