Loading core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java +134 −104 Original line number Original line Diff line number Diff line Loading @@ -42,17 +42,16 @@ import java.util.UUID; //TODO: Do not allow any crashes from this class. //TODO: Do not allow any crashes from this class. public final class SmartSelectionEventTracker { public final class SmartSelectionEventTracker { private static final String LOG_TAG = "SmartSelectionEventTracker"; private static final String LOG_TAG = "SmartSelectEventTracker"; private static final boolean DEBUG_LOG_ENABLED = true; private static final boolean DEBUG_LOG_ENABLED = true; private static final int START_EVENT_DELTA = MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS; private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START; private static final int PREV_EVENT_DELTA = MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS; private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS; private static final int ENTITY_TYPE = MetricsEvent.NOTIFICATION_TAG; private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX; private static final int INDEX = MetricsEvent.NOTIFICATION_SHADE_INDEX; private static final int VERSION_TAG = MetricsEvent.FIELD_SELECTION_VERSION_TAG; private static final int TAG = MetricsEvent.FIELD_CLASS_NAME; private static final int SMART_INDICES = MetricsEvent.FIELD_SELECTION_SMART_RANGE; private static final int SMART_INDICES = MetricsEvent.FIELD_GESTURE_LENGTH; private static final int EVENT_INDICES = MetricsEvent.FIELD_SELECTION_RANGE; private static final int EVENT_INDICES = MetricsEvent.FIELD_CONTEXT; private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID; private static final int SESSION_ID = MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN; private static final String ZERO = "0"; private static final String ZERO = "0"; private static final String TEXTVIEW = "textview"; private static final String TEXTVIEW = "textview"; Loading Loading @@ -84,6 +83,7 @@ public final class SmartSelectionEventTracker { private long mSessionStartTime; private long mSessionStartTime; private long mLastEventTime; private long mLastEventTime; private boolean mSmartSelectionTriggered; private boolean mSmartSelectionTriggered; private String mVersionTag; public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) { public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) { mWidgetType = widgetType; mWidgetType = widgetType; Loading @@ -98,7 +98,8 @@ public final class SmartSelectionEventTracker { public void logEvent(@NonNull SelectionEvent event) { public void logEvent(@NonNull SelectionEvent event) { Preconditions.checkNotNull(event); Preconditions.checkNotNull(event); if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null) { if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null && DEBUG_LOG_ENABLED) { Log.d(LOG_TAG, "Selection session not yet started. Ignoring event"); Log.d(LOG_TAG, "Selection session not yet started. Ignoring event"); return; return; } } Loading @@ -114,6 +115,7 @@ public final class SmartSelectionEventTracker { case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through case SelectionEvent.EventType.SMART_SELECTION_MULTI: case SelectionEvent.EventType.SMART_SELECTION_MULTI: mSmartSelectionTriggered = true; mSmartSelectionTriggered = true; mVersionTag = getVersionTag(event); mSmartIndices[0] = event.mStart; mSmartIndices[0] = event.mStart; mSmartIndices[1] = event.mEnd; mSmartIndices[1] = event.mEnd; break; break; Loading @@ -132,16 +134,16 @@ public final class SmartSelectionEventTracker { } } private void writeEvent(SelectionEvent event, long now) { private void writeEvent(SelectionEvent event, long now) { final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime; final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION) .setType(getLogType(event)) .setType(getLogType(event)) .setSubtype(event.mEventType) .setSubtype(getLogSubType(event)) .setPackageName(mContext.getPackageName()) .setPackageName(mContext.getPackageName()) .setTimestamp(now) .setTimestamp(now) .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime) .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime) .addTaggedData(PREV_EVENT_DELTA, now - mLastEventTime) .addTaggedData(PREV_EVENT_DELTA, prevEventDelta) .addTaggedData(ENTITY_TYPE, event.mEntityType) .addTaggedData(INDEX, mIndex) .addTaggedData(INDEX, mIndex) .addTaggedData(TAG, getTag(event)) .addTaggedData(VERSION_TAG, mVersionTag) .addTaggedData(SMART_INDICES, getSmartDelta()) .addTaggedData(SMART_INDICES, getSmartDelta()) .addTaggedData(EVENT_INDICES, getEventDelta(event)) .addTaggedData(EVENT_INDICES, getEventDelta(event)) .addTaggedData(SESSION_ID, mSessionId); .addTaggedData(SESSION_ID, mSessionId); Loading @@ -168,42 +170,120 @@ public final class SmartSelectionEventTracker { mSessionStartTime = 0; mSessionStartTime = 0; mLastEventTime = 0; mLastEventTime = 0; mSmartSelectionTriggered = false; mSmartSelectionTriggered = false; mVersionTag = getVersionTag(null); mSessionId = null; mSessionId = null; } } private int getLogType(SelectionEvent event) { private static int getLogType(SelectionEvent event) { switch (event.mEventType) { switch (event.mEventType) { case SelectionEvent.EventType.SELECTION_STARTED: // fall through case SelectionEvent.ActionType.OVERTYPE: case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE; case SelectionEvent.EventType.SMART_SELECTION_MULTI: // fall through case SelectionEvent.ActionType.COPY: case SelectionEvent.EventType.AUTO_SELECTION: return MetricsEvent.ACTION_TEXT_SELECTION_COPY; return MetricsEvent.TYPE_OPEN; case SelectionEvent.ActionType.PASTE: return MetricsEvent.ACTION_TEXT_SELECTION_PASTE; case SelectionEvent.ActionType.CUT: return MetricsEvent.ACTION_TEXT_SELECTION_CUT; case SelectionEvent.ActionType.SHARE: return MetricsEvent.ACTION_TEXT_SELECTION_SHARE; case SelectionEvent.ActionType.SMART_SHARE: return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE; case SelectionEvent.ActionType.DRAG: return MetricsEvent.ACTION_TEXT_SELECTION_DRAG; case SelectionEvent.ActionType.ABANDON: case SelectionEvent.ActionType.ABANDON: return MetricsEvent.TYPE_CLOSE; return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON; } case SelectionEvent.ActionType.OTHER: if (event.isActionType()) { return MetricsEvent.ACTION_TEXT_SELECTION_OTHER; if (event.isTerminal() && mSmartSelectionTriggered) { case SelectionEvent.ActionType.SELECT_ALL: if (matchesSmartSelectionBounds(event)) { return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL; // Smart selection accepted. case SelectionEvent.ActionType.RESET: return MetricsEvent.TYPE_SUCCESS; return MetricsEvent.ACTION_TEXT_SELECTION_RESET; } else if (containsOriginalSelection(event)) { case SelectionEvent.EventType.SELECTION_STARTED: // Smart selection rejected. return MetricsEvent.ACTION_TEXT_SELECTION_START; return MetricsEvent.TYPE_FAILURE; case SelectionEvent.EventType.SELECTION_MODIFIED: } return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY; // User changed the original selection entirely. case SelectionEvent.EventType.SMART_SELECTION_SINGLE: } return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE; return MetricsEvent.TYPE_ACTION; case SelectionEvent.EventType.SMART_SELECTION_MULTI: } else { return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI; return MetricsEvent.TYPE_UPDATE; case SelectionEvent.EventType.AUTO_SELECTION: } return MetricsEvent.ACTION_TEXT_SELECTION_AUTO; } default: return MetricsEvent.VIEW_UNKNOWN; private boolean matchesSmartSelectionBounds(SelectionEvent event) { } return event.mStart == mSmartIndices[0] && event.mEnd == mSmartIndices[1]; } private static String getLogTypeString(int logType) { switch (logType) { case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE: return "OVERTYPE"; case MetricsEvent.ACTION_TEXT_SELECTION_COPY: return "COPY"; case MetricsEvent.ACTION_TEXT_SELECTION_PASTE: return "PASTE"; case MetricsEvent.ACTION_TEXT_SELECTION_CUT: return "CUT"; case MetricsEvent.ACTION_TEXT_SELECTION_SHARE: return "SHARE"; case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE: return "SMART_SHARE"; case MetricsEvent.ACTION_TEXT_SELECTION_DRAG: return "DRAG"; case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON: return "ABANDON"; case MetricsEvent.ACTION_TEXT_SELECTION_OTHER: return "OTHER"; case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL: return "SELECT_ALL"; case MetricsEvent.ACTION_TEXT_SELECTION_RESET: return "RESET"; case MetricsEvent.ACTION_TEXT_SELECTION_START: return "SELECTION_STARTED"; case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY: return "SELECTION_MODIFIED"; case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE: return "SMART_SELECTION_SINGLE"; case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI: return "SMART_SELECTION_MULTI"; case MetricsEvent.ACTION_TEXT_SELECTION_AUTO: return "AUTO_SELECTION"; default: return UNKNOWN; } } private static int getLogSubType(SelectionEvent event) { switch (event.mEntityType) { case TextClassifier.TYPE_OTHER: return MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER; case TextClassifier.TYPE_EMAIL: return MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL; case TextClassifier.TYPE_PHONE: return MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE; case TextClassifier.TYPE_ADDRESS: return MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS; case TextClassifier.TYPE_URL: return MetricsEvent.TEXT_CLASSIFIER_TYPE_URL; default: return MetricsEvent.TEXT_CLASSIFIER_TYPE_UNKNOWN; } } private static String getLogSubTypeString(int logSubType) { switch (logSubType) { case MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER: return TextClassifier.TYPE_OTHER; case MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL: return TextClassifier.TYPE_EMAIL; case MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE: return TextClassifier.TYPE_PHONE; case MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS: return TextClassifier.TYPE_ADDRESS; case MetricsEvent.TEXT_CLASSIFIER_TYPE_URL: return TextClassifier.TYPE_URL; default: return TextClassifier.TYPE_UNKNOWN; } } private boolean containsOriginalSelection(SelectionEvent event) { return event.mStart <= mOrigStart && event.mEnd > mOrigStart; } } private int getSmartDelta() { private int getSmartDelta() { Loading @@ -221,7 +301,7 @@ public final class SmartSelectionEventTracker { | (clamp(event.mEnd - mOrigStart) & 0xffff); | (clamp(event.mEnd - mOrigStart) & 0xffff); } } private String getTag(SelectionEvent event) { private String getVersionTag(@Nullable SelectionEvent event) { final String widgetType; final String widgetType; switch (mWidgetType) { switch (mWidgetType) { case WidgetType.TEXTVIEW: case WidgetType.TEXTVIEW: Loading @@ -239,7 +319,9 @@ public final class SmartSelectionEventTracker { default: default: widgetType = UNKNOWN; widgetType = UNKNOWN; } } final String version = Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG); final String version = event == null ? SelectionEvent.NO_VERSION_TAG : Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG); return String.format("%s/%s", widgetType, version); return String.format("%s/%s", widgetType, version); } } Loading @@ -254,66 +336,17 @@ public final class SmartSelectionEventTracker { private static void debugLog(LogMaker log) { private static void debugLog(LogMaker log) { if (!DEBUG_LOG_ENABLED) return; if (!DEBUG_LOG_ENABLED) return; final String tag = Objects.toString(log.getTaggedData(TAG), "tag"); final String tag = Objects.toString(log.getTaggedData(VERSION_TAG), "tag"); final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO)); final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO)); if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) { final String event; switch (log.getSubtype()) { case SelectionEvent.ActionType.OVERTYPE: event = "OVERTYPE"; break; case SelectionEvent.ActionType.COPY: event = "COPY"; break; case SelectionEvent.ActionType.PASTE: event = "PASTE"; break; case SelectionEvent.ActionType.CUT: event = "CUT"; break; case SelectionEvent.ActionType.SHARE: event = "SHARE"; break; case SelectionEvent.ActionType.SMART_SHARE: event = "SMART_SHARE"; break; case SelectionEvent.ActionType.DRAG: event = "DRAG"; break; case SelectionEvent.ActionType.ABANDON: event = "ABANDON"; break; case SelectionEvent.ActionType.OTHER: event = "OTHER"; break; case SelectionEvent.ActionType.SELECT_ALL: event = "SELECT_ALL"; break; case SelectionEvent.ActionType.RESET: event = "RESET"; break; case SelectionEvent.EventType.SELECTION_STARTED: String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), ""); String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), ""); sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1); sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1); Log.d(LOG_TAG, String.format("New selection session: %s(%s)", tag, sessionId)); Log.d(LOG_TAG, String.format("New selection session: %s(%s)", tag, sessionId)); event = "SELECTION_STARTED"; break; case SelectionEvent.EventType.SELECTION_MODIFIED: event = "SELECTION_MODIFIED"; break; case SelectionEvent.EventType.SMART_SELECTION_SINGLE: event = "SMART_SELECTION_SINGLE"; break; case SelectionEvent.EventType.SMART_SELECTION_MULTI: event = "SMART_SELECTION_MULTI"; break; case SelectionEvent.EventType.AUTO_SELECTION: event = "AUTO_SELECTION"; break; default: event = "UNKNOWN"; } } final String type = getLogTypeString(log.getType()); final String subType = getLogSubTypeString(log.getSubtype()); final int smartIndices = Integer.parseInt( final int smartIndices = Integer.parseInt( Objects.toString(log.getTaggedData(SMART_INDICES), ZERO)); Objects.toString(log.getTaggedData(SMART_INDICES), ZERO)); final int smartStart = (short) ((smartIndices & 0xffff0000) >> 16); final int smartStart = (short) ((smartIndices & 0xffff0000) >> 16); Loading @@ -324,11 +357,8 @@ public final class SmartSelectionEventTracker { final int eventStart = (short) ((eventIndices & 0xffff0000) >> 16); final int eventStart = (short) ((eventIndices & 0xffff0000) >> 16); final int eventEnd = (short) (eventIndices & 0xffff); final int eventEnd = (short) (eventIndices & 0xffff); final String entity = Objects.toString( Log.d(LOG_TAG, String.format("%2d: %s/%s, context=%d,%d - old=%d,%d (%s)", log.getTaggedData(ENTITY_TYPE), TextClassifier.TYPE_UNKNOWN); index, type, subType, eventStart, eventEnd, smartStart, smartEnd, tag)); Log.d(LOG_TAG, String.format("%2d: %s, context=%d,%d - old=%d,%d [%s] (%s)", index, event, eventStart, eventEnd, smartStart, smartEnd, entity, tag)); } } /** /** Loading core/java/android/widget/Editor.java +8 −0 Original line number Original line Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.metrics.LogMaker; import android.os.Bundle; import android.os.Bundle; import android.os.LocaleList; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcel; Loading Loading @@ -3921,6 +3922,10 @@ public class Editor { textClassification.getLabel()) textClassification.getLabel()) .setIcon(textClassification.getIcon()) .setIcon(textClassification.getIcon()) .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); mMetricsLogger.write( new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) .setType(MetricsEvent.TYPE_OPEN) .setSubtype(textClassification.getLogType())); } } } } Loading Loading @@ -3962,6 +3967,9 @@ public class Editor { .onClick(mTextView); .onClick(mTextView); } } } } mMetricsLogger.action( MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST, textClassification.getLogType()); stopTextActionMode(); stopTextActionMode(); return true; return true; } } Loading proto/src/metrics_constants.proto +131 −0 Original line number Original line Diff line number Diff line Loading @@ -161,6 +161,16 @@ message MetricsEvent { CAMERA_EXTERNAL_USED = 2; CAMERA_EXTERNAL_USED = 2; } } // TextClassifier entity types. enum TextClassifierEntityType { TEXT_CLASSIFIER_TYPE_UNKNOWN = 1; TEXT_CLASSIFIER_TYPE_OTHER = 2; TEXT_CLASSIFIER_TYPE_EMAIL = 3; TEXT_CLASSIFIER_TYPE_PHONE = 4; TEXT_CLASSIFIER_TYPE_ADDRESS = 5; TEXT_CLASSIFIER_TYPE_URL = 6; } // Known visual elements: views or controls. // Known visual elements: views or controls. enum View { enum View { // Unknown view // Unknown view Loading Loading @@ -4320,6 +4330,127 @@ message MetricsEvent { // FIELD: A string value representing some state of the platform, e.g., boot reason // FIELD: A string value representing some state of the platform, e.g., boot reason FIELD_PLATFORM_REASON = 1099; FIELD_PLATFORM_REASON = 1099; // CATEGORY: The category for all actions relating to selection session logging. // OS: O MR TEXT_SELECTION_SESSION = 1100; // ACTION: A selection session started (i.e. the selection handles appeared) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_START = 1101; // ACTION: The user modified the selection (e.g. by dragging the handles) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_MODIFY = 1102; // ACTION: The user modified the selection by pressing the "select all" button. // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SELECT_ALL = 1103; // ACTION: The user modified the selection by pressing on a word in a multi word selection // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_RESET = 1104; // ACTION: Smart selection made a single word prediction // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SMART_SINGLE = 1105; // ACTION: Smart selection made a multi word prediction // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SMART_MULTI = 1106; // ACTION: The app made an automatic selection on the user's behalf (not smart selection) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_AUTO = 1107; // ACTION: A selection session ended with the user typing over the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_OVERTYPE = 1108; // ACTION: A selection session ended with the user copying the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_COPY = 1109; // ACTION: A selection session ended with the user pasting over the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_PASTE = 1110; // ACTION: A selection session ended with the user cutting the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_CUT = 1111; // ACTION: A selection session ended with the user pressing the share button // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SHARE = 1112; // ACTION: A selection session ended with the user pressing the smart share button // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SMART_SHARE = 1113; // ACTION: A selection session ended with the user dragging the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_DRAG = 1114; // ACTION: A selection session ended with the user abandoning the selection // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_ABANDON = 1115; // ACTION: A selection session ended with the user picking an unhandled action bar item // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_OTHER = 1116; // FIELD: Time in milliseconds from the start of the session to this event // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SINCE_START = 1117; // FIELD: time in milliseconds between the last event in the session and this one // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SINCE_PREVIOUS = 1118; // FIELD: a random uid for a selection session (lasting from select start to end) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SESSION_ID = 1119; // FIELD: the sequence number of the event in the session // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SESSION_INDEX = 1120; // FIELD: a concatenation of the widget type and ML model version. // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_VERSION_TAG = 1121; // FIELD: text select start offset in words (as defined by the ICU BreakIterator), stored as two // packed 16bit integers. (start in MSBs, end in LSBs) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_RANGE = 1122; // FIELD: smart text selection start offset in words (as defined by the ICU BreakIterator), // stored as two packed 16bit integers. (start in MSBs, end in LSBs) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SMART_RANGE = 1123; // ---- End O-MR1 Constants, all O-MR1 constants go above this line ---- // ---- End O-MR1 Constants, all O-MR1 constants go above this line ---- // Add new aosp constants above this line. // Add new aosp constants above this line. Loading Loading
core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java +134 −104 Original line number Original line Diff line number Diff line Loading @@ -42,17 +42,16 @@ import java.util.UUID; //TODO: Do not allow any crashes from this class. //TODO: Do not allow any crashes from this class. public final class SmartSelectionEventTracker { public final class SmartSelectionEventTracker { private static final String LOG_TAG = "SmartSelectionEventTracker"; private static final String LOG_TAG = "SmartSelectEventTracker"; private static final boolean DEBUG_LOG_ENABLED = true; private static final boolean DEBUG_LOG_ENABLED = true; private static final int START_EVENT_DELTA = MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS; private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START; private static final int PREV_EVENT_DELTA = MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS; private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS; private static final int ENTITY_TYPE = MetricsEvent.NOTIFICATION_TAG; private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX; private static final int INDEX = MetricsEvent.NOTIFICATION_SHADE_INDEX; private static final int VERSION_TAG = MetricsEvent.FIELD_SELECTION_VERSION_TAG; private static final int TAG = MetricsEvent.FIELD_CLASS_NAME; private static final int SMART_INDICES = MetricsEvent.FIELD_SELECTION_SMART_RANGE; private static final int SMART_INDICES = MetricsEvent.FIELD_GESTURE_LENGTH; private static final int EVENT_INDICES = MetricsEvent.FIELD_SELECTION_RANGE; private static final int EVENT_INDICES = MetricsEvent.FIELD_CONTEXT; private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID; private static final int SESSION_ID = MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN; private static final String ZERO = "0"; private static final String ZERO = "0"; private static final String TEXTVIEW = "textview"; private static final String TEXTVIEW = "textview"; Loading Loading @@ -84,6 +83,7 @@ public final class SmartSelectionEventTracker { private long mSessionStartTime; private long mSessionStartTime; private long mLastEventTime; private long mLastEventTime; private boolean mSmartSelectionTriggered; private boolean mSmartSelectionTriggered; private String mVersionTag; public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) { public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) { mWidgetType = widgetType; mWidgetType = widgetType; Loading @@ -98,7 +98,8 @@ public final class SmartSelectionEventTracker { public void logEvent(@NonNull SelectionEvent event) { public void logEvent(@NonNull SelectionEvent event) { Preconditions.checkNotNull(event); Preconditions.checkNotNull(event); if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null) { if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null && DEBUG_LOG_ENABLED) { Log.d(LOG_TAG, "Selection session not yet started. Ignoring event"); Log.d(LOG_TAG, "Selection session not yet started. Ignoring event"); return; return; } } Loading @@ -114,6 +115,7 @@ public final class SmartSelectionEventTracker { case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through case SelectionEvent.EventType.SMART_SELECTION_MULTI: case SelectionEvent.EventType.SMART_SELECTION_MULTI: mSmartSelectionTriggered = true; mSmartSelectionTriggered = true; mVersionTag = getVersionTag(event); mSmartIndices[0] = event.mStart; mSmartIndices[0] = event.mStart; mSmartIndices[1] = event.mEnd; mSmartIndices[1] = event.mEnd; break; break; Loading @@ -132,16 +134,16 @@ public final class SmartSelectionEventTracker { } } private void writeEvent(SelectionEvent event, long now) { private void writeEvent(SelectionEvent event, long now) { final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime; final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION) .setType(getLogType(event)) .setType(getLogType(event)) .setSubtype(event.mEventType) .setSubtype(getLogSubType(event)) .setPackageName(mContext.getPackageName()) .setPackageName(mContext.getPackageName()) .setTimestamp(now) .setTimestamp(now) .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime) .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime) .addTaggedData(PREV_EVENT_DELTA, now - mLastEventTime) .addTaggedData(PREV_EVENT_DELTA, prevEventDelta) .addTaggedData(ENTITY_TYPE, event.mEntityType) .addTaggedData(INDEX, mIndex) .addTaggedData(INDEX, mIndex) .addTaggedData(TAG, getTag(event)) .addTaggedData(VERSION_TAG, mVersionTag) .addTaggedData(SMART_INDICES, getSmartDelta()) .addTaggedData(SMART_INDICES, getSmartDelta()) .addTaggedData(EVENT_INDICES, getEventDelta(event)) .addTaggedData(EVENT_INDICES, getEventDelta(event)) .addTaggedData(SESSION_ID, mSessionId); .addTaggedData(SESSION_ID, mSessionId); Loading @@ -168,42 +170,120 @@ public final class SmartSelectionEventTracker { mSessionStartTime = 0; mSessionStartTime = 0; mLastEventTime = 0; mLastEventTime = 0; mSmartSelectionTriggered = false; mSmartSelectionTriggered = false; mVersionTag = getVersionTag(null); mSessionId = null; mSessionId = null; } } private int getLogType(SelectionEvent event) { private static int getLogType(SelectionEvent event) { switch (event.mEventType) { switch (event.mEventType) { case SelectionEvent.EventType.SELECTION_STARTED: // fall through case SelectionEvent.ActionType.OVERTYPE: case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE; case SelectionEvent.EventType.SMART_SELECTION_MULTI: // fall through case SelectionEvent.ActionType.COPY: case SelectionEvent.EventType.AUTO_SELECTION: return MetricsEvent.ACTION_TEXT_SELECTION_COPY; return MetricsEvent.TYPE_OPEN; case SelectionEvent.ActionType.PASTE: return MetricsEvent.ACTION_TEXT_SELECTION_PASTE; case SelectionEvent.ActionType.CUT: return MetricsEvent.ACTION_TEXT_SELECTION_CUT; case SelectionEvent.ActionType.SHARE: return MetricsEvent.ACTION_TEXT_SELECTION_SHARE; case SelectionEvent.ActionType.SMART_SHARE: return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE; case SelectionEvent.ActionType.DRAG: return MetricsEvent.ACTION_TEXT_SELECTION_DRAG; case SelectionEvent.ActionType.ABANDON: case SelectionEvent.ActionType.ABANDON: return MetricsEvent.TYPE_CLOSE; return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON; } case SelectionEvent.ActionType.OTHER: if (event.isActionType()) { return MetricsEvent.ACTION_TEXT_SELECTION_OTHER; if (event.isTerminal() && mSmartSelectionTriggered) { case SelectionEvent.ActionType.SELECT_ALL: if (matchesSmartSelectionBounds(event)) { return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL; // Smart selection accepted. case SelectionEvent.ActionType.RESET: return MetricsEvent.TYPE_SUCCESS; return MetricsEvent.ACTION_TEXT_SELECTION_RESET; } else if (containsOriginalSelection(event)) { case SelectionEvent.EventType.SELECTION_STARTED: // Smart selection rejected. return MetricsEvent.ACTION_TEXT_SELECTION_START; return MetricsEvent.TYPE_FAILURE; case SelectionEvent.EventType.SELECTION_MODIFIED: } return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY; // User changed the original selection entirely. case SelectionEvent.EventType.SMART_SELECTION_SINGLE: } return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE; return MetricsEvent.TYPE_ACTION; case SelectionEvent.EventType.SMART_SELECTION_MULTI: } else { return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI; return MetricsEvent.TYPE_UPDATE; case SelectionEvent.EventType.AUTO_SELECTION: } return MetricsEvent.ACTION_TEXT_SELECTION_AUTO; } default: return MetricsEvent.VIEW_UNKNOWN; private boolean matchesSmartSelectionBounds(SelectionEvent event) { } return event.mStart == mSmartIndices[0] && event.mEnd == mSmartIndices[1]; } private static String getLogTypeString(int logType) { switch (logType) { case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE: return "OVERTYPE"; case MetricsEvent.ACTION_TEXT_SELECTION_COPY: return "COPY"; case MetricsEvent.ACTION_TEXT_SELECTION_PASTE: return "PASTE"; case MetricsEvent.ACTION_TEXT_SELECTION_CUT: return "CUT"; case MetricsEvent.ACTION_TEXT_SELECTION_SHARE: return "SHARE"; case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE: return "SMART_SHARE"; case MetricsEvent.ACTION_TEXT_SELECTION_DRAG: return "DRAG"; case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON: return "ABANDON"; case MetricsEvent.ACTION_TEXT_SELECTION_OTHER: return "OTHER"; case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL: return "SELECT_ALL"; case MetricsEvent.ACTION_TEXT_SELECTION_RESET: return "RESET"; case MetricsEvent.ACTION_TEXT_SELECTION_START: return "SELECTION_STARTED"; case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY: return "SELECTION_MODIFIED"; case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE: return "SMART_SELECTION_SINGLE"; case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI: return "SMART_SELECTION_MULTI"; case MetricsEvent.ACTION_TEXT_SELECTION_AUTO: return "AUTO_SELECTION"; default: return UNKNOWN; } } private static int getLogSubType(SelectionEvent event) { switch (event.mEntityType) { case TextClassifier.TYPE_OTHER: return MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER; case TextClassifier.TYPE_EMAIL: return MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL; case TextClassifier.TYPE_PHONE: return MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE; case TextClassifier.TYPE_ADDRESS: return MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS; case TextClassifier.TYPE_URL: return MetricsEvent.TEXT_CLASSIFIER_TYPE_URL; default: return MetricsEvent.TEXT_CLASSIFIER_TYPE_UNKNOWN; } } private static String getLogSubTypeString(int logSubType) { switch (logSubType) { case MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER: return TextClassifier.TYPE_OTHER; case MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL: return TextClassifier.TYPE_EMAIL; case MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE: return TextClassifier.TYPE_PHONE; case MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS: return TextClassifier.TYPE_ADDRESS; case MetricsEvent.TEXT_CLASSIFIER_TYPE_URL: return TextClassifier.TYPE_URL; default: return TextClassifier.TYPE_UNKNOWN; } } private boolean containsOriginalSelection(SelectionEvent event) { return event.mStart <= mOrigStart && event.mEnd > mOrigStart; } } private int getSmartDelta() { private int getSmartDelta() { Loading @@ -221,7 +301,7 @@ public final class SmartSelectionEventTracker { | (clamp(event.mEnd - mOrigStart) & 0xffff); | (clamp(event.mEnd - mOrigStart) & 0xffff); } } private String getTag(SelectionEvent event) { private String getVersionTag(@Nullable SelectionEvent event) { final String widgetType; final String widgetType; switch (mWidgetType) { switch (mWidgetType) { case WidgetType.TEXTVIEW: case WidgetType.TEXTVIEW: Loading @@ -239,7 +319,9 @@ public final class SmartSelectionEventTracker { default: default: widgetType = UNKNOWN; widgetType = UNKNOWN; } } final String version = Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG); final String version = event == null ? SelectionEvent.NO_VERSION_TAG : Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG); return String.format("%s/%s", widgetType, version); return String.format("%s/%s", widgetType, version); } } Loading @@ -254,66 +336,17 @@ public final class SmartSelectionEventTracker { private static void debugLog(LogMaker log) { private static void debugLog(LogMaker log) { if (!DEBUG_LOG_ENABLED) return; if (!DEBUG_LOG_ENABLED) return; final String tag = Objects.toString(log.getTaggedData(TAG), "tag"); final String tag = Objects.toString(log.getTaggedData(VERSION_TAG), "tag"); final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO)); final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO)); if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) { final String event; switch (log.getSubtype()) { case SelectionEvent.ActionType.OVERTYPE: event = "OVERTYPE"; break; case SelectionEvent.ActionType.COPY: event = "COPY"; break; case SelectionEvent.ActionType.PASTE: event = "PASTE"; break; case SelectionEvent.ActionType.CUT: event = "CUT"; break; case SelectionEvent.ActionType.SHARE: event = "SHARE"; break; case SelectionEvent.ActionType.SMART_SHARE: event = "SMART_SHARE"; break; case SelectionEvent.ActionType.DRAG: event = "DRAG"; break; case SelectionEvent.ActionType.ABANDON: event = "ABANDON"; break; case SelectionEvent.ActionType.OTHER: event = "OTHER"; break; case SelectionEvent.ActionType.SELECT_ALL: event = "SELECT_ALL"; break; case SelectionEvent.ActionType.RESET: event = "RESET"; break; case SelectionEvent.EventType.SELECTION_STARTED: String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), ""); String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), ""); sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1); sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1); Log.d(LOG_TAG, String.format("New selection session: %s(%s)", tag, sessionId)); Log.d(LOG_TAG, String.format("New selection session: %s(%s)", tag, sessionId)); event = "SELECTION_STARTED"; break; case SelectionEvent.EventType.SELECTION_MODIFIED: event = "SELECTION_MODIFIED"; break; case SelectionEvent.EventType.SMART_SELECTION_SINGLE: event = "SMART_SELECTION_SINGLE"; break; case SelectionEvent.EventType.SMART_SELECTION_MULTI: event = "SMART_SELECTION_MULTI"; break; case SelectionEvent.EventType.AUTO_SELECTION: event = "AUTO_SELECTION"; break; default: event = "UNKNOWN"; } } final String type = getLogTypeString(log.getType()); final String subType = getLogSubTypeString(log.getSubtype()); final int smartIndices = Integer.parseInt( final int smartIndices = Integer.parseInt( Objects.toString(log.getTaggedData(SMART_INDICES), ZERO)); Objects.toString(log.getTaggedData(SMART_INDICES), ZERO)); final int smartStart = (short) ((smartIndices & 0xffff0000) >> 16); final int smartStart = (short) ((smartIndices & 0xffff0000) >> 16); Loading @@ -324,11 +357,8 @@ public final class SmartSelectionEventTracker { final int eventStart = (short) ((eventIndices & 0xffff0000) >> 16); final int eventStart = (short) ((eventIndices & 0xffff0000) >> 16); final int eventEnd = (short) (eventIndices & 0xffff); final int eventEnd = (short) (eventIndices & 0xffff); final String entity = Objects.toString( Log.d(LOG_TAG, String.format("%2d: %s/%s, context=%d,%d - old=%d,%d (%s)", log.getTaggedData(ENTITY_TYPE), TextClassifier.TYPE_UNKNOWN); index, type, subType, eventStart, eventEnd, smartStart, smartEnd, tag)); Log.d(LOG_TAG, String.format("%2d: %s, context=%d,%d - old=%d,%d [%s] (%s)", index, event, eventStart, eventEnd, smartStart, smartEnd, entity, tag)); } } /** /** Loading
core/java/android/widget/Editor.java +8 −0 Original line number Original line Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.metrics.LogMaker; import android.os.Bundle; import android.os.Bundle; import android.os.LocaleList; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcel; Loading Loading @@ -3921,6 +3922,10 @@ public class Editor { textClassification.getLabel()) textClassification.getLabel()) .setIcon(textClassification.getIcon()) .setIcon(textClassification.getIcon()) .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); mMetricsLogger.write( new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) .setType(MetricsEvent.TYPE_OPEN) .setSubtype(textClassification.getLogType())); } } } } Loading Loading @@ -3962,6 +3967,9 @@ public class Editor { .onClick(mTextView); .onClick(mTextView); } } } } mMetricsLogger.action( MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST, textClassification.getLogType()); stopTextActionMode(); stopTextActionMode(); return true; return true; } } Loading
proto/src/metrics_constants.proto +131 −0 Original line number Original line Diff line number Diff line Loading @@ -161,6 +161,16 @@ message MetricsEvent { CAMERA_EXTERNAL_USED = 2; CAMERA_EXTERNAL_USED = 2; } } // TextClassifier entity types. enum TextClassifierEntityType { TEXT_CLASSIFIER_TYPE_UNKNOWN = 1; TEXT_CLASSIFIER_TYPE_OTHER = 2; TEXT_CLASSIFIER_TYPE_EMAIL = 3; TEXT_CLASSIFIER_TYPE_PHONE = 4; TEXT_CLASSIFIER_TYPE_ADDRESS = 5; TEXT_CLASSIFIER_TYPE_URL = 6; } // Known visual elements: views or controls. // Known visual elements: views or controls. enum View { enum View { // Unknown view // Unknown view Loading Loading @@ -4320,6 +4330,127 @@ message MetricsEvent { // FIELD: A string value representing some state of the platform, e.g., boot reason // FIELD: A string value representing some state of the platform, e.g., boot reason FIELD_PLATFORM_REASON = 1099; FIELD_PLATFORM_REASON = 1099; // CATEGORY: The category for all actions relating to selection session logging. // OS: O MR TEXT_SELECTION_SESSION = 1100; // ACTION: A selection session started (i.e. the selection handles appeared) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_START = 1101; // ACTION: The user modified the selection (e.g. by dragging the handles) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_MODIFY = 1102; // ACTION: The user modified the selection by pressing the "select all" button. // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SELECT_ALL = 1103; // ACTION: The user modified the selection by pressing on a word in a multi word selection // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_RESET = 1104; // ACTION: Smart selection made a single word prediction // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SMART_SINGLE = 1105; // ACTION: Smart selection made a multi word prediction // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SMART_MULTI = 1106; // ACTION: The app made an automatic selection on the user's behalf (not smart selection) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_AUTO = 1107; // ACTION: A selection session ended with the user typing over the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_OVERTYPE = 1108; // ACTION: A selection session ended with the user copying the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_COPY = 1109; // ACTION: A selection session ended with the user pasting over the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_PASTE = 1110; // ACTION: A selection session ended with the user cutting the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_CUT = 1111; // ACTION: A selection session ended with the user pressing the share button // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SHARE = 1112; // ACTION: A selection session ended with the user pressing the smart share button // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_SMART_SHARE = 1113; // ACTION: A selection session ended with the user dragging the text // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_DRAG = 1114; // ACTION: A selection session ended with the user abandoning the selection // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_ABANDON = 1115; // ACTION: A selection session ended with the user picking an unhandled action bar item // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR ACTION_TEXT_SELECTION_OTHER = 1116; // FIELD: Time in milliseconds from the start of the session to this event // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SINCE_START = 1117; // FIELD: time in milliseconds between the last event in the session and this one // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SINCE_PREVIOUS = 1118; // FIELD: a random uid for a selection session (lasting from select start to end) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SESSION_ID = 1119; // FIELD: the sequence number of the event in the session // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SESSION_INDEX = 1120; // FIELD: a concatenation of the widget type and ML model version. // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_VERSION_TAG = 1121; // FIELD: text select start offset in words (as defined by the ICU BreakIterator), stored as two // packed 16bit integers. (start in MSBs, end in LSBs) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_RANGE = 1122; // FIELD: smart text selection start offset in words (as defined by the ICU BreakIterator), // stored as two packed 16bit integers. (start in MSBs, end in LSBs) // CATEGORY: TEXT_SELECTION_SESSION // OS: O MR FIELD_SELECTION_SMART_RANGE = 1123; // ---- End O-MR1 Constants, all O-MR1 constants go above this line ---- // ---- End O-MR1 Constants, all O-MR1 constants go above this line ---- // Add new aosp constants above this line. // Add new aosp constants above this line. Loading