@IntDef cleanup in the browser/java/contextualsearch (part 3)

@IntDef/@StringDef annotation are preferred way for declaring
set of String/int values:

1. they need less space in APK than enum, see
https://developer.android.com/topic/performance/reduce-apk-size#remove-enums
2. they give more control over allowed values than "static final" values

Main goal of patch is writing "static final" values, enum
and some classes in one common @IntDef/@StringDef form:

1. with @IntDef/@StringDef first, @Retention second
   and related @interface third
2. with values inside @interface
3. with NUM_ENTRIES declaring number of entries if necessary
4. with comment about numbering from 0 without gaps when necessary
5. with @Retention(RetentionPolicy.SOURCE)
6. without "static final" in the @interface

Change-Id: I83ee6e6280999c9e2b51a0ea3b1cee3d030cfd3f
Reviewed-on: https://chromium-review.googlesource.com/1128095
Reviewed-by: Donn Denman <donnd@chromium.org>
Commit-Queue: Donn Denman <donnd@chromium.org>
Commit-Queue: Marcin WiÄ…cek <marcin@mwiacek.com>
Cr-Commit-Position: refs/heads/master@{#575468}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLogger.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLogger.java
index c85aa43..a25c183 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLogger.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLogger.java
@@ -4,57 +4,74 @@
 
 package org.chromium.chrome.browser.contextualsearch;
 
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 
 import org.chromium.content_public.browser.WebContents;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * An interface for logging to UMA via Ranker.
  */
 public interface ContextualSearchRankerLogger {
-    // TODO(donnd): consider changing this enum to an IntDef.
     // NOTE: this list needs to be kept in sync with the white list in
     // predictor_config_definitions.cc, the names list in ContextualSearchRankerLoggerImpl.java
     // and with ukm.xml!
-    enum Feature {
-        UNKNOWN,
+    @IntDef({Feature.UNKNOWN, Feature.OUTCOME_WAS_PANEL_OPENED,
+            Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED, Feature.OUTCOME_WAS_QUICK_ANSWER_SEEN,
+            Feature.OUTCOME_WAS_CARDS_DATA_SHOWN, Feature.DURATION_AFTER_SCROLL_MS,
+            Feature.SCREEN_TOP_DPS, Feature.WAS_SCREEN_BOTTOM,
+            Feature.PREVIOUS_WEEK_IMPRESSIONS_COUNT, Feature.PREVIOUS_WEEK_CTR_PERCENT,
+            Feature.PREVIOUS_28DAY_IMPRESSIONS_COUNT, Feature.PREVIOUS_28DAY_CTR_PERCENT,
+            Feature.DID_OPT_IN, Feature.IS_SHORT_WORD, Feature.IS_LONG_WORD, Feature.IS_WORD_EDGE,
+            Feature.IS_ENTITY, Feature.TAP_DURATION_MS, Feature.FONT_SIZE,
+            Feature.IS_SECOND_TAP_OVERRIDE, Feature.IS_HTTP, Feature.IS_ENTITY_ELIGIBLE,
+            Feature.IS_LANGUAGE_MISMATCH, Feature.PORTION_OF_ELEMENT, Feature.TAP_COUNT,
+            Feature.OPEN_COUNT, Feature.QUICK_ANSWER_COUNT, Feature.ENTITY_IMPRESSIONS_COUNT,
+            Feature.ENTITY_OPENS_COUNT, Feature.QUICK_ACTION_IMPRESSIONS_COUNT,
+            Feature.QUICK_ACTIONS_TAKEN_COUNT, Feature.QUICK_ACTIONS_IGNORED_COUNT})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Feature {
+        int UNKNOWN = 0;
         // Outcome labels:
-        OUTCOME_WAS_PANEL_OPENED,
-        OUTCOME_WAS_QUICK_ACTION_CLICKED,
-        OUTCOME_WAS_QUICK_ANSWER_SEEN,
-        OUTCOME_WAS_CARDS_DATA_SHOWN, // a UKM CS v2 label.
-        // Features:
-        DURATION_AFTER_SCROLL_MS,
-        SCREEN_TOP_DPS,
-        WAS_SCREEN_BOTTOM,
+        int OUTCOME_WAS_PANEL_OPENED = 1;
+        int OUTCOME_WAS_QUICK_ACTION_CLICKED = 2;
+        int OUTCOME_WAS_QUICK_ANSWER_SEEN = 3;
+        int OUTCOME_WAS_CARDS_DATA_SHOWN = 4; // a UKM CS v2 label.
+                                              // Features:
+        int DURATION_AFTER_SCROLL_MS = 5;
+        int SCREEN_TOP_DPS = 6;
+        int WAS_SCREEN_BOTTOM = 7;
         // User usage features:
-        PREVIOUS_WEEK_IMPRESSIONS_COUNT,
-        PREVIOUS_WEEK_CTR_PERCENT,
-        PREVIOUS_28DAY_IMPRESSIONS_COUNT,
-        PREVIOUS_28DAY_CTR_PERCENT,
+        int PREVIOUS_WEEK_IMPRESSIONS_COUNT = 8;
+        int PREVIOUS_WEEK_CTR_PERCENT = 9;
+        int PREVIOUS_28DAY_IMPRESSIONS_COUNT = 10;
+        int PREVIOUS_28DAY_CTR_PERCENT = 11;
         // UKM CS v2 features (see go/ukm-cs-2).
-        DID_OPT_IN,
-        IS_SHORT_WORD,
-        IS_LONG_WORD,
-        IS_WORD_EDGE,
-        IS_ENTITY,
-        TAP_DURATION_MS,
+        int DID_OPT_IN = 12;
+        int IS_SHORT_WORD = 13;
+        int IS_LONG_WORD = 14;
+        int IS_WORD_EDGE = 15;
+        int IS_ENTITY = 16;
+        int TAP_DURATION_MS = 17;
         // UKM CS v3 features (see go/ukm-cs-3).
-        FONT_SIZE,
-        IS_SECOND_TAP_OVERRIDE,
-        IS_HTTP,
-        IS_ENTITY_ELIGIBLE,
-        IS_LANGUAGE_MISMATCH,
-        PORTION_OF_ELEMENT,
+        int FONT_SIZE = 18;
+        int IS_SECOND_TAP_OVERRIDE = 19;
+        int IS_HTTP = 20;
+        int IS_ENTITY_ELIGIBLE = 21;
+        int IS_LANGUAGE_MISMATCH = 22;
+        int PORTION_OF_ELEMENT = 23;
         // UKM CS v4 features (see go/ukm-cs-4).
-        TAP_COUNT,
-        OPEN_COUNT,
-        QUICK_ANSWER_COUNT,
-        ENTITY_IMPRESSIONS_COUNT,
-        ENTITY_OPENS_COUNT,
-        QUICK_ACTION_IMPRESSIONS_COUNT,
-        QUICK_ACTIONS_TAKEN_COUNT,
-        QUICK_ACTIONS_IGNORED_COUNT,
+        int TAP_COUNT = 24;
+        int OPEN_COUNT = 25;
+        int QUICK_ANSWER_COUNT = 26;
+        int ENTITY_IMPRESSIONS_COUNT = 27;
+        int ENTITY_OPENS_COUNT = 28;
+        int QUICK_ACTION_IMPRESSIONS_COUNT = 29;
+        int QUICK_ACTIONS_TAKEN_COUNT = 30;
+        int QUICK_ACTIONS_IGNORED_COUNT = 31;
     }
 
     /**
@@ -69,7 +86,7 @@
      * @param feature The feature to log.
      * @param value The value to log, which is associated with the given key.
      */
-    void logFeature(Feature feature, Object value);
+    void logFeature(@Feature int feature, Object value);
 
     /**
      * Returns whether or not AssistRanker query is enabled.
@@ -81,7 +98,7 @@
      * @param feature The feature to log.
      * @param value The outcome label value.
      */
-    void logOutcome(Feature feature, Object value);
+    void logOutcome(@Feature int feature, Object value);
 
     /**
      * Tries to run the machine intelligence model for tap suppression and returns an int that
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
index ab792cc1..200434b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
@@ -21,13 +21,17 @@
     private static final String TAG = "ContextualSearch";
 
     // Names for all our features and labels.
-    private static final Map<Feature, String> ALL_NAMES;
+    // Integer values should contain @Feature values only.
+    private static final Map<Integer, String> ALL_NAMES;
     @VisibleForTesting
-    static final Map<Feature, String> OUTCOMES;
+    // Integer values should contain @Feature values only.
+    static final Map<Integer, String> OUTCOMES;
     @VisibleForTesting
-    static final Map<Feature, String> FEATURES;
+    // Integer values should contain @Feature values only.
+    static final Map<Integer, String> FEATURES;
     static {
-        Map<Feature, String> outcomes = new HashMap<Feature, String>();
+        // Integer values should contain @Feature values only.
+        Map<Integer, String> outcomes = new HashMap<Integer, String>();
         outcomes.put(Feature.OUTCOME_WAS_PANEL_OPENED, "OutcomeWasPanelOpened");
         outcomes.put(Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED, "OutcomeWasQuickActionClicked");
         outcomes.put(Feature.OUTCOME_WAS_QUICK_ANSWER_SEEN, "OutcomeWasQuickAnswerSeen");
@@ -37,7 +41,8 @@
 
         // NOTE: this list needs to be kept in sync with the white list in
         // predictor_config_definitions.cc and with ukm.xml!
-        Map<Feature, String> features = new HashMap<Feature, String>();
+        // Integer values should contain @Feature values only.
+        Map<Integer, String> features = new HashMap<Integer, String>();
         features.put(Feature.DURATION_AFTER_SCROLL_MS, "DurationAfterScrollMs");
         features.put(Feature.SCREEN_TOP_DPS, "ScreenTopDps");
         features.put(Feature.WAS_SCREEN_BOTTOM, "WasScreenBottom");
@@ -70,7 +75,8 @@
         features.put(Feature.QUICK_ACTIONS_IGNORED_COUNT, "QuickActionsIgnored");
         FEATURES = Collections.unmodifiableMap(features);
 
-        Map<Feature, String> allNames = new HashMap<Feature, String>();
+        // Integer values should contain @Feature values only.
+        Map<Integer, String> allNames = new HashMap<Integer, String>();
         allNames.putAll(outcomes);
         allNames.putAll(features);
         ALL_NAMES = Collections.unmodifiableMap(allNames);
@@ -94,11 +100,13 @@
             AssistRankerPrediction.UNDETERMINED;
 
     // Map that accumulates all of the Features to log for a specific user-interaction.
-    private Map<Feature, Object> mFeaturesToLog;
+    // Integer values should contain @Feature values only.
+    private Map<Integer, Object> mFeaturesToLog;
 
     // A for-testing copy of all the features to log setup so that it will survive a {@link #reset}.
-    private Map<Feature, Object> mFeaturesLoggedForTesting;
-    private Map<Feature, Object> mOutcomesLoggedForTesting;
+    // Integer values should contain @Feature values only.
+    private Map<Integer, Object> mFeaturesLoggedForTesting;
+    private Map<Integer, Object> mOutcomesLoggedForTesting;
 
     /**
      * Constructs a Ranker Logger and associated native implementation to write Contextual Search
@@ -139,7 +147,7 @@
     }
 
     @Override
-    public void logFeature(Feature feature, Object value) {
+    public void logFeature(@Feature int feature, Object value) {
         assert mIsLoggingReadyForPage : "mIsLoggingReadyForPage false.";
         assert !mHasInferenceOccurred;
         if (!isEnabled()) return;
@@ -148,7 +156,7 @@
     }
 
     @Override
-    public void logOutcome(Feature feature, Object value) {
+    public void logOutcome(@Feature int feature, Object value) {
         assert mIsLoggingReadyForPage;
         assert mHasInferenceOccurred;
         if (!isEnabled()) return;
@@ -163,11 +171,11 @@
         mHasInferenceOccurred = true;
         if (isEnabled() && mBasePageWebContents != null && mFeaturesToLog != null
                 && !mFeaturesToLog.isEmpty()) {
-            for (Map.Entry<Feature, Object> entry : mFeaturesToLog.entrySet()) {
+            for (Map.Entry<Integer, Object> entry : mFeaturesToLog.entrySet()) {
                 logObject(entry.getKey(), entry.getValue());
             }
             mFeaturesLoggedForTesting = mFeaturesToLog;
-            mFeaturesToLog = new HashMap<Feature, Object>();
+            mFeaturesToLog = new HashMap<Integer, Object>();
             mAssistRankerPrediction = nativeRunInference(mNativePointer);
             ContextualSearchUma.logRecordedFeaturesToRanker();
         }
@@ -197,7 +205,7 @@
                 assert mHasInferenceOccurred;
                 // Only the outcomes will be present, since we logged inference features at
                 // inference time.
-                for (Map.Entry<Feature, Object> entry : mFeaturesToLog.entrySet()) {
+                for (Map.Entry<Integer, Object> entry : mFeaturesToLog.entrySet()) {
                     logObject(entry.getKey(), entry.getValue());
                 }
                 mOutcomesLoggedForTesting = mFeaturesToLog;
@@ -214,8 +222,8 @@
      * @param feature The feature to log.
      * @param value The value to log.
      */
-    private void logInternal(Feature feature, Object value) {
-        if (mFeaturesToLog == null) mFeaturesToLog = new HashMap<Feature, Object>();
+    private void logInternal(@Feature int feature, Object value) {
+        if (mFeaturesToLog == null) mFeaturesToLog = new HashMap<Integer, Object>();
         mFeaturesToLog.put(feature, value);
     }
 
@@ -230,7 +238,7 @@
      * @param feature The feature to log.
      * @param value An {@link Object} value to log (must be convertible to a {@code long}).
      */
-    private void logObject(Feature feature, Object value) {
+    private void logObject(@Feature int feature, Object value) {
         if (value instanceof Boolean) {
             logToNative(feature, ((boolean) value ? 1 : 0));
         } else if (value instanceof Integer) {
@@ -240,7 +248,8 @@
         } else if (value instanceof Character) {
             logToNative(feature, Character.getNumericValue((char) value));
         } else {
-            assert false : "Could not log feature to Ranker: " + feature.toString() + " of class "
+            assert false : "Could not log feature to Ranker: " + String.valueOf(feature)
+                           + " of class "
                            + value.getClass();
         }
     }
@@ -250,7 +259,7 @@
      * @param feature The feature to log.
      * @param value The value to log.
      */
-    private void logToNative(Feature feature, long value) {
+    private void logToNative(@Feature int feature, long value) {
         String featureName = getFeatureName(feature);
         assert featureName != null : "No Name for feature " + feature;
         nativeLogLong(mNativePointer, featureName, value);
@@ -259,7 +268,7 @@
     /**
      * @return The name of the given feature.
      */
-    private String getFeatureName(Feature feature) {
+    private String getFeatureName(@Feature int feature) {
         return ALL_NAMES.get(feature);
     }
 
@@ -270,7 +279,7 @@
      */
     @VisibleForTesting
     @Nullable
-    Map<Feature, Object> getFeaturesLogged() {
+    Map<Integer, Object> getFeaturesLogged() {
         return mFeaturesLoggedForTesting;
     }
 
@@ -281,7 +290,7 @@
      */
     @VisibleForTesting
     @Nullable
-    Map<Feature, Object> getOutcomesLogged() {
+    Map<Integer, Object> getOutcomesLogged() {
         return mOutcomesLoggedForTesting;
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index b1ae5b0..34762a1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -139,21 +139,23 @@
     private static final int ACTIVITY_STARTUP_DELAY_MS = 1000;
 
     // Ranker data that's expected to be logged.
-    private static final Set<ContextualSearchRankerLogger.Feature> EXPECTED_RANKER_OUTCOMES;
+    // Integer values should contain @Feature values only.
+    private static final Set<Integer> EXPECTED_RANKER_OUTCOMES;
     static {
-        Set<ContextualSearchRankerLogger.Feature> expectedOutcomes =
-                new HashSet<ContextualSearchRankerLogger.Feature>(
-                        ContextualSearchRankerLoggerImpl.OUTCOMES.keySet());
+        // Integer values should contain @Feature values only.
+        Set<Integer> expectedOutcomes =
+                new HashSet<Integer>(ContextualSearchRankerLoggerImpl.OUTCOMES.keySet());
         // We don't log whether the quick action was clicked unless we actually have a quick action.
         expectedOutcomes.remove(
                 ContextualSearchRankerLogger.Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED);
         EXPECTED_RANKER_OUTCOMES = Collections.unmodifiableSet(expectedOutcomes);
     }
-    private static final Set<ContextualSearchRankerLogger.Feature> EXPECTED_RANKER_FEATURES;
+    // Integer values should contain @Feature values only.
+    private static final Set<Integer> EXPECTED_RANKER_FEATURES;
     static {
-        Set<ContextualSearchRankerLogger.Feature> expectedFeatures =
-                new HashSet<ContextualSearchRankerLogger.Feature>(
-                        ContextualSearchRankerLoggerImpl.FEATURES.keySet());
+        // Integer values should contain @Feature values only.
+        Set<Integer> expectedFeatures =
+                new HashSet<Integer>(ContextualSearchRankerLoggerImpl.FEATURES.keySet());
         // We don't log previous user impressions and CTR if not available for the current user.
         expectedFeatures.remove(ContextualSearchRankerLogger.Feature.PREVIOUS_WEEK_CTR_PERCENT);
         expectedFeatures.remove(
@@ -1109,20 +1111,20 @@
     }
 
     /** @return The value of the given logged feature, or {@code null} if not logged. */
-    private Object loggedToRanker(ContextualSearchRankerLogger.Feature feature) {
+    private Object loggedToRanker(@ContextualSearchRankerLogger.Feature int feature) {
         return getRankerLogger().getFeaturesLogged().get(feature);
     }
 
     /** Asserts that all the expected features have been logged to Ranker. **/
     private void assertLoggedAllExpectedFeaturesToRanker() {
-        for (ContextualSearchRankerLogger.Feature feature : EXPECTED_RANKER_FEATURES) {
+        for (@ContextualSearchRankerLogger.Feature Integer feature : EXPECTED_RANKER_FEATURES) {
             Assert.assertNotNull(loggedToRanker(feature));
         }
     }
 
     /** Asserts that all the expected outcomes have been logged to Ranker. **/
     private void assertLoggedAllExpectedOutcomesToRanker() {
-        for (ContextualSearchRankerLogger.Feature feature : EXPECTED_RANKER_OUTCOMES) {
+        for (@ContextualSearchRankerLogger.Feature Integer feature : EXPECTED_RANKER_OUTCOMES) {
             Assert.assertNotNull("Expected this outcome to be logged: " + feature,
                     getRankerLogger().getOutcomesLogged().get(feature));
         }